1. 程式人生 > >MediaCodec硬編碼成H264視訊流

MediaCodec硬編碼成H264視訊流

android提供了一個強大的編解碼類MediaCodec,可以實現對視訊資料的編解碼,下面講一下如何對原始視訊資料硬編碼成h264格式的流

MediaCodec提供兩種方式的輸入,一種是將資料寫入它的輸入緩衝佇列裡,一種是讓MediaCodec建立一個輸入Surface,MediaCodec會自動從這個輸入Surface中讀取資料,因為我做的是錄製螢幕的需求,所以我是使用一個Surface輸入資料給MediaCodec。MediaCodec編碼出來的頭兩幀是特殊的,分別是sps 和 pps這兩幀在解碼時要用來配置解碼器用的。下面貼出編碼器程式碼,這裡我做的是視訊實時編碼傳輸,所以編碼後的資料我使用socket傳送的,大家主要還是看看編碼部分的程式碼就好了:

package com.seewo.seewoair.coder;

/**
 * @author zhangsutao
 * @file VideoCodec.java
 * @brief 視訊編解碼器基類
 * @date 2016/8/7
 */
public interface VideoCodec {

    String MIME_TYPE = "video/avc";
    int VIDEO_FRAME_PER_SECOND = 15;
    int VIDEO_I_FRAME_INTERVAL = 5;
    int VIDEO_BITRATE = 500 * 8 * 1000;
}


/**
 * @author zhangsutao
 * @file VideoEncoder.java
 * @brief 視訊編碼器
 * @date 2016/7/29
 */
public class VideoEncoder implements VideoCodec {

    private Worker mWorker;
    private MediaProjection mMediaProjection;
    private VirtualDisplay mVirtualDisplay;
    private Client mClient;
    //寫入本地的流,在除錯的時候使用
    private DataOutputStream mOutput;
    private final boolean isDebug=true;
    private final String TAG="VideoEncoder";
    private byte[] mFrameByte;

    public VideoEncoder(MediaProjection mediaProjection,Client client) {
        mClient=client;
        mMediaProjection=mediaProjection;
        if(isDebug){
            try {
                mOutput=new  DataOutputStream(new FileOutputStream(new File("/sdcard/h264encode")));;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    protected void onSurfaceCreated(Surface surface, int mWidth, int mHeight) {
        //將螢幕資料與surface進行關聯
         mVirtualDisplay = mMediaProjection.createVirtualDisplay("-display",
                mWidth, mHeight, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                surface, null, null);

    }

    protected void onSurfaceDestroyed(Surface surface) {
        mVirtualDisplay.release();
        surface.release();
    }



    public void start() {
        if (mWorker == null) {
            mWorker = new Worker();
            mWorker.setRunning(true);
            mWorker.start();
        }
    }

    public void stop() {
        if (mWorker != null) {
            mWorker.setRunning(false);
            mWorker = null;
        }
        if(mClient!=null){
            if(!mClient.hasRelease()){
                mClient.release();
            }
        }
    }


    private  class Worker extends Thread {
        private MediaCodec.BufferInfo mBufferInfo;
        private MediaCodec mCodec;
        private volatile boolean isRunning;
        private Surface mSurface;
        private final long mTimeoutUsec;
        private int mWidth;
        private int mHeight;

        public Worker() {
            mBufferInfo = new MediaCodec.BufferInfo();
            mTimeoutUsec = 10000l;
        }

        public void setRunning(boolean running) {
            isRunning = running;
        }

        protected void onEncodedSample(MediaCodec.BufferInfo info, ByteBuffer data) {
            if(mFrameByte==null||mFrameByte.length<info.size){
                mFrameByte=new byte[info.size];
            }
            data.get(mFrameByte,0,info.size);
            boolean isSuccess1=mClient.sendInt(info.size);
            boolean isSuccess2=mClient.send(mFrameByte,0,info.size);
            Log.d(TAG,"sending success:"+isSuccess1+"  "+isSuccess2);
            if(!(isSuccess1&&isSuccess2)){
                isRunning=false;
                mClient.release();
            }
            //在debug時在本地寫一份
            if(isDebug){
                try {
                    mOutput.writeInt(info.size);
                    mOutput.write(mFrameByte,0,info.size);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        @Override
        public void run() {
            if(!prepare()){
                isRunning=false;
            }
            while (isRunning) {
                encode();
            }
            release();
        }

        void encode() {
            if (!isRunning) {
                //編碼結束,傳送結束訊號,讓surface不在提供資料
                mCodec.signalEndOfInputStream();
            }
            int status = mCodec.dequeueOutputBuffer(mBufferInfo, mTimeoutUsec);
            if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    return;
            } else
                if (status >= 0) {
                    ByteBuffer data = mCodec.getOutputBuffer(status);
                    if (data != null) {
                        final int endOfStream = mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM;
                        //傳遞編碼資料
                        if (endOfStream == 0) {
                            onEncodedSample(mBufferInfo, data);
                        }
                        // 一定要記得釋放
                        mCodec.releaseOutputBuffer(status, false);
                        if (endOfStream == MediaCodec.BUFFER_FLAG_END_OF_STREAM){
                            return;
                        }
                    }
                }
        }

        private void release() {
            onSurfaceDestroyed(mSurface);
            if(mCodec!=null){
                mCodec.stop();
                mCodec.release();
            }
            if(mOutput!=null){
                try {
                    mOutput.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        private boolean prepare() {
            // configure video output
            mWidth= SpUtils.readInt(Constans.KEY_DEVICE_WIDTH,-1);
            mHeight=SpUtils.readInt(Constans.KEY_DEVICE_HEIGHT,-1);
            if(mWidth==-1||mHeight==-1){
                return false;
            }
            mClient.connectToServer();
            //傳送寬高
            boolean isSuccess1=mClient.sendInt(mWidth);
            boolean isSuccess2=mClient.sendInt(mHeight);
            if(!(isSuccess1&&isSuccess2)){
                isRunning=false;
                mClient.release();
            }
            MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                              MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            format.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BITRATE);
            format.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAME_PER_SECOND);
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,VIDEO_I_FRAME_INTERVAL);
            format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER,40);
            try {
                mCodec = MediaCodec.createEncoderByType(MIME_TYPE);
            } catch (IOException e) {
                e.printStackTrace();
                return false;

            }
            mCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            //建立關聯的輸入surface
            mSurface = mCodec.createInputSurface();
            mCodec.start();
            onSurfaceCreated(mSurface,mWidth,mHeight);
            return true;
        }
    }
}


相關推薦

MediaCodec編碼H264視訊流

android提供了一個強大的編解碼類MediaCodec,可以實現對視訊資料的編解碼,下面講一下如何對原始視訊資料硬編碼成h264格式的流 MediaCodec提供兩種方式的輸入,一種是將資料寫入它的輸入緩衝佇列裡,一種是讓MediaCodec建立一個輸入Surface

Android使用MediaCodec解碼播放H264格式視訊檔案

前些時間,通過各種搜尋加請教了好幾個同行的朋友,在他們的指點下實現: RTSP+H264實時視訊播放播放及把實時視訊流儲存到手機SD卡中,再對儲存的H264格式檔案進行播放等基本功能。 非常感謝這些朋友的無私幫忙,在實現功能的同時,我也把他們提供的一

YUV資料流編碼H264

自己使用ffmpeg進行編碼,在網上搜索到了雷霄驊大神,他的部落格內幾乎全是關於ffmpeg的,內容很全面,不過ffmpeg更新很快,他部落格上的有些程式碼,不太適合了。 但是,他提供了更新後的程式碼,自己使用的是如下的程式碼例子,地址在最後面。 檔案中有兩個例子,我都實驗

【Android】Android Camera實時資料採集及通過MediaCodec編碼編碼資料的流程

// video device. private Camera camera; private MediaCodec vencoder; private MediaCodecInfo vmci; private MediaCodec.BufferInfo vebi; private byte[] vbuff

android編碼h264資料,並使用rtp推送資料流,實現一個簡單的直播-MediaCodec(一)

  寫在前面:我並非專業做流媒體的coder,對流媒體行業無比崇拜,只是做了幾年安卓車載ROM,對安卓AV開發算是略懂。本篇部落格是我對MediaCodec編解碼和rtp推流的一次嘗試,希望能給有需要的朋友一些細微的幫助,不喜勿噴,如果有不對的地方希望大神指正共

Android 利用MediaCodec 實現編碼 h264

本篇文章記錄一下,Android呼叫mediacodec編碼camera回掉的YUV資料為h264的方法。     由於公司需要,軟編碼(X264)由於手機效能的瓶頸,已不能滿足要求,所以決定使用硬編碼。其實硬編碼最早用過MediaRecord,但是不能直接得到h2

ios 視頻流H264編碼---分解LFLiveKit

header count enable api osc center dealloc using 默認 #import "LFHardwareVideoEncoder.h" #import <VideoToolbox/VideoToolbox.h> @int

H264視訊編碼MP4檔案

        最近需要將H264視訊編碼成MP4格式。研究了一下,一種方法是採用ffmpeg庫,可以先將H264檔案解碼,再編碼生成MP4檔案,但這種方式效率較低,10M的視訊可能需要幾秒鐘才能完成。另一種方式根據MP4檔案協議直接將H264包封裝成MP4格式,由於是直接

1小時學會:最簡單的iOS直播推流(七)h264/aac 編碼

最簡單的iOS 推流程式碼,視訊捕獲,軟編碼(faac,x264),硬編碼(aac,h264),美顏,flv編碼,rtmp協議,陸續更新程式碼解析,你想學的知識這裡都有,願意懂直播技術的同學快來看!! 前面已經介紹瞭如何從硬體裝置獲取到音視

iOS h264編碼

從這裡抄過來的:https://github.com/LevyGG/iOS-H.264-hareware-encode-and-decode/blob/master/VTDemoOniPad/H264HwEncoderImpl.h #import<Founda

Android手機的h264編碼測試

網上原始碼進行點小改動, 程式碼如下: /** * oppo r7s(android 4.4.4)測試通過, 紅米3(android 5.1.1)測試未通過 * @author Administrator */ @SuppressLint("NewApi") pub

嵌入式 視頻編碼H264)hi3518

過程 abs rbsp 狀態 hive pty xheditor dev 錯誤碼 這幾天在編寫視頻錄制模塊,所以,閑暇之余,又粗粗的整理了一下,主要是API,以備不時之用 攝像頭獲取的模擬信號通過經芯片處理(我們使用的是CX25825),將模擬信號轉成數字信號

【GPU編解碼】GPU編碼

cuda 說明 閱讀 cnblogs itl href ble con info 【GPU編解碼】GPU硬編碼 一、OpenCV中的硬編碼 OpenCV2.4.6中,已實現利用GPU進行寫視頻,編碼過程由cv::gpu::VideoWriter_GPU完成,其示例程序如

H.264編碼&解碼

firefly rk3288 h.264編解碼 開源硬件 網絡攝像頭 Firefly-RK3288擁有強大的VPU(視像處理器),能夠流暢實現720P和1080P視頻的H.264編解碼;而H.264的壓縮率更高,可以更大程度更小視頻的空間占用。 詳細看視頻演示 1. 演示介紹基於Firef

iOS平臺上音頻編碼aac

AudioConverter pcm aac 硬編碼 小程之前介紹解碼aac時,曾經使用了fadd,並且有提到,如果想編碼成aac格式,可以使用facc、fdk-aac等,但使用fdk-aac等編碼方式,都是軟編碼,在cpu的消耗上會明顯大於硬件編碼。 硬編碼的優勢是可以用硬件芯片集成的功能,

Fortify漏洞之Dynamic Code Evaluation: Code Injection(動態腳本註入)和 Password Management: Hardcoded Password(密碼編碼

mys info 用戶輸入 strong 獲取 center 連接數 密碼 new   繼續對Fortify的漏洞進行總結,本篇主要針對 Dynamic Code Evaluation: Code Injection(動態腳本註入) 和 Password Manageme

基於FFmpeg-4.0 SDK的PCM編碼AAC

1. 初始化 AVCodecContext *m_avctx; AVCodec *m_codec; /* Init ffmpeg log */ ffmpeg_log_callback pcb_log = l

PS流解複用H264和音訊流(ES提取)

  技術在於交流、溝通,轉載請註明出處並保持作品的完整性。 原文:https://blog.csdn.net/hiwubihe/article/details/80759142   [本系列相關文章] H264和音訊流打包成PS流 (MPEG2-PS

編碼與軟編碼

硬編碼:就是將資料直接寫入到程式碼中進行編譯開發,比如在沒有mybatits前,將sql語句寫入到jdbc程式碼裡,在比如純jsp開發的過程中,將前端的html程式碼與java程式碼耦合,這都是應編碼,如果要發生更改的問題,就需要更改原始碼,如果是C/S開發,就直接一位這,客戶端的軟體需

使用MediaCodec解碼h.265視訊及音訊進行播放

h.265這個視訊是很多播放器不支援的,就算是bilibili開源的ijkplayer也不能直接播放,需要自己去重新編譯 才可以支援。 這裡通過這個demo來演示一下如何硬解碼視訊,播放h.265視訊,其實編碼的視訊同樣道理。 視訊的播放主要在surfaceView中顯示,而解碼