【基础+代码流】在JAVA下利用MEDIACODEC进行硬解码

/****************************************************
/*	利用原生SURFACE和MEDIACODEC进行本地文件播放和流媒体播放
/*
/*
*****************************************************/

package com.timestech.xplayer;

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

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;

public class MainActivity extends Activity implements SurfaceHolder.Callback {
	private SurfaceView xSurface = null;
	private PlayerThread xPlayer = null;
	private TsRecv tR = null;

	private static final String videoUrl = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4";
	private static final String videoLocalUrl = "http://192.168.1.179:60000";
	private static final String videoPath = "/mnt/sdcard/Movies/std_p.ts";

	@SuppressLint("SdCardPath")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//隐藏标题栏
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		//沉浸模式
		getWindow().getDecorView().setSystemUiVisibility(
														View.SYSTEM_UI_FLAG_FULLSCREEN |
														View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
														View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
		// 设置横屏 禁止翻转
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

		// 禁止休眠
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
				WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

		setContentView(R.layout.activity_main);

		//----------------------------------------surfaceView播放视频----------------------------------------//
		xSurface = (SurfaceView)findViewById(R.id.surfaceView);

        /*
		LayoutParams lp = (LayoutParams) xSurface.getLayoutParams();	//导致闪退
		lp.width = 1280;
		lp.height = 647;
		xSurface.setLayoutParams(lp);
		*/

	    SurfaceHolder surfaceHolder = xSurface.getHolder(); //SurfaceHolder是SurfaceView的控制接口
	    surfaceHolder.addCallback(this); //因为这个类实现了SurfaceHolder.Callback接口,所以回调参数直接this
		//surfaceHolder.setFixedSize(1280, 720); //显示的分辨率,不是surface的大小,不设置为视频默认
	}

	private class PlayerThread extends Thread {
		private MediaExtractor extractor;
		private MediaCodec decoder;
		private Surface surface;
		private boolean DEMUX_ANDROID = false;
		public PlayerThread(Surface surface) {
			this.surface = surface;
		}

		@Override
		public void run() {
			if(DEMUX_ANDROID)
			{
				////////////////////MediaExtractor
				extractor = new MediaExtractor();
				try {
					extractor.setDataSource(videoPath);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				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(mime);
						decoder.configure(format, surface, null, 0);
						break;
					}
				}
			}
			else
			{
				tR = new TsRecv();
				tR.start(videoLocalUrl, 0);

				MediaFormat format;
				format = MediaFormat.createVideoFormat("video/avc", 1280, 720);
				String mime = format.getString(MediaFormat.KEY_MIME);
				decoder = MediaCodec.createDecoderByType(mime);
				decoder.configure(format, surface, null, 0);
			}

			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();
			long startMs = System.currentTimeMillis();
			int sampleSize = 0;
			boolean isEOS = false;

			while (true) {
				//if (!isEOS)
				{
					int inIndex = decoder.dequeueInputBuffer(10000);
					if (inIndex >= 0) {
						ByteBuffer buffer = inputBuffers[inIndex];

						if(DEMUX_ANDROID)
						{
							////////////////////MediaExtractor
							sampleSize = extractor.readSampleData(buffer, 0);
							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 {
								///////////////////   MediaExtractor
								//Log.d("SAMPLESIZE", "SIZE["+sampleSize+"]");
								long timeus = extractor.getSampleTime();
								//Log.d("TIMESUS", "extractor.getSampleTime() is "+timeus);
								decoder.queueInputBuffer(inIndex, 0, sampleSize, timeus, 0);
								extractor.advance();
							}
						}
						else
						{
							sampleSize = tR.readSampleData(buffer);

							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);
							} else {
								//Log.d("SAMPLESIZE", "SIZE["+sampleSize+"]");
								decoder.queueInputBuffer(inIndex, 0, sampleSize, 0, 0);
								//Log.d("DecodeActivity", "sampleSize   "+sampleSize);
							}
						}
					}
					else
						continue;
				}

				int outIndex = decoder.dequeueOutputBuffer(info, 0);
				//Log.d("SAMPLESIZE", "OUTINDEX["+outIndex+"]");
				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:
					if(DEMUX_ANDROID)
					{
						////////////////////MediaExtractor
						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

						//Log.v("DecodeActivity", "info.presentationTimeUs is  " + info.presentationTimeUs/1000 + " currTimeMilli is " + System.currentTimeMillis() + " startMs is " + startMs);
						while (info.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
							try {
								sleep(12);
							} catch (InterruptedException e) {
								e.printStackTrace();
								break;
							}
						}
					}
					else
					{
						try {
							sleep(30);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					decoder.releaseOutputBuffer(outIndex, true);
					break;
				}

				// All decoded frames have been rendered, we can stop playing now

				if(DEMUX_ANDROID)
				{
					////////////////////MediaExtractor
					if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
						Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
						break;
					}
				}
				else
				{

				}
			}

			Log.d("PlayerThread:", "OVER.......................................");

			if(DEMUX_ANDROID)
			{
				////////////////////MediaExtractor
				decoder.stop();
				decoder.release();
				extractor.release();
			}
			return ;
		}
	}

	public void surfaceCreated(SurfaceHolder holder) {
		if (xPlayer == null) {
			xPlayer = new PlayerThread(holder.getSurface());
			xPlayer.start();
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		// TODO Auto-generated method stub
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO Auto-generated method stub

	}

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

这个强大了,楼主大大能否开源项目代码呢?
TsRecv这个类是怎样操作的呢?

参考下实现思路

huntengan 发表于 2016-11-30 16:10
这个强大了,楼主大大能否开源项目代码呢?
TsRecv这个类是怎样操作的呢?

TsRecv是自己写的针对TS流的接收,解复用,封装,打包库,是在JNI下用C实现的

jingjin221 发表于 2016-12-15 09:51
TsRecv是自己写的针对TS流的接收,解复用,封装,打包库,是在JNI下用C实现的

你可以用FFMEPG的解复用库,支持的格式更多

请教一下,怎么从MediaCodec获取解码后的帧呢?
我是使用的decoder.configure中的surface使用null,然后在dequeueOutputBuffer后使用decoder.getOutputBuffers()或者getOutputImage()得到的bytebuffer都是空的position都为0。