How to play avi files on browser

11.9k views Asked by At

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:

2

There are 2 answers

1
mvalencaa On BEST ANSWER

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!

5
VC.One On

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...

  • Instead of 03 there is only 01 display time entry (yet other entry bytes were not deleted).
  • Instead of 00 00 00 80 = 128, there is actually 00 00 00 8D = 141 frames
  • I still kept the original display time for these (now 141 frames) as 00 00 02 00 = 512 ms
  • I changed the remaining two entries bytes all to zero's (since we don't need use them).

That'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)

    • find start pos of "mvhd" then skip 20 bytes to find duration.
    • find start pos of "tkhd" then skip 24 bytes to find duration.
    • find start pos of "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.