我得到了一个没有元数据的视频文件。
例如如果我这样做:
ffmpeg -i test.m2v
我得到这些值:
Duration: N/A, bitrate: N/A
还有办法获取视频/.m2v 文件的时长吗?
编辑:
完整的控制台输出:
ffmpeg version 2.8.4 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 5.2.0 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libdcadec --enable-libfr
eetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsoxr --enable-libspeex --enab
le-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-aacenc --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-lzma --enable-decklink
--enable-zlib
libavutil 54. 31.100 / 54. 31.100
libavcodec 56. 60.100 / 56. 60.100
libavformat 56. 40.101 / 56. 40.101
libavdevice 56. 4.100 / 56. 4.100
libavfilter 5. 40.101 / 5. 40.101
libswscale 3. 1.101 / 3. 1.101
libswresample 1. 2.101 / 1. 2.101
libpostproc 53. 3.100 / 53. 3.100
Input #0, mpegvideo, from '.\Test.m2v':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: mpeg2video (Main), yuv420p(tv), 720x576 [SAR 64:45 DAR 16:9], max. 7000 kb/s, 25 fps, 25 tbr, 1200k tbn, 50 tbc
答案1
对于原始比特流,可以使用 ffprobe。
ffprobe -show_entries stream=r_frame_rate,nb_read_frames -select_streams v -count_frames -of compact=p=0:nk=1 -v 0 in.m2v
这产生了
30/1|120
其中第一个条目是帧速率,是一个有理数,第二个条目是计数的帧数。持续时间是120 / (30/1) = 4.00s
答案2
如果你不有元数据,那么你需要建造通过读取和解析整个文件来获取这些元数据(这就是它的ffprobe
作用),并根据文件的实际内容得到正确的答案。
没有更准确答案。然而,快点答案。正如所注意到的,“如果你在 VLC 中打开此类视频,它会立即显示视频的时长“。在很多地方,VLC(以及其他工具)似乎估计,而不是读取并解析整个文件(您可能无法或不愿意这样做):
/* try to calculate movie time */
if( p_sys->p_fp->i_data_packets_count > 0 )
{
uint64_t i_count;
uint64_t i_size = stream_Size( p_demux->s );
if( p_sys->i_data_end > 0 && i_size > p_sys->i_data_end )
{
i_size = p_sys->i_data_end;
}
/* real number of packets */
i_count = ( i_size - p_sys->i_data_begin ) /
p_sys->p_fp->i_min_data_packet_size;
/* calculate the time duration in micro-s */
p_sys->i_length = VLC_TICK_FROM_MSFTIME(p_sys->p_fp->i_play_duration) *
(vlc_tick_t)i_count /
(vlc_tick_t)p_sys->p_fp->i_data_packets_count;
if( p_sys->i_length <= p_sys->p_fp->i_preroll )
p_sys->i_length = 0;
else
{
p_sys->i_length -= p_sys->p_fp->i_preroll;
p_sys->i_bitrate = 8 * i_size * CLOCK_FREQ / p_sys->i_length;
}
}
在这种情况下,
流 #0:0:视频:mpeg2video(主)、yuv420p(电视)、720x576 [SAR 64:45 DAR 16:9],最大 7000 kb/s、25 fps、25 tbr、1200k tbn、50 tbc
已知流速度为 7000 kb/s,文件大小为,将文件大小除以 7000 kbit 并乘以 8 即可立即得出可能的持续时间(以秒为单位)。
例如我这里有一个文件(实际文件时长 02:32:19,文件大小 733,802,496)
Stream #0:0: Video: ... 562 kb/s, 25 fps ...
Stream #0:1: Audio: ... 54 kb/s
562+54 为 616kbits,即 77kbytes。733,802,496 除以 (77*1024) 为 9306,9306 秒为 2 小时 35 分 6 秒,不确切地正确,但非常接近。
根据实际使用的编解码器、音频/视频交错方法、相同的填充以及是否是 CBR 或 VBR,实际准确度可能会有所不同。
缺乏可靠的元数据,如果您重视速度而不是准确性,那么我担心估计是可行的方法。
在某些情况下,您可能能够同时执行这两项操作(根据文件头和大小提供即时估计,然后在 UI/程序没有更好的事情可做时开始读取并优化答案。然后,您甚至可以将新计算的元数据保存在某个地方,以便以后需要时检索它们 - 在数据库、缩略图文件、备用数据流中,或者甚至在可行的情况下提供更新/“修复”视频文件)。
命令行
从命令行使用 ffmpeg:
#!/bin/bash
if [ ! -r "$1" ]; then
echo "File '$1' not found"
exit 1
fi
FILESIZE=$( stat -c "%s" "$1" )
STREAMS=$(
ffmpeg -i "$1" 2>&1 \
| grep 'Stream #' \
| tr "," "\n" \
| grep "kb/s" \
| tr " " "\n" \
| grep "^[1-9][0-9]*$" )
RATE=0
for r in $STREAMS; do
RATE=$[ $RATE + $r ]
done
# I don't think that bash has decimal support, so we use bc
SECONDS=$( echo "$FILESIZE / $RATE" | bc )
# To get seconds in HH:MM:SS format we use 'date'
DURATION=$( TZ=UTC date +"%H:%M:%S" -d @$SECONDS )
即使视频文件中有多个音频流,这也应该是准确的。我不确定如果有字幕之类的数据流会发生什么。这种流的大小应该不大,所以它不应该造成干扰,但话又说回来,它可能造成干扰。