RAD Studio 10.4.1 is now available! Learn more. Looking for discounts? Visit our Special Offers page!
News

Media file meta-data on Android in Delphi.

Author: Craig Chapman

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

The Problem.

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.

The Solution.

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;

{ TMediaMeta }

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 “assetsinternal”..

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.

Conclusion

This was a fun little side project for me this morning, and here’s the source code: MediaExtractor 
I hope you find it useful, and…

Thanks for Reading!

 


Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Start Free Trial   Learn More About Upgrading

Related posts
C++

Articles and News from the C++ community

News

Building REST services with RAD Server (Delphi & C++ Builder)

News

A vulkan header generator for Delphi.

C++

Learn to Program with Community Edition

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

IN THE ARTICLES