Android如何播放h.264格式视频

关于h.264格式的视频文件播放,我们可以通过android.media.MediaCodec这个接口去解码实现,这个对搞流媒体,安防实时视频传输的朋友应该很熟悉,这边只是个demo,有需要的同学自行下载源码研究吧!

效果图如下:

这里写图片描述

关键代码如下:

这里写图片描述

这里写图片描述

package com.asir.h264demo;

import android.annotation.TargetApi;
import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

public class MainActivity extends Activity {
    private SurfaceView mSurfaceView;
    private Button mReadButton;
    private MediaCodec mCodec;
    Thread readFileThread;
    boolean isInit = false;

    // Video Constants
    private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video
    private static int VIDEO_WIDTH = 1024;
    private static int VIDEO_HEIGHT = 600;
    private final static int TIME_INTERNAL = 30;
    private final static int HEAD_OFFSET = 512;
    private final static String LOG_TAG = "asir";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        mReadButton = (Button) findViewById(R.id.btn_readfile);
        mReadButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (!isInit) {
                    initDecoder();
                    isInit = true;
                }

                readFileThread = new Thread(readFile);
                readFileThread.start();
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
     protected void onDestroy() {
        super.onDestroy();
        readFileThread.interrupt();
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public void initDecoder() {
        try {
            mCodec = MediaCodec.createDecoderByType(MIME_TYPE);
            MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, VIDEO_WIDTH, VIDEO_HEIGHT);
            mCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(), null, 0);
            mCodec.start();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

 int mCount = 0;
//    final byte[] h264Split = {0, 0, 0, 1};

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public boolean onFrame(byte[] buf, int length) {
        Log.e(LOG_TAG, "onFrame start");
        // Get input buffer index
        long ptsUsec = 0;

        if (mCount == 0) {
            ByteBuffer[] inputBuffers = mCodec.getInputBuffers();
            int inputBufferIndex = mCodec.dequeueInputBuffer(100000);

            Log.e(LOG_TAG, "onFrame index:" + inputBufferIndex);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                inputBuffer.clear();
                inputBuffer.put(buf, 0, length).rewind();
                mCodec.queueInputBuffer(inputBufferIndex, 0, length, 0, MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
            } else {
                Log.e(LOG_TAG, "inputBufferIndex < 0 " + inputBufferIndex);
                return false;
            }
             } else {
            ByteBuffer[] inputBuffers = mCodec.getInputBuffers();
            int inputBufferIndex = mCodec.dequeueInputBuffer(100000);

            Log.e(LOG_TAG, "onFrame index:" + inputBufferIndex);
            if (inputBufferIndex >= 0) {
                inputBuffers[inputBufferIndex].clear();
                inputBuffers[inputBufferIndex].put(buf, 0, length).rewind();
                mCodec.queueInputBuffer(inputBufferIndex, 0, length, ptsUsec, 0);
            } else {
                Log.e(LOG_TAG, "inputBufferIndex < 0 " + inputBufferIndex);
                return false;
            }
        }
        mCount++;
          // Get output buffer index
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 10000);
        while (outputBufferIndex >= 0) {
            mCodec.releaseOutputBuffer(outputBufferIndex, true);
            outputBufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, 0);
        }
        Log.e(LOG_TAG, "onFrame end");
        return true;
    }

    /**
     * Find H264 frame head
     *
     * @param buffer
     * @param len
     * @return the offset of frame head, return 0 if can not find one
     */
      static int findHead(byte[] buffer, int len) {
        int i;
        for (i = HEAD_OFFSET; i < len; i++) {
            if (checkHead(buffer, i))
                break;
        }
        if (i == len)
            return 0;
        if (i == HEAD_OFFSET)
            return 0;
        return i;
    }

    /**
     * Check if is H264 frame head
     *
     * @param buffer
     * @param offset
     * @return whether the src buffer is frame head
     */
    static boolean checkHead(byte[] buffer, int offset) {
        // 00 00 00 01
        if (buffer[offset] == 0 && buffer[offset + 1] == 0
                && buffer[offset + 2] == 0 && buffer[3] == 1)
            return true;
        // 00 00 01
        if (buffer[offset] == 0 && buffer[offset + 1] == 0
                && buffer[offset + 2] == 1)
            return true;
        return false;
    }
     Runnable readFile = new Runnable() {

        @Override
        public void run() {
            boolean readFlag = true;
            int h264Count = 0;
            while (!Thread.interrupted() && readFlag) {
                if (h264Count > 300) {
                    try {
                        Thread.sleep(20);
                        h264Count = 0;
                        continue;
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "Exception for: " + e.getMessage());
                    }
                }
                BufferedInputStream bis = null;
                String sdFileName = "/sdcard/" + String.format("%03d", h264Count) + ".h264";
                String assetsFileName = String.format("%03d", h264Count) + ".h264";
                Log.e(LOG_TAG, "Read from fileName: " + sdFileName);

//                FileInputStream fos = null;
                try {
                    File h264 = new File(sdFileName);
                    if (!h264.exists()) {
                        Log.e(LOG_TAG, "File isn't exist: " + sdFileName);
                        return;
                    }
                    // 读取SD卡
                    //                    fos = new FileInputStream(h264);
//                    bis = new BufferedInputStream(fos);
                    // 读取assets目录
                    InputStream  is = getResources().getAssets().open(assetsFileName);
                    bis = new BufferedInputStream(is);

                    int fileLen = (int) h264.length();
                    byte[] buffer = new byte[fileLen];
                    try {
                        bis.read(buffer);
                    } catch (IOException e) {
                        Log.e(LOG_TAG, "error: read from file " + e.getMessage());
                    }
                    onFrame(buffer, fileLen);
                } catch (Exception e) {
                    if (BuildConfig.DEBUG) {
                        Log.e(LOG_TAG, " Error: " + e.getMessage());
                    }
                }
                h264Count++;

            }
            Log.d(LOG_TAG, "end loop");
        }
    };
}

代码逻辑分析:这边主要是通过一个线程thread去读取工程目录assets目录下的h264文件,然后将读取的内容和要进行界面显示的SurfaceView传入到MediaCodec中即可实现播放,

xml界面文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />

    <Button
        android:id="@+id/btn_readfile"
        android:layout_width="100dp"
        android:layout_height="80dp"
        android:layout_centerInParent="true"
        android:text="Start"
        android:textColor="@android:color/white"
        android:textSize="30dp" />

</RelativeLayout>

源码点击下载

猜你喜欢

转载自blog.csdn.net/u013171283/article/details/78961679