One of my customers emailed me with an interesting problem this morning.
“I need to be able to peek into a video file, specifically a .mp4 file, and determine if it’s PAL or NTSC.”
This seemed like a fun challenge, so I thought I’d write a helper class to solve it..

Ultimately, what determines if an image or video conforms to PAL or NTSC is a combination of the frame rate and the image resolution.

  • PAL has a frame rate of 25 per second, and a vertical resolution of 625 scan lines.
  • NTSC has a frame rate of 30 per second, and a vertical resolution of 512 scan lines.

(As an aside, NTSC is actually 29.9 frames per second due to a technical restriction in broadcast frequencies, but I digress…)
So what we need to do is get the resolution and frame rate information of a video track within an mp4 file.

Android offers a class named “MediaExtractor” to extract data from a media file within it’s API.
So we need to create an instance of this class and extract the data from it.

There’s a bug to watch for in the Android API, in my first attempt I created an instance of MediaExtractor like this…

var 
  Extractor: JMediaExtractor; 
begin 
  Extractor := TJMediaExtractor.JavaClass.init; 
  Extractor.setDataSource( filename );

Unfortunately, the android API appears to have a bug which means that the overload of setDataSource() which takes simply a filename does not work, and raises an exception. The solution to this problem is to open the file manually using the ‘File’ android API class, connect an input stream to it using the ‘FileInputStream’ API class, and then get a descriptor for the file using the ‘FileDescriptor’ class, finally pass the file descriptor to the setDataSource() method. This can be seen in the ExtractMeta() method of my source below…

This is the source for a class which extracts the required information from a media file…

unit uMediaMeta; 

interface 
uses 
  system.generics.collections; 

type 
  /// <summary> 
  /// This record is used to store information about the video tracks /// discovered in the target file. 
  /// </summary> 
  TVideoTrackInfo = record 
    FrameRate: int32; 
    xRes: int32; 
    yRes: int32; 
  end; 

  /// <summary> 
  /// This class provides array-style access to the video track information 
  /// discovered in the target media file. 
  /// </summary> 
  TMediaMeta = class 
  private
     fVideoTracks: TList<TVideoTrackInfo>; 
    fFilename: string; 
    fFilepath: string; 
  private 
    procedure ExtractMeta; 
    function getVideoTrackCount: uint32; 
    function getVideoTrackInfo(idx: uint32): TVideoTrackInfo; 
  public 
    constructor Create( Filepath: string; Filename: string ); reintroduce; 
    destructor Destroy; override; 
  public 
    property VideoTrackCount: uint32 read getVideoTrackCount; 
    property VideoTrackInfo[ idx: uint32 ]: TVideoTrackInfo read getVideoTrackInfo; 
  end; 

implementation 
uses 
  AndroidAPI.JNIBridge, 
  AndroidAPI.JNI.JavaTypes, 
  AndroidAPI.JNI.Media, 
  AndroidAPI.Helpers; 

constructor TMediaMeta.Create(Filepath, Filename: string); 
begin 
  inherited Create; 
  fFilePath := FilePath; 
  fFilename := Filename; 
  fVideoTracks := TList<TVideoTrackInfo>.Create; 
  ExtractMeta; 
end; 

destructor TMediaMeta.Destroy; 
begin 
  fVideoTracks.DisposeOf; 
  inherited; 
end;

procedure TMediaMeta.ExtractMeta; 
var 
  f: JFile;
  fis: JFileInputStream; 
  fd: JFileDescriptor; 
  Extractor: JMediaExtractor; 
  Format: JMediaFormat; 
  FormatClass: JMediaFormatClass; 
  numTracks: int32; 
  counter: int32; 
  idx: int32;
  mime: JString; 
  ARecord: TVideoTrackInfo; 
begin 
  f := TJFile.JavaClass.init( StringToJString( fFilepath ), StringToJString( fFilename ) ); 
  fis := TJFileInputStream.JavaClass.init( f ); 
  fd := fis.getFD; 
  Extractor := TJMediaExtractor.JavaClass.init; 
  Extractor.setDataSource( fd ); 
  numTracks := extractor.getTrackCount; 
  counter := 0;
  for idx := 0 to pred( numTracks ) do begin 
    format := extractor.getTrackFormat( idx ); 
    mime := format.getString( TJMediaFormat.JavaClass.KEY_MIME ); 
    if mime.startsWith( StringToJString( 'video/' ) ) then begin 
      if format.containsKey( TJMediaFormat.JavaClass.KEY_FRAME_RATE ) then begin 
        ARecord.FrameRate := format.getInteger( TJMediaFormat.JavaClass.KEY_FRAME_RATE ); 
        ARecord.xRes := format.getInteger (TJMediaFormat.JavaClass.KEY_WIDTH ); 
        ARecord.yRes := format.getInteger( TJMediaFormat.JavaClass.KEY_HEIGHT ); 
        fVideoTracks.Add( ARecord ); 
      end; 
    end; 
  end; 
end;

function TMediaMeta.getVideoTrackCount: uint32; 
begin 
  Result := fVideoTracks.Count; 
end; 

function TMediaMeta.getVideoTrackInfo( idx: uint32 ): TVideoTrackInfo; 
begin 
  Result := fVideoTracks.Items[ idx ]; 
end; 

end.

In order to use this class, we first need to build an application and deploy a media file with it to examine.
With your project open, go to “Project / Deployment” and add your media file, in my case, I added small.mp4.
Be sure to set the remote path field to “assets\internal”..

Drop in the uMediaMeta unit (the source I posted above), and add it to your forms uses list.
Add a button and a memo to the form, and name the memo  “mmoData”

For the button handler, add this code..

procedure TForm2.btnGetDataClick( Sender: TObject ); 
var 
  MediaMeta: TMediaMeta; 
  idx: int32;
begin 
  MediaMeta := TMediaMeta.Create( TPath.GetHomePath, 'small.mp4' ); 
  try //- Check there are video tracks. 
    if MediaMeta.VideoTrackCount = 0 then begin 
      mmoData.Lines.Add( 'No video tracks found in file.' ); 
      exit; 
    end;
    //- Loop them 
    for idx := 0 to pred( MediaMeta.VideoTrackCount ) do begin 
      mmoData.Lines.Add( 'Track ' + IntToStr(idx) + ' = ' + IntToStr( MediaMeta.VideoTrackInfo[ idx ].xRes ) + 'x' + IntToStr( MediaMeta.VideoTrackInfo[ idx ].yRes ) + '@' + IntToStr( MediaMeta.VideoTrackInfo[ idx ].FrameRate ) + 'fps' ); 
    end; 
  finally 
    MediaMeta.DisposeOf; 
  end; 
end;

When you deploy and run this application to an android device, and click on the button, you should see something like this:

Mission accomplished – you can use the resolution and fps data to determine if the file contains a PAL or NTSC track, or both, or neither.

This was a fun little side project for me this morning, and here’s the source code:


I hope you find it useful, and…
Thanks for Reading!

1 thought on “Media file meta-data on Android in Delphi.”

Comments are closed.