The following works very nicely to determine the length of various audio/video files:
mplayer -identify file.ogg 2>/dev/null | grep ID_LENGTH
However, I want to kill mplayer's output so I can determine the length of many files more efficiently. How do I do that?
The MPlayer source ships with a sample script called midentify, which looks like this:
#!/bin/sh
#
# This is a wrapper around the -identify functionality.
# It is supposed to escape the output properly, so it can be easily
# used in shellscripts by 'eval'ing the output of this script.
#
# Written by Tobias Diedrich <ranma+mplayer#tdiedrich.de>
# Licensed under GNU GPL.
if [ -z "$1" ]; then
echo "Usage: midentify.sh <file> [<file> ...]"
exit 1
fi
mplayer -vo null -ao null -frames 0 -identify "$#" 2>/dev/null |
sed -ne '/^ID_/ {
s/[]()|&;<>`'"'"'\\!$" []/\\&/g;p
}'
The -frames 0 makes mplayer exit immediately, and the -vo null -ao null prevent it from trying to open any video or audio devices. These options are all documented in man mplayer.
FFMPEG can give you the same information in a different format (and doesn't attempt playing the file):
ffmpeg -i <myfile>
There's another FF-way in addition to #codelogic's method, which doesn't exit with an error:
ffprobe <file>
and look for the duration entry.
Or grep for it directly in the error stream:
ffprobe <file> 2> >(grep Duration)
looks like there are a few other libs available, see time length of an mp3 file
Download your .mp3 file, play it with your Player (ex. Windows Media Player) and the player will show the total time at the end of play.
Related
I have a library of video files. They get moved around, zipped, unzipped and stuff.
It happened, that some of the files get e.g., transferred/ extracted only partially. This problem usually shows up only when actually watching that video (i.e., the video stops prematurely, which is then really annoying).
Is there a way to batch-verify the integrity of a video library?
I came up with the following, inspired by this question:
find . -regex ".*\.\(avi\|mkv\)" -exec ffmpeg -v error -i {} -f null - \;
The problem here is, that ffmpeg does not include the file name when printing the error messages which means I do not know which file in the batch is erroneous.
To make a long story short:
Is there a way to include the file name in the ffmpeg error messages?
Simply capture the output of ffmpeg and print it out with a proper header if not empty:
find . -regex ".*\.\(avi\|mkv\)" | while read f; do
ffmpeg_out=$(ffmpeg -hide_banner -nostdin -v error -i "$f" -f null - 2>&1)
[[ $ffmpeg_out ]] && echo -e "==> ERROR in $f\n${ffmpeg_out}"
done
I've added a couple of ffmpeg options to ensure proper operations:
-hide_banner turns off the normal FFmpeg preamble, which is just unnecessary noise
-nostdin tells FFmpeg to ignore any (accidental) keyboard interaction
I have a script for playing movies through mplayer, for which I am attempting to include automatic conversion of 3D to 2D. Here is the relevant cmd line as it appears in the script
mplayer -fs "${g[$i]}" -ss $f "${d%.*}".* -hardframedrop -nocorrect-pts -identify &>> log.txt
This does not render to 2D even though "${g[$i]}" has the needed option
echo "${g[$i]}"
-vo gl:stereo=3
But if I modify the cmd line to show the option directly, the movie is shown in 2D
mplayer -fs -vo gl:stereo=3 -ss $f "${d%.*}".* -hardframedrop -nocorrect-pts -identify &>> log.txt
The problem seems to be that mplayer interprets the option as a filename when delivered via ${g[$i]}, thus from log.txt
Playing -vo gl:stereo=3 .
File not found: ' -vo gl:stereo=3 '
Failed to open -vo gl:stereo=3 .
How can I prevent this?
You are passing " -vo gl:stereo=3 " which is indeed invalid. You should be passing "-vo" followed by "gl:stereo=3". In other words, it should be passed as two separate arguments without the leading and trailing spaces.
The easiest workaround to make this happen is to skip the quotes around ${g[$i]}:
mplayer -fs ${g[$i]} -ss $f "${d%.*}".* -hardframedrop -nocorrect-pts -identify &>> log.txt
The more robust workaround would be to store the arguments separately in an array, e.g. opts=("-vo" "gl:stereo=3") and using "${opt[#]}", but Bash does not support multidimensional arrays so this may require some deeper changes to your script.
I have stuttering, seeking, and general playback issues when playing large mkv files through my Plex Media Server setup. I have been looking around for a way to automate scheduled tasks to move everything to mp4. The objective is:
Copy mkv files into mp4 preserving subtitles of every kind. Put the new file in the same subdir, and delete previous mkv version if conversion went successful.
When I tried to run ffmpeg on a loop, I run into the problem described here:
https://unix.stackexchange.com/questions/36310/strange-errors-when-using-ffmpeg-in-a-loop
This is my first adventure on shell scripting and I am pretty much stumbling around and trying to understand the syntax and philosophy of it. What I understand is that they use a file descriptor to redirect ffmpeg output to /dev/null.
The problem with that solution is that I would need to check ffmpeg output for errors to decide whether to delete the previous file or not. Furthermore, there is a common error when converting from picture based subtitles streams, which I circumvent by using a script I found (http://www.computernerdfromhell.com/blog/automatically-extract-subtitles-from-mkv/) to work after some modifications to my needs.
After much frustration I ended modifying the script so much that it does not serve to its purpose. It does not check for errors. Anyways, I will post it here. Mind you that this is my first shell script ever, and almost everything is confusing about it. The problem with this, is that I had to ditch my error checking and I am eliminating files that errored when converting. Losing the original without a valid copy.
#!/bin/bash
FOLDERS=( "/mnt/stg4usb/media0/test/matroska1" "/mnt/stg4usb/media0/test/season1" "/mnt/stg4usb/media0/test/secondtest")
FLAGS="-y -metadata title="" -c:v copy -map 0 -c:a libfdk_aac -ac 2 -movflags +faststart"
COUNTER=0
LOGFILE=batch-$(date +"%Y%m%d-%H%M%S").log
for FOLDER in "${FOLDERS[#]}"
do
echo "---===> STARTING folder: '$FOLDER'"
find $FOLDER -name "*.mkv" | while read line; do
OUTPUT=""
DATE=$(date +"%Y-%m-%d")
TIME=$(date +"%H:%M:%S")
COUNTER=$((COUNTER+1))
FILE=$(basename "$line")
DIR=$(dirname "${line}")
echo $'\n'$'\n'"[$COUNTER][$DATE][$TIME][FILE:'${line%.mkv}.mp4']"$'\n'
echo "#### Transcoding ####"'\n'
ffmpeg -i $line $FLAGS -sn "${line%.mkv}.mp4" < /dev/null
echo "#### Extracting subtitles ###"'\n''\n'
mkvmerge -i "$line" | grep 'subtitles' | while read subline
do
# Grep the number of the subtitle track
tracknumber=`echo $subline | egrep -o "[0-9]{1,2}" | head -1`
# Get base name for subtitle
subtitlename=${line%.*}
# Extract the track to a .tmp file
mkvextract tracks "$line" $tracknumber:"$subtitlename.$tracknumber.srt" < /dev/null
chmod g+rw "$subtitlename.$tracknumber"* < /dev/null
done
rm -frv "$line" < /dev/null
echo "Finished: $(date +"%Y%m%d-%H%M%S")"
done
echo '\n'"<===--- DONE with folder: '$FOLDER'"$'\n'$'\n' >> $LOGFILE
done
exit 0
So, basically, the idea is: run ffmpeg on a loop for all mkv under a directory and subdirectories (I was using find). Check it for all possible errors. If errors, try again without subtitles and extract the subtitles using mkvextract, else everything went ok, and delete the previous file.
I record calls on my phone (as audio only). Records are storing to 3gp file. When I play a single file it can be mentioned that it is plaing few times faster then normal speed of conversation. I tested, that played on ffplay using command
ffplay.exe %1 -ar 8000
sounds as a real speed.
I would like to convert 3gp file to mp3 file using ffmpeg file (easy batch script running on Windows). I started with command line
ffmpeg.exe -i %1 -ar 8000 -f mp3 %1.mp3
but bitaire of mp3 don't sounds well (too fast). I performed experiments with -b, -b:a and others parameters but bitrate does not change.
Which parameters have I use to change 3gp to mp3 file with proper bitrate value?
-ar 8000
forces ffmpeg to interpret your input file as being sampled at 8kHz. Is tis the case or are you trying to downsample it to 8 kHz?
If so try the following instead:
-af "aresample=8000"
Please mark this as good answer if you're happy with it ;)
Well the commands you have fired are correct. I myself tried them myself and are working correctly. So firstly just try again with 1 file rather than batch files.
Here is command 1
ffmpeg -i input.3gp -ar 32k -f mp3 out.mp3
Result
Bitrate of out.mp3 is 47kbps
Command 2
ffmpeg -i inout.3gp -b:a 32k out.mp3
Bitrate of out.mp3 32kbps
So you can go with any of the above.
Cheers :)
Below you will find a script that I altered to include .3gp files.
It also does .mp4 .mkv and .webm files.
The clarity of the voice was good at 30k and that is what is here, but if you want larger files with better sound clarity then try 200k.
Call your script file vidconvmp3.sh for example and save it.
Then Modch +x vidconvmp3.sh to authorize execute.
then ./vidconvmp3.sh and your batch will run through and toss your mp3 files into a folder called Desktop-mp3 if you run terminal on the desktop... tah dah.
#!/usr/bin/env bash
# My bash Script to convert mp4 to mp3
# By NerdJK23
# web: www.computingforgeeks.com
# email: kiplangatmtai#gmail.com
# Requires
# ffmpeg installed
# lame installed
# Check https://computingforgeeks.com/how-to-convert-mp4-to-mp3-on-linux/
echo -ne """
1: Current directory
2: Provide directory
"""
echo ""
echo -n "Selection : "
read selection
case $selection in
1)
echo "Okay.."
echo ""
echo "Current dir is `pwd` "
;;
2)
echo ""
echo -n "Give diretory name: "
read dir_name
# Check if given directory is valid
if [ -d $dir_name ]; then
cd "${$dir_name}"
echo "Current directory is `pwd` "
echo
else
echo "Invalid directory, exiting.."
echo ""
exit 10
fi
echo
;;
*)
echo
echo "Wrong selection"
exit 11
;;
esac
echo ""
# Create dir to store mp3 files if it doesn't exist
# First get the current directory name
current_dir=`pwd`
base_name=` basename "$current_dir"`
if [[ ! -d "$base_name"-mp3 ]]; then
echo "$base_name" | xargs -d "\n" -I {} mkdir {}-mp3
echo ""
fi
echo ""
# Bigin to covert videos to mp3 audio files
# -d "\n" > Change delimiter from any whitespace to end of line character
find . -name "*.mp4" -o -name "*.mkv" -o -name "*.webm" -o -name "*.3gp" | xargs -d "\n" -I {} ffmpeg -i {} -b:a 30K -vn "$base_name"-mp3/{}.mp3
# remove video extensions
cd "${base_name}"-mp3
for file_name in *; do
mv "$file_name" "`echo $file_name | sed "s/.mp4//g;s/.mkv//g;s/.3pg//g;s/.webm//g"`";
done
# Move audio directory to ~/Music
if [[ ! -d ~/Music ]]; then
mkdir ~/Music
fi
cd ..
mv "$base_name"-mp3 ~/Music/
# Check if conversion successfull
echo ""
if [[ $? -eq "0" ]];then
echo " All files converted successfully"
else
echo "Conversation failed"
exit 1
fi
I have a script, wacaw (http://webcam-tools.sourceforge.net/) that outputs video from my webcam to a file. I am trying to basically stream that to some sort of display i.e vlc, quicktime, etc to get a "mirror" type effect.
Aside from altering the source code for wacaw, is there any way to force a script's file output to stdout so I can pipe it to something like vlc? Is it even possible to stream video like that?
Thanks for your help!
UPDATE: just to clarify:
running the wacaw script is formatted as follows:
./wacaw --video --duration 5 --VGA myFile
and it outputs a file myFile.avi. If I try to do a named pipe:
mkfifo pipe
./wacaw --video --duration 5 --VGA pipe
it outputs a file pipe.avi
You can use named pipes. You use mkfifo to create the pipe, hand that filename to the writing process and then read from that file with the other process. I have no idea if video in particular would work that way, but many other things do.
At least in bash you can do like this:
Original command:
write-to-file-command -f my-file -c
Updated command:
write-to-file-command -f >(pipe-to-command) -c
write-to-file-command will think >(pipe-to-command) is a write-only file and pipe-command will receive the file data on its stdin.
(If you just want the output to stdout you could do
write-to-file-command >(cat)
)
You may also try using tail -F myFile.avi:
# save stdout to file stdout.avi
man tail | less -p '-F option'
(rm -f myFile.avi stdout.avi; touch myFile.avi; exec tail -F myFile.avi > stdout.avi ) &
rm -f myFile.avi; wacaw --video --duration 1 --VGA myFile
md5 -q myFile.avi stdout.avi
stat -f "bytes: %z" myFile.avi stdout.avi
# pipe stdout to mplayer (didn't work for me though)
# Terminal window 1
# [mov,mp4,m4a,3gp,3g2,mj2 # ...]moov atom not found
#rm -f myFile.avi; touch myFile.avi; tail -F myFile.avi | mplayer -cache 8192 -
# Terminal window 2
#rm -f myFile.avi; wacaw --video --duration 1 --VGA myFile