音視頻編解碼:視頻編解碼基礎(chǔ)2-FFmpeg解碼實(shí)踐

音視頻編解碼:視頻編解碼基礎(chǔ)1-FFmpeg結(jié)構(gòu)與API摘要
音視頻編解碼:視頻編解碼基礎(chǔ)2-FFmpeg解碼實(shí)踐
音視頻編解碼:視頻編解碼基礎(chǔ)篇3-FFmpeg轉(zhuǎn)碼實(shí)踐
音視頻編解碼:視頻編解碼基礎(chǔ)篇4-FFmpeg解碼播放

一.視頻解碼

本地視頻和網(wǎng)路視頻解碼,由于本地視頻解碼某種意義上相當(dāng)于網(wǎng)路視頻解碼的子集這里所以直接上網(wǎng)路解碼.

解碼初始化

avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)這里的options也就是代碼中的參數(shù)四,可以根據(jù)具體情況進(jìn)行具體分析配置
1.畫(huà)質(zhì)優(yōu)化
原生的ffmpeg參數(shù)在對(duì)1920x1080的RTSP流進(jìn)行播放時(shí),花屏現(xiàn)象很嚴(yán)重长窄,根據(jù)網(wǎng)上查的資料而钞,可以通過(guò)增大“buffer_size”參數(shù)來(lái)提高畫(huà)質(zhì)未桥,減少花屏現(xiàn)象
如:av_dict_set(&options, "buffer_size", "1080000", 0);
2.RTSP連接不上導(dǎo)致卡死的問(wèn)題
原生的ffmpeg參數(shù)在打開(kāi)RTSP流時(shí)旭旭,若連接不上,會(huì)出現(xiàn)卡死在打開(kāi)函數(shù)的情況,在有些情況下這是很不好的,可以通過(guò)設(shè)置超時(shí)來(lái)改變卡死的情況
如設(shè)置20s超時(shí):av_dict_set(&options, "stimeout", "20000000", 0); //設(shè)置超時(shí)斷開(kāi)連接時(shí)間
3.其他
可以設(shè)置的參數(shù)還有很多窍蓝,如可以設(shè)置連接為TCP,設(shè)置最大延時(shí)等等
av_dict_set(&options, "max_delay", "300000", 0);
av_dict_set(&options, "rtsp_transport", "tcp", 0); //以u(píng)dp方式打開(kāi)繁成,如果以tcp方式打開(kāi)將udp替換為tcp

//第一步:組冊(cè)組件
    av_register_all();
    avformat_network_init();
//第二步:打開(kāi)封裝格式->打開(kāi)文件
    //參數(shù)一:封裝格式上下文
    //作用:保存整個(gè)視頻信息(解碼器、編碼器等等...)
    //信息:碼率淑玫、幀率等...
    AVFormatContext* avformat_context = avformat_alloc_context();
    //參數(shù)二:視頻路徑
    const char *url = [urlString cStringUsingEncoding:NSUTF8StringEncoding];
    //參數(shù)三:指定輸入的格式
    //參數(shù)四:設(shè)置默認(rèn)參數(shù) AVDictionary* options = NULL;
    int avformat_open_input_result = avformat_open_input(&avformat_context, url, NULL, NULL);
if (avformat_open_input_result != 0){
        NSLog("打開(kāi)文件失敗");
 if (formatCtx){
                釋放上下文
                avformat_free_context(avformat_context);
            }
        return;
    }
//第三步:查找視頻流->拿到視頻信息
    //參數(shù)一:封裝格式上下文
    //參數(shù)二:指定默認(rèn)配置
    int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context, NULL);
    if (avformat_find_stream_info_result < 0){
        NSLog(@"查找失敗");
        NSLog(@"avformat_find_stream_info error, %s", av_err2str(avformat_find_stream_info_result));
        return;
    }
 //打印流信息
    /*Input #0, rtsp, from 'rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov':
     Metadata:
     title           : BigBuckBunny_175k.mov
     Duration: 00:09:56.46, start: 0.000000, bitrate: N/A
     Stream #0:0: Audio: aac (LC), 48000 Hz, stereo, fltp
     Stream #0:1: Video: h264 (Constrained Baseline), yuv420p(progressive), 240x160, 24 fps, 24 tbr, 90k tbn, 48 tbc*/
    av_dump_format(avformat_context, 0, url, false);
//第四步:查找視頻解碼器
    //1巾腕、查找視頻流索引位置
    int av_stream_index = -1;
    for (int i = 0; i < avformat_context->nb_streams; ++i) {
        //判斷流類型:視頻流、音頻流絮蒿、字母流等等...
        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            av_stream_index = i;
            break;
        }
    }
    
    //2尊搬、根據(jù)視頻流索引,獲取解碼器上下文
    AVCodecContext *avcodec_context = avformat_context->streams[av_stream_index]->codec;
    
    //3土涝、根據(jù)解碼器上下文佛寿,獲得解碼器ID,然后查找解碼器
    AVCodec *avcodec = avcodec_find_decoder(avcodec_context->codec_id);
    
    //第五步:打開(kāi)解碼器
    int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec, NULL);
    if (avcodec_open2_result != 0){
        NSLog(@"打開(kāi)解碼器失敗");
        return;
    }
    
    //測(cè)試一下
    //打印信息
    NSLog(@"解碼器名稱:%s", avcodec->name);
    
    //第六步:讀取視頻壓縮數(shù)據(jù)->循環(huán)讀取
    //1但壮、分析av_read_frame參數(shù)
    //參數(shù)一:封裝格式上下文
    //參數(shù)二:一幀壓縮數(shù)據(jù) = 一張圖片
    //av_read_frame()
    //結(jié)構(gòu)體大小計(jì)算:字節(jié)對(duì)齊原則
    AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    
    //3.2 解碼一幀視頻壓縮數(shù)據(jù)->進(jìn)行解碼(作用:用于解碼操作)
    //開(kāi)辟一塊內(nèi)存空間
    AVFrame* avframe_in = av_frame_alloc();
    int decode_result = 0;

    //4冀泻、注意:在這里我們不能夠保證解碼出來(lái)的一幀視頻像素?cái)?shù)據(jù)格式是yuv格式
    //參數(shù)一:源文件->原始視頻像素?cái)?shù)據(jù)格式寬
    //參數(shù)二:源文件->原始視頻像素?cái)?shù)據(jù)格式高
    //參數(shù)三:源文件->原始視頻像素?cái)?shù)據(jù)格式類型
    //參數(shù)四:目標(biāo)文件->目標(biāo)視頻像素?cái)?shù)據(jù)格式寬
    //參數(shù)五:目標(biāo)文件->目標(biāo)視頻像素?cái)?shù)據(jù)格式高
    //參數(shù)六:目標(biāo)文件->目標(biāo)視頻像素?cái)?shù)據(jù)格式類型
    struct SwsContext *swscontext = sws_getContext(avcodec_context->width,
                                            avcodec_context->height,
                                            avcodec_context->pix_fmt,
                                            avcodec_context->width,
                                            avcodec_context->height,
                                            AV_PIX_FMT_YUV420P,
                                            SWS_BICUBIC,
                                            NULL,
                                            NULL,
                                            NULL);
    
    //創(chuàng)建一個(gè)yuv420視頻像素?cái)?shù)據(jù)格式緩沖區(qū)(一幀數(shù)據(jù))
    AVFrame* avframe_yuv420p = av_frame_alloc();
    //給緩沖區(qū)設(shè)置類型->yuv420類型
    //得到Y(jié)UV420P緩沖區(qū)大小
    //參數(shù)一:視頻像素?cái)?shù)據(jù)格式類型->YUV420P格式
    //參數(shù)二:一幀視頻像素?cái)?shù)據(jù)寬 = 視頻寬
    //參數(shù)三:一幀視頻像素?cái)?shù)據(jù)高 = 視頻高
    //參數(shù)四:字節(jié)對(duì)齊方式->默認(rèn)是1
    int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
                                               avcodec_context->width,
                                               avcodec_context->height,
                                               1);
    
    //開(kāi)辟一塊內(nèi)存空間
    uint8_t *out_buffer = (uint8_t *)av_malloc(buffer_size);
    //向avframe_yuv420p->填充數(shù)據(jù)
    //參數(shù)一:目標(biāo)->填充數(shù)據(jù)(avframe_yuv420p)
    //參數(shù)二:目標(biāo)->每一行大小
    //參數(shù)三:原始數(shù)據(jù)
    //參數(shù)四:目標(biāo)->格式類型
    //參數(shù)五:寬
    //參數(shù)六:高
    //參數(shù)七:字節(jié)對(duì)齊方式
    av_image_fill_arrays(avframe_yuv420p->data,
                         avframe_yuv420p->linesize,
                         out_buffer,
                         AV_PIX_FMT_YUV420P,
                         avcodec_context->width,
                         avcodec_context->height,
                         1);
    
    int y_size, u_size, v_size;
    
    
    //5.2 將yuv420p數(shù)據(jù)寫(xiě)入.yuv文件中
    //打開(kāi)寫(xiě)入文件
    const char *outfile = [outFilePath UTF8String];
    FILE* file_yuv420p = fopen(outfile, "wb+");
    if (file_yuv420p == NULL){
        NSLog(@"輸出文件打開(kāi)失敗");
        return;
    }
    
    int currentIndex = 0;
    
    while (av_read_frame(avformat_context, packet) >= 0){
        // >=:讀取到了
        // <0:讀取錯(cuò)誤或者讀取完畢
        // 2、是否是我們的視頻流
        if (packet->stream_index == av_stream_index){
            //第七步:解碼
            //學(xué)習(xí)一下C基礎(chǔ)蜡饵,結(jié)構(gòu)體
            //3弹渔、解碼一幀壓縮數(shù)據(jù)->得到視頻像素?cái)?shù)據(jù)->yuv格式
            //采用新的API
            //3.1 發(fā)送一幀視頻壓縮數(shù)據(jù)
            avcodec_send_packet(avcodec_context, packet);
            //3.2 解碼一幀視頻壓縮數(shù)據(jù)->進(jìn)行解碼(作用:用于解碼操作)
            decode_result = avcodec_receive_frame(avcodec_context, avframe_in);
            if (decode_result == 0){
                //解碼成功
                //4、注意:在這里我們不能夠保證解碼出來(lái)的一幀視頻像素?cái)?shù)據(jù)格式是yuv格式
                //視頻像素?cái)?shù)據(jù)格式很多種類型: yuv420P溯祸、yuv422p肢专、yuv444p等等...
                //保證:我的解碼后的視頻像素?cái)?shù)據(jù)格式統(tǒng)一為yuv420P->通用的格式
                //進(jìn)行類型轉(zhuǎn)換: 將解碼出來(lái)的視頻像素點(diǎn)數(shù)據(jù)格式->統(tǒng)一轉(zhuǎn)類型為yuv420P
                //sws_scale作用:進(jìn)行類型轉(zhuǎn)換的yuv420P,<~>RGB
                //參數(shù)一:視頻像素?cái)?shù)據(jù)格式上下文
                //參數(shù)二:原來(lái)的視頻像素?cái)?shù)據(jù)格式->輸入數(shù)據(jù)
                //參數(shù)三:原來(lái)的視頻像素?cái)?shù)據(jù)格式->輸入畫(huà)面每一行大小
                //參數(shù)四:原來(lái)的視頻像素?cái)?shù)據(jù)格式->輸入畫(huà)面每一行開(kāi)始位置(填寫(xiě):0->表示從原點(diǎn)開(kāi)始讀取)
                //參數(shù)五:原來(lái)的視頻像素?cái)?shù)據(jù)格式->輸入數(shù)據(jù)行數(shù)
                //參數(shù)六:轉(zhuǎn)換類型后視頻像素?cái)?shù)據(jù)格式->輸出數(shù)據(jù)
                //參數(shù)七:轉(zhuǎn)換類型后視頻像素?cái)?shù)據(jù)格式->輸出畫(huà)面每一行大小
                sws_scale(swscontext,
                          (const uint8_t *const *)avframe_in->data,
                          avframe_in->linesize,
                          0,
                          avcodec_context->height,
                          avframe_yuv420p->data,
                          avframe_yuv420p->linesize);
                
                //1:直接顯示視頻上面去
                //2:寫(xiě)入yuv文件格式
                //5、將yuv420p數(shù)據(jù)寫(xiě)入.yuv文件中
                //5.1 計(jì)算YUV大小
                //分析一下原理?
                //Y表示:亮度
                //UV表示:色度
                //YUV420P格式:Y結(jié)構(gòu)表示一個(gè)像素(一個(gè)像素對(duì)應(yīng)一個(gè)Y)
                  4個(gè)像素點(diǎn)對(duì)應(yīng)一個(gè)(U和V: 4Y = U = V)
                y_size = avcodec_context->width * avcodec_context->height;
                u_size = y_size / 4;
                v_size = y_size / 4;
                //5.2 寫(xiě)入.yuv文件
                //Y數(shù)據(jù)
                fwrite(avframe_yuv420p->data[0], 1, y_size, file_yuv420p);
                //U數(shù)據(jù)
                fwrite(avframe_yuv420p->data[1], 1, u_size, file_yuv420p);
                //V數(shù)據(jù)
                fwrite(avframe_yuv420p->data[2], 1, v_size, file_yuv420p);
                
                currentIndex++;
                NSLog(@"解碼第%d幀", currentIndex);
            }
        }
    }
    
    釋放內(nèi)存資源焦辅,關(guān)閉解碼器
    av_packet_free(&packet);
    fclose(file_yuv420p);
    av_frame_free(&avframe_in);
    av_frame_free(&avframe_yuv420p);
    free(out_buffer);
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);

二.小結(jié)以上只是解碼過(guò)程的基本流程和配置,后續(xù)還會(huì)對(duì)細(xì)節(jié)做處理,增加播放和音視頻同步等等操作.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末博杖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子筷登,更是在濱河造成了極大的恐慌剃根,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仆抵,死亡現(xiàn)場(chǎng)離奇詭異跟继,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)镣丑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門舔糖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人莺匠,你說(shuō)我怎么就攤上這事金吗。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵摇庙,是天一觀的道長(zhǎng)旱物。 經(jīng)常有香客問(wèn)我,道長(zhǎng)卫袒,這世上最難降的妖魔是什么宵呛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮夕凝,結(jié)果婚禮上宝穗,老公的妹妹穿的比我還像新娘。我一直安慰自己码秉,他們只是感情好逮矛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著转砖,像睡著了一般须鼎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上府蔗,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天晋控,我揣著相機(jī)與錄音,去河邊找鬼礁竞。 笑死糖荒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的模捂。 我是一名探鬼主播捶朵,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼狂男!你這毒婦竟也來(lái)了综看?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岖食,失蹤者是張志新(化名)和其女友劉穎红碑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體泡垃,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡析珊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔑穴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忠寻。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖存和,靈堂內(nèi)的尸體忽然破棺而出奕剃,到底是詐尸還是另有隱情衷旅,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布纵朋,位于F島的核電站柿顶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏操软。R本人自食惡果不足惜嘁锯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望聂薪。 院中可真熱鬧猪钮,春花似錦、人聲如沸胆建。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)笆载。三九已至,卻和暖如春涯呻,著一層夾襖步出監(jiān)牢的瞬間凉驻,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工复罐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涝登,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓效诅,卻偏偏與公主長(zhǎng)得像胀滚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乱投,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354