My team needs to develop a system that plays avi files on the web. These files are recorded by a hardware whose firmware we don't have access to. We are trying to negotiate it with the manufacturer to change the file format to mp4, but until now we have nothing.
Because of this, we are trying another manners to make it work. Our first attempt was to use FFMPEG to convert the files to mp4 (or webm or ogg), but this process takes too long because we have to do it everyday with a really huge amount of videos.
We also tried to use FFMPEG's copy command (which is much faster), but the video always crashes at some point (mainly when we need to navigate in its timeline) and we don't know why.
So now we are thinking to customize videojs flash player to reproduce the AVI files, but because we don't have too much experience with video programming and flash, we don't know if this is possible. Is it possible to write a decoder in action script to read avi files?
I saw that Youtube and Facebook can play AVI files... How do they do this? I have already looked a lot about it, but had no success.
EDIT 1
avi video file before copy command:
$ ffmpeg -i video.avi
ffmpeg version N-82324-g872b358 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 5.4.0 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-dx va2 --enable-libmfx --enable-nvenc --enable-avisynth --enable-bzlib --enable-lib ebur128 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --ena ble-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfree type --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enab le-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-lib openh264 --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschr oedinger --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libtheor a --enable-libtwolame --enable-libvidstab --enable-libvo-amrwbenc --enable-libvo rbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --ena ble-libx265 --enable-libxavs --enable-libxvid --enable-libzimg --enable-lzma --e nable-decklink --enable-zlib
libavutil 55. 36.100 / 55. 36.100
libavcodec 57. 66.101 / 57. 66.101
libavformat 57. 57.100 / 57. 57.100
libavdevice 57. 2.100 / 57. 2.100
libavfilter 6. 66.100 / 6. 66.100
libswscale 4. 3.100 / 4. 3.100
libswresample 2. 4.100 / 2. 4.100
libpostproc 54. 2.100 / 54. 2.100
Input #0, avi, from 'video.avi':
Metadata:
encoder :
Duration: 00:50:07.67, start: 0.000000, bitrate: 6 kb/s
Stream #0:0: Video: h264 (Constrained Baseline) (H264 / 0x34363248), yuv420p (progressive), 352x240, 3 fps, 3 tbr, 3 tbn, 6 tbc
At least one output file must be specified
copy command (with no audio stream because the videos don't have it):
$ ffmpeg -i video.avi -vcodec copy video.mp4
ffmpeg version N-82324-g872b358 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 5.4.0 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-dxva2 --enable-libmfx --enable-nvenc --enable-avisynth --enable-bzlib --enable-libebur128 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschroedinger --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-libzimg --enable-lzma --enable-decklink --enable-zlib
libavutil 55. 36.100 / 55. 36.100
libavcodec 57. 66.101 / 57. 66.101
libavformat 57. 57.100 / 57. 57.100
libavdevice 57. 2.100 / 57. 2.100
libavfilter 6. 66.100 / 6. 66.100
libswscale 4. 3.100 / 4. 3.100
libswresample 2. 4.100 / 2. 4.100
libpostproc 54. 2.100 / 54. 2.100
Input #0, avi, from 'video.avi':
Metadata:
encoder :
Duration: 00:50:07.67, start: 0.000000, bitrate: 6 kb/s
Stream #0:0: Video: h264 (Constrained Baseline) (H264 / 0x34363248), yuv420p(progressive), 352x240, 3 fps, 3 tbr, 3 tbn, 6 tbc
Output #0, mp4, to 'video.mp4':
Metadata:
encoder : Lavf57.57.100
Stream #0:0: Video: h264 (Constrained Baseline) ([33][0][0][0] / 0x0021), yuv420p(progressive), 352x240, q=2-31, 3 fps, 3 tbr, 12288 tbn, 3 tbc
Stream mapping:
Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help
[mp4 @ 0000000002513fc0] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
[NULL @ 0000000002524020] missing picture in access unit with size 16
Last message repeated 300 times
frame= 9324 fps=0.0 q=-1.0 Lsize= 1388kB time=01:38:27.66 bitrate= 1.9kbits/s speed=3.32e+004x
video:1354kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 2.493988%
mp4 video file after copy command:
$ ffmpeg -i video.mp4
ffmpeg version N-82324-g872b358 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 5.4.0 (GCC)
configuration: --enable-gpl --enable-version3 --disable-w32threads --enable-dx va2 --enable-libmfx --enable-nvenc --enable-avisynth --enable-bzlib --enable-lib ebur128 --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --ena ble-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfree type --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enab le-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-lib openh264 --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libschr oedinger --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libtheor a --enable-libtwolame --enable-libvidstab --enable-libvo-amrwbenc --enable-libvo rbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --ena ble-libx265 --enable-libxavs --enable-libxvid --enable-libzimg --enable-lzma --e nable-decklink --enable-zlib
libavutil 55. 36.100 / 55. 36.100
libavcodec 57. 66.101 / 57. 66.101
libavformat 57. 57.100 / 57. 57.100
libavdevice 57. 2.100 / 57. 2.100
libavfilter 6. 66.100 / 6. 66.100
libswscale 4. 3.100 / 4. 3.100
libswresample 2. 4.100 / 2. 4.100
libpostproc 54. 2.100 / 54. 2.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'video.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.57.100
Duration: 01:38:28.00, start: 0.000000, bitrate: 1 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yu v420p, 352x240, 1 kb/s, 1.58 fps, 3 tbr, 12288 tbn, 24576 tbc (default)
Metadata:
handler_name : VideoHandler
At least one output file must be specified
EDIT 2
I uploaded two little samples if somebody wants to test:
We also tried to use FFMPEG's
copy
command (which is much faster), but the video always crashes at some point (mainly when we need to navigate in its timeline) and we don't know why.
Here is a link to your re-fixed MP4. Seeking is now working.
I manually checked the bytes and, as I suspected, the timestamps were wrong.
FFprobe (using ffprobe video.avi
) says AVI duration is 5.88 seconds. MPC-HC also showed duration as 6 seconds (rounded). Now we later expect the MP4 to also have same 5.88 duration when using copy
command. But after copy
, the output MP4 has a new 10 second duration???
An MP4 puts duration in 3 places. Each software has own rules of which entry to check so best to edit all 3 places for everybody's sake. The metadata is divided into "atoms" so we edit 3 atoms.
These atoms called mvhd
, tkhd
, and elst
all had byte values 00 00 28 87
for duration, which equals 10375 ms (or 10.375 sec).
This is wrong because we want 5880 ms (or 5.88 secs). I manually changed them all from 00 00 28 87
to become 00 00 16 F8
which gives 5880 millisecs. Now MPC-HC thinks the mp4 is 5 seconds (phew!!) but Chrome keeps saying it's still 10 seconds (damn!!).
As a last resort I checked an atom called stts
. This had 3 items for timings of a total 136 frames (note FFprobe says AVI actually had 141 frames). Anyways the first item said the first 128 frames have a display time of 512 ms (which takes duration to 128 * 512 = 65536 ms
). This is wrong.
To cut a long story short. I edited the stts
to say...
03
there is only 01
display time entry (yet other entry bytes were not deleted). 00 00 00 80
= 128, there is actually 00 00 00 8D
= 141 frames00 00 02 00
= 512 msThat's how your MP4 become a working seekable file as tested in Chrome browser.
Conclusion :
To fix your own files I suspect editing just the first entry only of stts
section in MP4 metadata is enough. You're telling it "I have X-amount of frames, each frame displays for X interval, no extras!!". Then just edit the duration times in mvhd
, tkhd
, elst
. Here you must know how to work with bytes.
Write a desktop program that (if someone in your team can do it)...
Gets "duration" and "total frames" from AVI (I recommend ExifTool over FFprobe cos it shows both details correctly). Your program reads this info via std in/out.
Once known, search the atom names as UTF text to find their beginning positions within bytes.
When you have each start position... you will now edit the numbers as 4-byte Integers (written in Hex notation)
mvhd
" then skip 20 bytes to find duration. tkhd
" then skip 24 bytes to find duration.elst
" then skip 24 bytes to find duration.Finally find start pos of "stts
" then skip 8 bytes to find total entries (4 bytes). After that the next 4 bytes are "total frames" number (you fix with real number), Then next following 4 bytes are timing for frames but leave that un-changed...
Other Query : ...
Is it possible to write a decoder in action script to read avi files? I saw that Youtube and Facebook can play AVI files... How do they do this?
Yes!! Since your AVI has Mpeg (H.264) video, Flash has a decoder that accepts MPEG a/v bytes so if you extract frame bytes from AVI and feed them to decoder it will display.
This old demo app is one example where I feed Mpeg's AAC audio bytes to the decoder. Same principle for your AVI. I work with Flash but I'll tell you... Better to fix the files than have a situation where only one customized app in the world can play them. A fixed file works in Flash, HTML5 browser and everywhere else.
PS : Facebook and Youtube are re-encoding your file. It's not a direct file copy.
We finally solved this problem.
Although the @VC.One's solution seems to work, we didn't made it because we thought there were a better way to solve it without have to make bytes operations (once we don't have to much experience with it)...
After a lot of tries, we realized our AVI file was malformed, so we used a program named Mencoder, which has the function forceidx:
Force index rebuilding. Useful for files with broken index (A/V desync, etc). This will enable seeking in files where seeking was not possible. You can fix the index permanently with MEncoder (see the documentation). NOTE: This option only works if the underlying media supports seeking (i.e. not with stdin, pipe, etc).
After run this command, we then used FFMPEG's copy command to "convert" the (now well formed) AVI file to mp4. Voilá! The magic happened!
Thanks a lot guys!
Thanks, @VC.One by your time dispended here!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With