AVCLevels PHP Library: read and set profile level from H264 AVC-encoded video (MP4, MKV, ...)

I was stunned this morning to find out about AVC levels (H264/MPEG-4 AVC). Basically, when you encode a video using the widespread H264 codec for the video stream, the encoder sets a flag called the "level". Wikipedia says that the level is a specified set of constraints indicating a degree of required decoder performance for a profile. The problem I have with this is: some software and equipment restrict their own capabilities based on that level! They check the level, and if the value doesn't please them, they refuse to even attempt to play the video.

What is this all about?

See, I had two MP4 videos. One would play fine on my iPhone, the other refused to play (iTunes refused to copy it into my library, claiming that my iPhone did not support the video format). I inspected both files with MediaInfo, they were both encoded properly with AVC (H264) and AAC audio, similar bitrates, same resolution. After hours of testing and research I stumbled upon this blog post. Turns out that the only thing preventing my iPhone to play the second video was the "level" flag that was set to 51. The first file's flag was 31. Further researches indicated that my iPhone 4S supports H.264 video up to 720p, 30 frames per second, Main Profile level 3.1 with AAC-LC audio up to 160 Kbps per channel, 48kHz, stereo audio in .m4v, .mp4, and .mov file formats.


So I manually edited my video using some hex editor, set the "level" byte from 51 to 31, and voila: the video played fine on my iPhone. Wow. 


A good solution for Windows

I discovered a nify little Windows application that allows me to set the level: H264 Level Editor. Easy to use, the program does the job, and more. Make sure to back up your files before setting the level.

Problem is, I have hundreds of videos that I need to edit out. And since the videos are on my web servers, I need a solution that runs under Linux. I didn't find anything like that. I expected MP4Box to be able to do so, or ffmpeg, but since this is a downright "hack" (a workaround) these noble apps won't help - at they will only re-encode the videos which takes hours. Come on! All I need to do is change a single byte...

My AVCLevels PHP library

Since I didn't find any program that would do it, I programmed a simple PHP library that will do the job. Again, all it does is detect the flag from your video file, and read or set the "level" byte.

Download the AVCLevels library from Filetrip (Free. This is the source code, feel free to use it any way you want). Use it at your own risks! This isn't guaranteed to work on all videos, though I have tested it with over 100 videos and it always worked fine. Maybe I was lucky, anyhow DO back up your files before using this.


Using it couldn't be simpler. There are no particular requirements, just PHP 5 and higher.

// Include library
require_once("AVCLevels.php");
 
// Set file path
$file = "./test.mp4";
 
// Retrieve & display level
$level = AVCL_RetrieveLevel($file);
echo "Level: ".$level;
 
// set level to 31 (AVC Profile Level 3.1)
AVCL_SetLevel($file, 31);

That's all there is to it really. If you want an iOS compatible video, set the level to 31, since that's what it takes. Anything higher won't work. Lower profile levels should work, but 31 seems to do the trick. I don't know what level is recommended for the PS3 but I know Sony's been using the same practices (31 should work for the PS3 I suppose).

Comments

Popular posts from this blog

Nginx error 413: Request entity too large Quick Fix

Dealing with Nginx 400 Bad Request HTTP errors

Affiliate module for Interspire Shopping Cart