You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
12 KiB
343 lines
12 KiB
|
|
package com.frank.ffmpeg.metadata;
|
|
|
|
import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.res.AssetFileDescriptor;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.net.Uri;
|
|
import android.util.Log;
|
|
|
|
import java.io.FileDescriptor;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
|
|
/**
|
|
* Retrieve frame and extract metadata from an input media file.
|
|
*/
|
|
public class FFmpegMediaRetriever {
|
|
|
|
static {
|
|
try {
|
|
System.loadLibrary("media-handle");
|
|
} catch (UnsatisfiedLinkError e) {
|
|
Log.e("FFmpegMediaRetriever", "loadLibrary error=" + e.toString());
|
|
}
|
|
}
|
|
|
|
// The field is accessed by native method
|
|
private long mNativeRetriever;
|
|
|
|
public FFmpegMediaRetriever() {
|
|
native_init();
|
|
native_setup();
|
|
}
|
|
|
|
public void setDataSource(String path) {
|
|
native_setDataSource(path);
|
|
}
|
|
|
|
/**
|
|
* Sets the data source (FileDescriptor) to use. It is the caller's
|
|
* responsibility to close the file descriptor. It is safe to do so as soon
|
|
* as this call returns. Call this method before the rest of the methods in
|
|
* this class. This method may be time-consuming.
|
|
*
|
|
* @param fd the FileDescriptor for the file you want to play
|
|
*/
|
|
public void setDataSource(FileDescriptor fd)
|
|
throws IllegalArgumentException {
|
|
if (fd == null) {
|
|
return;
|
|
}
|
|
native_setDataSourceFD(fd, 0, 0x7ffffffffffffffL);
|
|
}
|
|
|
|
/**
|
|
* Sets the data source as a content Uri. Call this method before
|
|
* the rest of the methods in this class. This method may be time-consuming.
|
|
*
|
|
* @param context the Context to use when resolving the Uri
|
|
* @param uri the Content URI of the data you want to play
|
|
*
|
|
*/
|
|
public void setDataSource(Context context, Uri uri) throws IllegalArgumentException, SecurityException {
|
|
if (uri == null) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
|
|
String scheme = uri.getScheme();
|
|
if(scheme == null || scheme.equals("file")) {
|
|
setDataSource(uri.getPath());
|
|
return;
|
|
}
|
|
|
|
AssetFileDescriptor fd = null;
|
|
try {
|
|
ContentResolver resolver = context.getContentResolver();
|
|
try {
|
|
fd = resolver.openAssetFileDescriptor(uri, "r");
|
|
} catch(FileNotFoundException e) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
if (fd == null) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
FileDescriptor descriptor = fd.getFileDescriptor();
|
|
if (!descriptor.valid()) {
|
|
throw new IllegalArgumentException();
|
|
}
|
|
if (fd.getDeclaredLength() < 0) {
|
|
setDataSource(descriptor);
|
|
} else {
|
|
native_setDataSourceFD(descriptor, fd.getStartOffset(), fd.getDeclaredLength());
|
|
}
|
|
return;
|
|
} catch (SecurityException ex) {
|
|
ex.printStackTrace();
|
|
} finally {
|
|
try {
|
|
if (fd != null) {
|
|
fd.close();
|
|
}
|
|
} catch(IOException ioEx) {
|
|
ioEx.printStackTrace();
|
|
}
|
|
}
|
|
setDataSource(uri.toString());
|
|
}
|
|
|
|
public String extractMetadata(String key) {
|
|
if (key == null || key.isEmpty()) {
|
|
return null;
|
|
}
|
|
return native_extractMetadata(key);
|
|
}
|
|
|
|
/**
|
|
* Call this method after setDataSource(). This method finds a
|
|
* representative frame close to the given time position by considering
|
|
* the given option if possible, and returns it as a bitmap. This is
|
|
* useful for generating a thumbnail for an input data source or just
|
|
* obtain and display a frame at the given time position.
|
|
*
|
|
* @param timeUs The time position where the frame will be retrieved.
|
|
*
|
|
* @param option a hint on how the frame is found. Use
|
|
* {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
|
|
* that has a timestamp earlier than or the same as timeUs. Use
|
|
* {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
|
|
* that has a timestamp later than or the same as timeUs. Use
|
|
* {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
|
|
* that has a timestamp closest to or the same as timeUs. Use
|
|
* {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
|
|
* or may not be a sync frame but is closest to or the same as timeUs.
|
|
* {@link #OPTION_CLOSEST} often has larger performance overhead compared
|
|
* to the other options if there is no sync frame located at timeUs.
|
|
*
|
|
* @return A Bitmap containing a representative video frame, which
|
|
* can be null, if such a frame cannot be retrieved.
|
|
*/
|
|
public Bitmap getFrameAtTime(long timeUs, int option) {
|
|
if (option < OPTION_PREVIOUS_SYNC ||
|
|
option > OPTION_CLOSEST) {
|
|
throw new IllegalArgumentException("Unsupported option: " + option);
|
|
}
|
|
|
|
BitmapFactory.Options bitmapOptions= new BitmapFactory.Options();
|
|
bitmapOptions.inScaled = true;
|
|
byte [] picture = native_getFrameAtTime(timeUs, option);
|
|
if (picture != null) {
|
|
return BitmapFactory.decodeByteArray(picture, 0, picture.length, bitmapOptions);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public Bitmap getFrameAtTime(long timeUs) {
|
|
return getFrameAtTime(timeUs, OPTION_CLOSEST_SYNC);
|
|
}
|
|
|
|
/**
|
|
* Call this method after setDataSource(). This method finds a
|
|
* representative frame close to the given time position by considering
|
|
* the given option if possible, and returns it as a bitmap. This is
|
|
* useful for generating a thumbnail for an input data source or just
|
|
* obtain and display a frame at the given time position.
|
|
*
|
|
* @param timeUs The time position where the frame will be retrieved.
|
|
*
|
|
* @param option a hint on how the frame is found. Use
|
|
* {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
|
|
* that has a timestamp earlier than or the same as timeUs. Use
|
|
* {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
|
|
* that has a timestamp later than or the same as timeUs. Use
|
|
* {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
|
|
* that has a timestamp closest to or the same as timeUs. Use
|
|
* {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
|
|
* or may not be a sync frame but is closest to or the same as timeUs.
|
|
* {@link #OPTION_CLOSEST} often has larger performance overhead compared
|
|
* to the other options if there is no sync frame located at timeUs.
|
|
*
|
|
* @return A Bitmap containing a representative video frame, which
|
|
* can be null, if such a frame cannot be retrieved.
|
|
*/
|
|
public Bitmap getScaledFrameAtTime(long timeUs, int option, int width, int height) {
|
|
if (option < OPTION_PREVIOUS_SYNC ||
|
|
option > OPTION_CLOSEST) {
|
|
throw new IllegalArgumentException("Unsupported option: " + option);
|
|
}
|
|
|
|
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
|
|
bitmapOptions.inScaled = true;
|
|
byte [] picture = native_getScaleFrameAtTime(timeUs, option, width, height);
|
|
if (picture != null) {
|
|
return BitmapFactory.decodeByteArray(picture, 0, picture.length, bitmapOptions);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public Bitmap getScaledFrameAtTime(long timeUs, int width, int height) {
|
|
return getScaledFrameAtTime(timeUs, OPTION_CLOSEST_SYNC, width, height);
|
|
}
|
|
|
|
public Bitmap getAudioThumbnail() {
|
|
byte[] picture = native_getAudioThumbnail();
|
|
if (picture != null) {
|
|
return BitmapFactory.decodeByteArray(picture, 0, picture.length, null);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public void release() {
|
|
native_release();
|
|
}
|
|
|
|
private native void native_setup();
|
|
|
|
private native void native_init();
|
|
|
|
private native void native_setDataSource(String path) throws IllegalArgumentException;
|
|
|
|
private native void native_setDataSourceFD(FileDescriptor fd, long offset, long length) throws IllegalArgumentException;
|
|
|
|
private native String native_extractMetadata(String key);
|
|
|
|
private native void native_setSurface(Object surface);
|
|
|
|
private native byte[] native_getFrameAtTime(long timeUs, int option);
|
|
|
|
private native byte[] native_getScaleFrameAtTime(long timeUs, int option, int width, int height);
|
|
|
|
private native byte[] native_getAudioThumbnail();
|
|
|
|
private native void native_release();
|
|
|
|
/**
|
|
* This option is used with {@link #getFrameAtTime(long, int)} to retrieve
|
|
* a sync (or key) frame associated with a data source that is located
|
|
* right before or at the given time.
|
|
*
|
|
* @see #getFrameAtTime(long, int)
|
|
*/
|
|
int OPTION_PREVIOUS_SYNC = 0x00;
|
|
/**
|
|
* This option is used with {@link #getFrameAtTime(long, int)} to retrieve
|
|
* a sync (or key) frame associated with a data source that is located
|
|
* right after or at the given time.
|
|
*
|
|
* @see #getFrameAtTime(long, int)
|
|
*/
|
|
int OPTION_NEXT_SYNC = 0x01;
|
|
/**
|
|
* This option is used with {@link #getFrameAtTime(long, int)} to retrieve
|
|
* a sync (or key) frame associated with a data source that is located
|
|
* closest to (in time) or at the given time.
|
|
*
|
|
* @see #getFrameAtTime(long, int)
|
|
*/
|
|
int OPTION_CLOSEST_SYNC = 0x02;
|
|
/**
|
|
* This option is used with {@link #getFrameAtTime(long, int)} to retrieve
|
|
* a frame (not necessarily a key frame) associated with a data source that
|
|
* is located closest to or at the given time.
|
|
*
|
|
* @see #getFrameAtTime(long, int)
|
|
*/
|
|
int OPTION_CLOSEST = 0x03;
|
|
|
|
|
|
/**
|
|
* The metadata key to retrieve the original name of the file.
|
|
*/
|
|
public static String METADATA_KEY_FILENAME = "filename";
|
|
/**
|
|
* The metadata key to retrieve the main language.
|
|
*/
|
|
public static String METADATA_KEY_LANGUAGE = "language";
|
|
/**
|
|
* The metadata key to retrieve the name.
|
|
*/
|
|
public static String METADATA_KEY_TITLE = "title";
|
|
/**
|
|
* The metadata key to retrieve the general bitrate.
|
|
*/
|
|
public static String METADATA_KEY__BITRATE = "bitrate";
|
|
/**
|
|
* The metadata key to retrieve the duration in milliseconds.
|
|
*/
|
|
public static String METADATA_KEY_DURATION = "duration";
|
|
/**
|
|
* The metadata key to retrieve the audio codec.
|
|
*/
|
|
public static String METADATA_KEY_AUDIO_CODEC = "audio_codec";
|
|
/**
|
|
* The metadata key to retrieve the video codec.
|
|
*/
|
|
public static String METADATA_KEY_VIDEO_CODEC = "video_codec";
|
|
/**
|
|
* This key retrieves the video degree of rotation.
|
|
* The video rotation angle may be 0, 90, 180, or 270 degrees.
|
|
*/
|
|
public static String METADATA_KEY_VIDEO_ROTATION = "rotate";
|
|
/**
|
|
* This metadata key retrieves the average frame rate (in frames/sec).
|
|
*/
|
|
public static String METADATA_KEY_FRAME_RATE = "frame_rate";
|
|
/**
|
|
* The metadata key to retrieve the file size in bytes.
|
|
*/
|
|
public static String METADATA_KEY_FILE_SIZE = "file_size";
|
|
/**
|
|
* The metadata key to retrieve the video width.
|
|
*/
|
|
public static String METADATA_KEY_VIDEO_WIDTH = "video_width";
|
|
/**
|
|
* The metadata key to retrieve the video height.
|
|
*/
|
|
public static String METADATA_KEY_VIDEO_HEIGHT = "video_height";
|
|
/**
|
|
* The metadata key to retrieve the mime type.
|
|
*/
|
|
public static String METADATA_KEY_MIME_TYPE = "mime_type";
|
|
/**
|
|
* The metadata key to retrieve sample rate.
|
|
*/
|
|
public static String METADATA_KEY_SAMPLE_RATE = "sample_rate";
|
|
/**
|
|
* The metadata key to retrieve channel count.
|
|
*/
|
|
public static String METADATA_KEY_CHANNEL_COUNT = "channel_count";
|
|
/**
|
|
* The metadata key to retrieve channel layout.
|
|
*/
|
|
public static String METADATA_KEY_CHANNEL_LAYOUT = "channel_layout";
|
|
/**
|
|
* The metadata key to retrieve pixel format.
|
|
*/
|
|
public static String METADATA_KEY_PIXEL_FORMAT = "pixel_format";
|
|
|
|
}
|
|
|