Android MediaCodec解码器:减慢视频播放速度

时间:2014-12-17 17:19:01

标签: android video android-video-player mediacodec

我已经搜索过但仍无法找到答案。

我正在制作一个简单的视频播放器,使用API​​ 21中的androids MediaCodec(使用解码器和表面)。但是,视频播放速度非常快。我如何以正常速度播放视频?

这是我的代码:

package com.bd.mediacodec;

import java.io.IOException;
import java.nio.ByteBuffer;

import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodec.CodecException;
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 DecodeActivity extends Activity implements SurfaceHolder.Callback {
	private static final String SAMPLE = Environment.getExternalStorageDirectory() + "/video.mp4";
	private Surface surface;
	private MediaExtractor extractor;
	private MediaCodec decoder;
	boolean isEOS = false;
	long extractorSampleTime = 0;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		SurfaceView sv = new SurfaceView(this);
		sv.getHolder().addCallback(this);
		setContentView(sv);
	}

	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		
		extractor = new MediaExtractor();
		try {
			extractor.setDataSource(SAMPLE);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		for (int i = 0; i < extractor.getTrackCount(); i++) {
			MediaFormat mediaFormat = extractor.getTrackFormat(i);
			String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
			if (mime.startsWith("video/")) {
				extractor.selectTrack(i);
				try {
					decoder = MediaCodec.createDecoderByType(mime);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				surface = holder.getSurface();
				decoder.configure(mediaFormat, surface, null, 0);
				break;
			}
		}

		if (decoder == null) {
			Log.e("DecodeActivity", "Can't find / Open Video");
			return;
		}
		// Adding Callback
		decoder.setCallback(mDecoderCallback);
		decoder.start();		    		
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		
		decoder.stop();
		decoder.release();
		extractor.release();    		
	}

	MediaCodec.Callback mDecoderCallback = new MediaCodec.Callback() {
		
		@Override
		public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void onOutputBufferAvailable(MediaCodec codec, int index,
				BufferInfo info) {
			// TODO Auto-generated method stub
			// Release output buffer.
			codec.releaseOutputBuffer(index, true);    			    			
		}
		
		@Override
		public void onInputBufferAvailable(MediaCodec codec, int index) {
			// TODO Auto-generated method stub

			if(!isEOS){
				ByteBuffer buffer = codec.getInputBuffer(index);
				int sampleSize = extractor.readSampleData(buffer, 0);
				if (sampleSize < 0) {
					
					Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
					decoder.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
					isEOS = true;
				} else {
					extractorSampleTime = extractor.getSampleTime();
					decoder.queueInputBuffer(index, 0, sampleSize, extractorSampleTime, 0);
					extractor.advance();
				}
			}    			    		
		}
		
		@Override
		public void onError(MediaCodec codec, CodecException e) {
			// TODO Auto-generated method stub
			
		}
	};
	
}

2 个答案:

答案 0 :(得分:3)

Grafika中有一对简单的视频播放器,一个用于SurfaceView,一个用于TextureView。他们都使用SpeedControlCallback类来管理播放速度。关键是使用每帧的显示时间戳来确定在显示下一帧之前等待多长时间。

如果视频使用固定的帧速率,则仅使用固定值作为播放速度才有意义。有关可变帧速率视频的示例,请参阅Grafika中生成的电影。其中一个玩家有一个“60fps的游戏”按钮,让玩家忽略时间戳,这样你就可以观察到差异。

BTW,将播放循环放在surfaceChanged()内并不是一个好主意。使用回调来触发活动,不要在其中构建整个玩家。

答案 1 :(得分:1)

找到解决方案。不要知道它是否是最好的解决方案。

mediaFormat开始,我提取了captureRate

captureRate = mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);

然后,

&#13;
&#13;
  @Override
  public void onOutputBufferAvailable(MediaCodec codec, int index,
  BufferInfo info) {
    // TODO Auto-generated method stub
    // Release output buffer.

    codec.releaseOutputBuffer(index, true);
    try {

      Thread.sleep((int)1000/captureRate);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

  }
&#13;
&#13;
&#13;