【基础+代码流】在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;
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;
//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。
页:
[1]