C Get MP4 Duration

GeekThis happily runs on Vultr. Get $300 of free hosting credits to try out their cloud compute, kubernetes engine, or managed databases. Try Vultr today to claim your free $300.

I was recently working on a program that needed to get the duration of MP4 files and I really didn’t want to call an external program like ffprobe and parse the output. The code as it’s currently standing is below, but maybe the information along side it may be more useful. Now, this code isn’t 100% accurate and I coded it only for speed, lower IO usage and the videos I had to parse all were encoded under the same conditions.

The MP4 Container

The MP4 file format is a container for video and audio, and can contain multiple of each. Each media part of the file starts with the header of moov which equates to 6d 6f 6f 76. Inside of the “movie (presentation) box” you can find information under the headers of mvhd, iods,rmdr, and about 20 others. The one we want to focus on is the mvhd which is the “movie (presentation) header box”. This box will contain values such as time unit per second and time length.

The duration is calculated by time length / time unit per second and will end up giving you the duration of the video in seconds. Below is part of the format of the “movie (presentation) header box”, and the full mp4 spec document I’ve been using is over at Xhelmboyx Mp4 File Spec, but a backup is here just in case.

8+ bytes movie (presentation) header box = long unsigned offset + long ASCII text string 'mvhd'
	-> 1 byte version = 8-bit unsigned value
		- if version is 1 then date and duration values are 8 bytes in length
	-> 3 bytes flags =  24-bit hex flags (current = 0)

	-> 4 bytes created mac UTC date
		= long unsigned value in seconds since beginning 1904 to 2040
	-> 4 bytes modified mac UTC date
		= long unsigned value in seconds since beginning 1904 to 2040
	OR
	-> 8 bytes created mac UTC date
		= 64-bit unsigned value in seconds since beginning 1904
	-> 8 bytes modified mac UTC date
		= 64-bit unsigned value in seconds since beginning 1904

	-> 4 bytes time scale = long unsigned time unit per second (default = 600)

	-> 4 bytes duration = long unsigned time length (in time units)
	OR
	-> 8 bytes duration = 64-bit unsigned time length (in time units)

The C Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>

char* videoinfo_mem(char* haystack, unsigned int sizehaystack, char* needle, unsigned int sizeneedle) {
	int i = 0;
	int end = sizehaystack - sizeneedle;
	for(i = 0; i < end; ++i) {
		if(memcmp(haystack+i,needle,sizeneedle) == 0) {
			return haystack+i;
		}
	}
	return NULL;
}

void* videoinfo_find(char* filename, void* find, int size, int resultSize) {
	FILE* fp = NULL;
	char* buffer = NULL;
	char* result = NULL;
	char* pos = NULL;
	unsigned int bufferSize = 2048;
	double filesize = 0;
	double split = 16;
	double splitsize = 0;
	double start = 0;
	double end = 0;
	double i = 0;
	unsigned int read = 0;

	if(resultSize > bufferSize) {
		resultSize = bufferSize;
	}

	buffer = malloc(bufferSize);
	if(buffer == NULL) {
		return NULL;
	}

	fp = fopen(filename,"rb");
	if(fp == NULL) {
		free(buffer);
		return NULL;
	}

	fseek(fp,0,SEEK_END);
	filesize = ftell(fp);
	rewind(fp);

	split = ceil(filesize / 100000);
	splitsize = ceil(filesize/split);

	for(i = split-1; i >= 0; --i) {
		start = (i*splitsize);
		end = start + splitsize;
		fseek(fp,start,SEEK_SET);

		while((read = fread(buffer,1,bufferSize,fp)) != 0) {
			if((pos = videoinfo_mem(buffer,bufferSize,find,size)) != NULL) {
				result = malloc(resultSize);
				memcpy(result,pos,resultSize);
				i = -1;
				break;
			}

			if(read != bufferSize || ftell(fp) >= end) {
				break; // go onto next split
			}
		}

	}

	fclose(fp);
	free(buffer);
	return result;
}

unsigned long videoinfo_flip(unsigned long val) {
	unsigned long new = 0;
	new += (val & 0x000000FF) << 24;
	new += (val & 0xFF000000) >> 24;
	new += (val & 0x0000FF00) << 8;
	new += (val & 0x00FF0000) >> 8;

   return new;
}

unsigned long videoinfo_duration(char* filename) {
	unsigned long duration = 0;
	char version = 0;
	void* data = NULL;
	char* pos = NULL;
	unsigned long timescale = 0;
	unsigned long timeunits = 0;
	int bytesize = 4;

	data = videoinfo_find(filename,"mvhd",4,64);
	if(data == NULL) {
		goto clean;
	}

	pos = (char*)data;
	pos += 4; // skip mvhd

	version = *pos++;
	pos+=3; //skip flags

	if(version == 1) {
		bytesize = 8;
	}else{
		bytesize = 4;
	}

	pos += bytesize; // skip created date
	pos += bytesize; // skip modified date

	memcpy(&amp;timescale,pos,4);
	memcpy(&timeunits,pos+4,bytesize);

	timescale = videoinfo_flip(timescale);
	timeunits = videoinfo_flip(timeunits);

	if(timescale > 0 && timeunits > 0) {
		duration = timeunits/timescale;
	}

	clean:
	free(data);
	return duration;
}

Related Posts

Windows API Single Key Hotkeys

Learn how to create a program that registers a single key as a hotkey using the Windows API.

PHP - Check if Production or Sandbox

Use PHP to check of your site is on your development server or uploaded to the production server.

Setup PHP Development Web Server Quickly inside of Windows

Quickly setup a PHP development enviornment by using the built in PHP web server with no additional software required.

PHP Dynamic Subdomains

How to setup dynamic subdomains with PHP to give users a custom domain for their profile page.