Pujari Gupta
- Forum posts: 1
May 28, 2013, 12:28:47 PM via Website
May 28, 2013 12:28:47 PM via Website
Hi,
I wanted to decode video frames without using extractor.
So I just tried a small sample, where I use media extractor but I don't do "extractor.readsample()" to copy the bitstream data into the input buffer instead I use FFmpeg parser , inside JNI, where I memcopy the video frames into the input byte buffers and then queued the input buffer..
But when I call "decoder.dequeueOutputBuffer(info, 10000)" , it returns "MediaCodec.INFO_TRY_AGAIN_LATER".
While it works fine if I use extractor.readsample().
Please find the code snippet below :
Java Side :
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class VideoBrowser extends Activity implements SurfaceHolder.Callback {
private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/obama.mp4";
private PlayerThread mPlayer = null;
private static native <jintArray> int AVinitializecntxt(String strl, jintArray arr);
private native int AVREADVIDEO(byte[] array);
public int FLAG = 0;
public int jk = 0;
File f1;
FileOutputStream f;
static {
Log.i("ABCD", "BEFORE");
System.loadLibrary("ffmpeg");
System.loadLibrary("ffmpeg-test-jni");
Log.i("ABCD", "Success");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SurfaceView sv = new SurfaceView(this);
sv.getHolder().addCallback(this);
setContentView(sv);
int val;
int[] array = new int[6];
int END_OF_FILE = 0;
int aud_stream = 0;
int vid_stream = 0;
String urlString = "/mnt/sdcard/obama.mp4";
f1 = new File("/mnt/sdcard/t.h264");
try {
f = new FileOutputStream(f1);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// This is where I call the function to initialize the ffmpeg inside JNI
val = AVinitializecntxt(urlString, array);
FLAG = val;
Log.i("ABCD", "FLAG : "+ FLAG + val);
}
protected void onDestroy() {
super.onDestroy();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mPlayer == null) {
mPlayer = new PlayerThread(holder.getSurface());
mPlayer.start();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mPlayer != null) {
mPlayer.interrupt();
}
}
private class PlayerThread extends Thread {
private MediaExtractor extractor;
private MediaCodec decoder;
private Surface surface;
// private VideoPlayer VideoPlayerAPIInterfaceClass = new VideoPlayer();
public PlayerThread(Surface surface) {
this.surface = surface;
}
@Override
public void run() {
if(FLAG == 1){
extractor = new MediaExtractor();
extractor.setDataSource(SAMPLE);
for (int i = 0; i < extractor.getTrackCount(); i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
extractor.selectTrack(i);
decoder = MediaCodec.createDecoderByType("video/avc");
// Log.i("ABCD", "MIME : " + mime);
decoder.configure(format, surface, null, 0);
break;
}
}
if (decoder == null) {
Log.e("DecodeActivity", "Can't find video info!");
return;
}
decoder.start();
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
BufferInfo info = new BufferInfo();
boolean isEOS = false;
long startMs = System.currentTimeMillis();
int outIndex1 = -1 ;
while(outIndex1 < 0){
outIndex1 = decoder.dequeueOutputBuffer(info, 10000);
Log.i("ABCD", "etgeuieoy");
}
while (!Thread.interrupted()) {
if (!isEOS) {
int inIndex = decoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
// int sampleSize = extractor.readSampleData(buffer, 0);
byte[] bytes = new byte[buffer.capacity()];
// This is where we call JNI function to memcopy the encoded bitstream into the input buffer
int sampleSize = AVREADVIDEO(bytes);
buffer.clear();
buffer.put(bytes, 0, sampleSize);
if (sampleSize < 0) {
// We shouldn't stop the playback at this point, just pass the EOS
// flag to decoder, we will get it again from the
// dequeueOutputBuffer
// Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
decoder.queueInputBuffer(inIndex, 0, sampleSize,0, 0);
extractor.advance();
}
}
}
int outIndex = decoder.dequeueOutputBuffer(info, 10000);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
break;
default:
ByteBuffer buffer = outputBuffers[outIndex];
Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);
// We use a very simple clock to keep the video FPS, or the video
// playback will be too fast
while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
// Log.i("ABCD", "RELEASING OUTPUT BUFFER");
decoder.releaseOutputBuffer(outIndex, true);
//decoder.releaseOutputBuffer(outIndex, false);
break;
}
// All decoded frames have been rendered, we can stop playing now
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
break;
}
}
decoder.stop();
decoder.release();
extractor.release();
}
}
}
}
JNI Side :
JNIEXPORT jint JNICALL
Java_com_alldigital_videoplayer_VideoBrowser_AVREADVIDEO(JNIEnv *pEnv,
jobject pObj, jbyteArray array ) {
AV_ctxt *avctxt = &aud_vid_ctxt;
jbyte *buf = (*pEnv)->GetByteArrayElements(pEnv,
array, NULL);
if (buf == NULL) {
LOGERR(10, "AVVIDEOREAD", "Bytes null");
}
AVPacket *packet;
packet = av_malloc(sizeof(AVPacket));
av_init_packet(packet);
int avread_res = av_read_frame(avctxt->gFormatCtx, packet);
int size = packet->size;
if (avread_res >= 0) {
if (packet->stream_index == avctxt->gVideoStreamIndex) {
// packet->size,packet->
if (NULL
== memcpy(buf,
(char *) packet->data,
packet->size
))
LOGERR(10, "AV_AUDIO_DECODE",
"memcpy for audio buffer failed");
}
}
(*pEnv)->ReleaseByteArrayElements(pEnv, array, buf,
0);
av_free_packet(packet);
packet = NULL;
return size;
}
Can anyone quickly help me on this,
though I am copying the encoded data of each frame through FFmpeg without calling extractor , then why I am getting this outputbuffer timeout issue.
Thanks
I wanted to decode video frames without using extractor.
So I just tried a small sample, where I use media extractor but I don't do "extractor.readsample()" to copy the bitstream data into the input buffer instead I use FFmpeg parser , inside JNI, where I memcopy the video frames into the input byte buffers and then queued the input buffer..
But when I call "decoder.dequeueOutputBuffer(info, 10000)" , it returns "MediaCodec.INFO_TRY_AGAIN_LATER".
While it works fine if I use extractor.readsample().
Please find the code snippet below :
Java Side :
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class VideoBrowser extends Activity implements SurfaceHolder.Callback {
private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/obama.mp4";
private PlayerThread mPlayer = null;
private static native <jintArray> int AVinitializecntxt(String strl, jintArray arr);
private native int AVREADVIDEO(byte[] array);
public int FLAG = 0;
public int jk = 0;
File f1;
FileOutputStream f;
static {
Log.i("ABCD", "BEFORE");
System.loadLibrary("ffmpeg");
System.loadLibrary("ffmpeg-test-jni");
Log.i("ABCD", "Success");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SurfaceView sv = new SurfaceView(this);
sv.getHolder().addCallback(this);
setContentView(sv);
int val;
int[] array = new int[6];
int END_OF_FILE = 0;
int aud_stream = 0;
int vid_stream = 0;
String urlString = "/mnt/sdcard/obama.mp4";
f1 = new File("/mnt/sdcard/t.h264");
try {
f = new FileOutputStream(f1);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// This is where I call the function to initialize the ffmpeg inside JNI
val = AVinitializecntxt(urlString, array);
FLAG = val;
Log.i("ABCD", "FLAG : "+ FLAG + val);
}
protected void onDestroy() {
super.onDestroy();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mPlayer == null) {
mPlayer = new PlayerThread(holder.getSurface());
mPlayer.start();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mPlayer != null) {
mPlayer.interrupt();
}
}
private class PlayerThread extends Thread {
private MediaExtractor extractor;
private MediaCodec decoder;
private Surface surface;
// private VideoPlayer VideoPlayerAPIInterfaceClass = new VideoPlayer();
public PlayerThread(Surface surface) {
this.surface = surface;
}
@Override
public void run() {
if(FLAG == 1){
extractor = new MediaExtractor();
extractor.setDataSource(SAMPLE);
for (int i = 0; i < extractor.getTrackCount(); i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
extractor.selectTrack(i);
decoder = MediaCodec.createDecoderByType("video/avc");
// Log.i("ABCD", "MIME : " + mime);
decoder.configure(format, surface, null, 0);
break;
}
}
if (decoder == null) {
Log.e("DecodeActivity", "Can't find video info!");
return;
}
decoder.start();
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
BufferInfo info = new BufferInfo();
boolean isEOS = false;
long startMs = System.currentTimeMillis();
int outIndex1 = -1 ;
while(outIndex1 < 0){
outIndex1 = decoder.dequeueOutputBuffer(info, 10000);
Log.i("ABCD", "etgeuieoy");
}
while (!Thread.interrupted()) {
if (!isEOS) {
int inIndex = decoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
// int sampleSize = extractor.readSampleData(buffer, 0);
byte[] bytes = new byte[buffer.capacity()];
// This is where we call JNI function to memcopy the encoded bitstream into the input buffer
int sampleSize = AVREADVIDEO(bytes);
buffer.clear();
buffer.put(bytes, 0, sampleSize);
if (sampleSize < 0) {
// We shouldn't stop the playback at this point, just pass the EOS
// flag to decoder, we will get it again from the
// dequeueOutputBuffer
// Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
decoder.queueInputBuffer(inIndex, 0, sampleSize,0, 0);
extractor.advance();
}
}
}
int outIndex = decoder.dequeueOutputBuffer(info, 10000);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.d("DecodeActivity", "New format " + decoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
break;
default:
ByteBuffer buffer = outputBuffers[outIndex];
Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);
// We use a very simple clock to keep the video FPS, or the video
// playback will be too fast
while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
// Log.i("ABCD", "RELEASING OUTPUT BUFFER");
decoder.releaseOutputBuffer(outIndex, true);
//decoder.releaseOutputBuffer(outIndex, false);
break;
}
// All decoded frames have been rendered, we can stop playing now
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
break;
}
}
decoder.stop();
decoder.release();
extractor.release();
}
}
}
}
JNI Side :
JNIEXPORT jint JNICALL
Java_com_alldigital_videoplayer_VideoBrowser_AVREADVIDEO(JNIEnv *pEnv,
jobject pObj, jbyteArray array ) {
AV_ctxt *avctxt = &aud_vid_ctxt;
jbyte *buf = (*pEnv)->GetByteArrayElements(pEnv,
array, NULL);
if (buf == NULL) {
LOGERR(10, "AVVIDEOREAD", "Bytes null");
}
AVPacket *packet;
packet = av_malloc(sizeof(AVPacket));
av_init_packet(packet);
int avread_res = av_read_frame(avctxt->gFormatCtx, packet);
int size = packet->size;
if (avread_res >= 0) {
if (packet->stream_index == avctxt->gVideoStreamIndex) {
// packet->size,packet->
if (NULL
== memcpy(buf,
(char *) packet->data,
packet->size
))
LOGERR(10, "AV_AUDIO_DECODE",
"memcpy for audio buffer failed");
}
}
(*pEnv)->ReleaseByteArrayElements(pEnv, array, buf,
0);
av_free_packet(packet);
packet = NULL;
return size;
}
Can anyone quickly help me on this,
though I am copying the encoded data of each frame through FFmpeg without calling extractor , then why I am getting this outputbuffer timeout issue.
Thanks
— modified on May 29, 2013, 8:37:47 AM
Recommended editorial content
With your consent, external content is loaded here.
By clicking on the button above, you agree that external content may be displayed to you. Personal data may be transmitted to third-party providers in the process. You can find more information about this in our Privacy Policy.