jingjin221 发表于 2016-10-12 17:06:17

【基础+代码流】在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();
    }
}

huntengan 发表于 2016-11-30 16:10:35

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

csworld 发表于 2016-11-30 20:15:34

参考下实现思路

jingjin221 发表于 2016-12-15 09:51:57

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

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

jingjin221 发表于 2016-12-15 09:52:50

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

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

shiqizheng 发表于 2017-1-13 15:37:27

darkise 发表于 2018-3-15 11:16:32

请教一下,怎么从MediaCodec获取解码后的帧呢?
我是使用的decoder.configure中的surface使用null,然后在dequeueOutputBuffer后使用decoder.getOutputBuffers()或者getOutputImage()得到的bytebuffer都是空的position都为0。
页: [1]
查看完整版本: 【基础+代码流】在JAVA下利用MEDIACODEC进行硬解码