part04_ffmpeg+native_window實(shí)現(xiàn)萬能視頻播放器播放本地視頻

一磺陡、java層創(chuàng)建一個(gè)SuifaceView,獲取其holder對(duì)象舒萎,定義一個(gè)native函數(shù)將該holder傳遞給jni層進(jìn)行繪制曹洽。

    public class VideoView  extends SurfaceView {
        static{
            System.loadLibrary("avcodec-56");
            System.loadLibrary("avdevice-56");
            System.loadLibrary("avfilter-5");
            System.loadLibrary("avformat-56");
            System.loadLibrary("avutil-54");
            System.loadLibrary("postproc-53");
            System.loadLibrary("swresample-1");
            System.loadLibrary("swscale-3");
            System.loadLibrary("native-lib");
        }
        public VideoView(Context context) {
            super(context);
        }
    
        public VideoView(Context context, AttributeSet attrs) {
          super(context, attrs);
            init();
    
        }
    
        private void init() {
            //獲取holder對(duì)象
            SurfaceHolder holder = getHolder();
            holder.setFormat(PixelFormat.RGBA_8888);//設(shè)置格式
        }
    
        public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public void player(final String input) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //將holder傳遞給底層進(jìn)行繪制
                    render(input, VideoView.this.getHolder().getSurface());
                }
            }).start();
        }
    
        public native void render(String input, Surface surface);
    }

二鳍置、使用ffmpeg解碼視頻并使用native繪制在SurfaceView的holder對(duì)象上

    extern "C" {
    //編碼
    #include "libavcodec/avcodec.h"
    //封裝格式處理
    #include "libavformat/avformat.h"
    //像素處理
    #include "libswscale/swscale.h"
    //native_window_jni 在ndk 的libandroid.so庫中,需要在CMakeLists.txt中引入android庫
    #include <android/native_window_jni.h>
    #include <unistd.h>//sleep用的頭文件
    }
    /**
        *將任意格式的視頻在手機(jī)上進(jìn)行播放送淆,使用native進(jìn)行繪制
        * env:虛擬機(jī)指針
        * inputStr:視頻文件路徑
        * surface: 從java層傳遞過來的SurfaceView的serface對(duì)象 
        */
    void ffmpegVideoPlayer(JNIEnv *env, char *inputStr, jobject surface) {
        // 1.注冊(cè)各大組件税产,執(zhí)行ffmgpe都必須調(diào)用此函數(shù)
        av_register_all();
        //2.得到一個(gè)ffmpeg的上下文(上下文里面封裝了視頻的比特率,分辨率等等信息...非常重要)
        AVFormatContext *pContext = avformat_alloc_context();
        //3.打開一個(gè)視頻
        if (avformat_open_input(&pContext, inputStr, NULL, NULL) < 0) {
            LOGE("打開失敗");
            return;
        }
        //4.獲取視頻信息(將視頻信息封裝到上下文中)
        if (avformat_find_stream_info(pContext, NULL) < 0) {
            LOGE("獲取信息失敗");
            return;
        }
        //5.用來記住視頻流的索引
        int vedio_stream_idx = -1;
        //從上下文中尋找找到視頻流
        for (int i = 0; i < pContext->nb_streams; ++i) {
            LOGE("循環(huán)  %d", i);
            //codec:每一個(gè)流 對(duì)應(yīng)的解碼上下文
            //codec_type:流的類型
            if (pContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                //如果找到的流類型 == AVMEDIA_TYPE_VIDEO 即視頻流坊夫,就將其索引保存下來
                vedio_stream_idx = i;
            }
        }
    
        //獲取到解碼器上下文
        AVCodecContext *pCodecCtx = pContext->streams[vedio_stream_idx]->codec;
        //獲取解碼器(加密視頻就是在此處無法獲茸┑凇)
        AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);
        LOGE("獲取視頻編碼 %p", pCodex);
    
        //6.打開解碼器撤卢。 (ffempg版本升級(jí)名字叫做avcodec_open2)
        if (avcodec_open2(pCodecCtx, pCodex, NULL) < 0) {
            LOGE("解碼失敗");
            return;
        }
        //----------------------解碼前準(zhǔn)備--------------------------------------
        //準(zhǔn)備開始解碼時(shí)需要一個(gè)AVPacket存儲(chǔ)數(shù)據(jù)(通過av_malloc分配內(nèi)存)
        AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
        av_init_packet(packet);//初始化結(jié)構(gòu)體
    
        //解封裝需要AVFrame
        AVFrame *frame = av_frame_alloc();
        //聲明一個(gè)rgb_Frame的緩沖區(qū)
        AVFrame *rgb_Frame = av_frame_alloc();
        //rgb_Frame  的緩沖區(qū) 初始化
        uint8_t *out_buffer = (uint8_t *) av_malloc(
                avpicture_get_size(AV_PIX_FMT_RGBA, pCodecCtx->width, pCodecCtx->height));
        //給緩沖區(qū)進(jìn)行替換
        int re = avpicture_fill((AVPicture *) rgb_Frame, out_buffer, AV_PIX_FMT_RGBA, pCodecCtx->width,
                                pCodecCtx->height);
        LOGE("寬 %d  高 %d", pCodecCtx->width, pCodecCtx->height);
    
    
        //格式轉(zhuǎn)碼需要的轉(zhuǎn)換上下文(根據(jù)封裝格式的寬高和編碼格式环凿,以及需要得到的格式的寬高)
        //pCodecCtx->pix_fmt 封裝格式文件的上下文
        //AV_PIX_FMT_RGBA : 目標(biāo)格式 需要跟SurfaceView設(shè)定的格式相同
        //SWS_BICUBIC :清晰度稍微低一點(diǎn)的算法(轉(zhuǎn)換算法,前面的算法清晰度高效率低放吩,下面的算法清晰度低效率高) 
        //NULL,NULL,NULL : 過濾器等
        SwsContext *swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                                pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGBA,
                                                SWS_BICUBIC, NULL, NULL, NULL
        );
        int frameCount = 0;
    
        //獲取nativeWindow對(duì)象,準(zhǔn)備進(jìn)行繪制
        ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
        ANativeWindow_Buffer outBuffer;//申明一塊緩沖區(qū) 用于繪制
    
        //------------------------一楨一幀開始解碼--------------------
        int length = 0;
        int got_frame;
        while (av_read_frame(pContext, packet) >= 0) {//開始讀每一幀的數(shù)據(jù)
            if (packet->stream_index == vedio_stream_idx) {//如果這是一個(gè)視頻流
                //7.解封裝(將packet解壓給frame智听,即:拿到了視頻數(shù)據(jù)frame)
                length = avcodec_decode_video2(pCodecCtx, frame, &got_frame, packet);//解封裝函數(shù)
                LOGE(" 獲得長(zhǎng)度   %d 解碼%d  ", length, frameCount++);
                if (got_frame > 0) {
    
                    //8.準(zhǔn)備繪制
                    //配置繪制信息 寬高 格式(這個(gè)繪制的寬高直接決定了視頻在屏幕上顯示的情況,這樣會(huì)平鋪整個(gè)屏幕渡紫,可以根據(jù)特定的屏幕分辨率和視頻寬高進(jìn)行匹配)
                    ANativeWindow_setBuffersGeometry(nativeWindow, pCodecCtx->width, pCodecCtx->height,
                                                     WINDOW_FORMAT_RGBA_8888);
                    ANativeWindow_lock(nativeWindow, &outBuffer, NULL);//鎖定畫布(outBuffer中將會(huì)得到數(shù)據(jù))
                    //9.轉(zhuǎn)碼(轉(zhuǎn)碼上下文到推,原數(shù)據(jù),一行數(shù)據(jù)惕澎,開始位置莉测,yuv的緩沖數(shù)組,yuv一行的數(shù)據(jù))
                    sws_scale(swsContext, (const uint8_t *const *) frame->data, frame->linesize, 0,
                              frame->height, rgb_Frame->data,
                              rgb_Frame->linesize
                    );
                    //10.繪制
                    uint8_t *dst = (uint8_t *) outBuffer.bits; //實(shí)際的位數(shù)
                    int destStride = outBuffer.stride * 4; //拿到一行有多少個(gè)字節(jié) RGBA
                    uint8_t *src = (uint8_t *) rgb_Frame->data[0];//像素?cái)?shù)據(jù)的首地址
                    int srcStride = rgb_Frame->linesize[0]; //實(shí)際內(nèi)存一行的數(shù)量
                    for (int i = 0; i < pCodecCtx->height; ++i) {
                        //將rgb_Frame緩沖區(qū)里面的數(shù)據(jù)一行一行copy到window的緩沖區(qū)里面
                        //copy到window緩沖區(qū)的時(shí)候進(jìn)行一些偏移設(shè)置可以將視頻播放居中
                        memcpy(dst + i * destStride, src + i * srcStride, srcStride);
                    }
    
                    ANativeWindow_unlockAndPost(nativeWindow);//解鎖畫布
                    usleep(1000 * 16);//可以根據(jù)幀率休眠16ms
    
                }
            }
            av_free_packet(packet);//釋放
        }
        ANativeWindow_release(nativeWindow);//釋放window
        av_frame_free(&frame);
        av_frame_free(&rgb_Frame);
        avcodec_close(pCodecCtx);
        avformat_free_context(pContext);
    
        free(inputStr);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唧喉,一起剝皮案震驚了整個(gè)濱河市捣卤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌八孝,老刑警劉巖董朝,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異干跛,居然都是意外死亡子姜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門楼入,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哥捕,“玉大人牧抽,你說我怎么就攤上這事∫W” “怎么了阎姥?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鸽捻。 經(jīng)常有香客問我呼巴,道長(zhǎng),這世上最難降的妖魔是什么御蒲? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任衣赶,我火速辦了婚禮,結(jié)果婚禮上厚满,老公的妹妹穿的比我還像新娘府瞄。我一直安慰自己,他們只是感情好碘箍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布遵馆。 她就那樣靜靜地躺著,像睡著了一般丰榴。 火紅的嫁衣襯著肌膚如雪货邓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天四濒,我揣著相機(jī)與錄音换况,去河邊找鬼。 笑死盗蟆,一個(gè)胖子當(dāng)著我的面吹牛戈二,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播喳资,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼觉吭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了仆邓?” 一聲冷哼從身側(cè)響起鲜滩,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宏赘,沒想到半個(gè)月后绒北,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡察署,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年闷游,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脐往,死狀恐怖休吠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情业簿,我是刑警寧澤瘤礁,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站梅尤,受9級(jí)特大地震影響柜思,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巷燥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一赡盘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缰揪,春花似錦陨享、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至艳狐,卻和暖如春定硝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背僵驰。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國打工喷斋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唁毒,地道東北人蒜茴。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像浆西,于是被迫代替她去往敵國和親粉私。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,117評(píng)論 25 707
  • 在Android系統(tǒng)中近零,有一種特殊的視圖诺核,稱為SurfaceView,它擁有獨(dú)立的繪圖表面久信,即它不與其宿主窗口共享...
    一個(gè)不掉頭發(fā)的開發(fā)閱讀 11,317評(píng)論 12 74
  • 孩子一歲了窖杀,越來越活潑可愛,每天的表現(xiàn)也不相同裙士,他與你熟絡(luò)的時(shí)候入客,會(huì)不自覺的把手放在你的臉 上摸一摸,慢慢的就是用...
    舒舒舒小妮閱讀 167評(píng)論 0 0
  • 一、獲取頁面的表格有多少行 List rows =driver.findElements(By.xpath("//...
    別吭氣閱讀 458評(píng)論 0 0
  • 每個(gè)人的生活方式各不相同桌硫,今天所有的狀態(tài)都是曾經(jīng)的每個(gè)選擇造就的夭咬。不去回首過往,不去后悔曾經(jīng)铆隘。唯有過的更好卓舵,才不辜...
    Luiii閱讀 449評(píng)論 0 0