【问题标题】:Android MediaCodec Decoder: Slow Down Video PlaybackAndroid MediaCodec 解码器:减慢视频播放速度
【发布时间】:2019-08-29 23:04:19
【问题描述】:

我已经搜索过,但仍然找不到答案。

我正在使用 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
			
		}
	};
	
}

【问题讨论】:

    标签: android video android-video-player android-mediacodec


    【解决方案1】:

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

    仅当视频使用固定帧速率时,使用固定的播放速度值才有意义。有关可变帧率视频的示例,请参阅 Grafika 中生成的电影。其中一位玩家有一个“以 60fps 播放”按钮,该按钮会导致玩家忽略时间戳,以便您观察差异。

    顺便说一句,将播放循环放在surfaceChanged() 中并不是一个好主意。使用回调来触发活动,不要在其中构建整个播放器。

    【讨论】:

    • 非常好。我会检查链接。如果好,我会接受你的回答。
    【解决方案2】:

    找到了解决办法。不知道这是否是最佳解决方案。

    mediaFormat,我提取了captureRate

    captureRate = mediaFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
    

    那么,

      @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();
        }
    
      }

    【讨论】:

    • 难道不只是在 bufferInfo 上设置 PresentationTimeUs 而不是做得更好吗? info.PresentationTimeUs *= slowRatio; 只是一个想法。不确定它是否符合上下文。但是我已经看到,呈现时间是确定何时显示帧的唯一值,并且您可以在解码/编码过程中随时使用它。
    猜你喜欢
    • 2011-06-17
    • 2013-11-23
    • 2018-08-28
    • 1970-01-01
    • 2012-10-07
    • 2022-01-20
    • 1970-01-01
    • 2015-01-02
    • 2014-02-07
    相关资源
    最近更新 更多