FFmpeg的視頻編解碼流程:
第一步:組冊(cè)組件
av_register_all()
例如:編碼器、解碼器等等…
第二步:打開封裝格式->打開文件
例如:.mp4、.mov、.wmv文件等等...
avformat_open_input();
第三步:查找視頻流
如果是視頻解碼,那么查找視頻流,如果是音頻解碼,那么就查找音頻流
avformat_find_stream_info();
第四步:查找視頻解碼器
1、查找視頻流索引位置
2、根據(jù)視頻流索引,獲取解碼器上下文
3、根據(jù)解碼器上下文,獲得解碼器ID,然后查找解碼器
第五步:打開解碼器
avcodec_open2();
第六步:讀取視頻壓縮數(shù)據(jù)->循環(huán)讀取
沒讀取一幀數(shù)據(jù),立馬解碼一幀數(shù)據(jù)
第七步:視頻解碼->播放視頻->得到視頻像素?cái)?shù)據(jù)
第八步:關(guān)閉解碼器->解碼完成
上代碼:
//第一步:組冊(cè)組件
? ? av_register_all();
? ? //第二步:打開封裝格式->打開文件
? ? //參數(shù)一:封裝格式上下文
? ? //作用:保存整個(gè)視頻信息(解碼器、編碼器等等...)
? ? //信息:碼率、幀率等...
? ? AVFormatContext* avformat_context = avformat_alloc_context();
? ? //參數(shù)二:視頻路徑
? ? const char *url = [jinFilePath UTF8String]
? ? //參數(shù)三:指定輸入的格式
? ? //參數(shù)四:設(shè)置默認(rèn)參數(shù)
? ? int avformat_open_input_result = avformat_open_input(&avformat_context, url,NULL, NULL);
? ? if (avformat_open_input_result !=0){
? ? ? ? NSLog("打開文件失敗");
? ? ? ? //不同的平臺(tái)替換不同平臺(tái)log日志
? ? ? ? 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("?查找失敗");
? ? ? ? return;
? ? }
? ? //第四步:查找視頻解碼器
? ? //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);
? ? //第五步:打開解碼器
? ? int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec,NULL);
? ? if (avcodec_open2_result !=0){
? ? ? ? NSLog("打開解碼器失敗");
? ? ? ? return;
? ? }
? ? //第六步:讀取視頻壓縮數(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)行解碼(作用:用于解碼操作)
? ? //開辟一塊內(nèi)存空間
? ? AVFrame* avframe_in = av_frame_alloc();
? ? int decode_result =0;
? ? //4、注意:在這里我們不能夠保證解碼出來的一幀視頻像素?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ù)格式類型
? ? 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);
? ? //開辟一塊內(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ù)寫入.yuv文件中
? ? //打開寫入文件
? ? const char *outfile = [joutFilePath UTF8String];
? ? FILE* file_yuv420p = fopen(outfile,"wb+");
? ? if (file_yuv420p ==NULL){
? ? ? ?NSLog("輸出文件打開失敗");
? ? ? ? return;
? ? }
? ? int current_index =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、注意:在這里我們不能夠保證解碼出來的一幀視頻像素?cái)?shù)據(jù)格式是yuv格式
? ? ? ? ? ? ? ? //視頻像素?cái)?shù)據(jù)格式很多種類型: yuv420P、yuv422p、yuv444p等等...
? ? ? ? ? ? ? ? //保證:我的解碼后的視頻像素?cái)?shù)據(jù)格式統(tǒng)一為yuv420P->通用的格式
? ? ? ? ? ? ? ? //進(jìn)行類型轉(zhuǎn)換: 將解碼出來的視頻像素點(diǎn)數(shù)據(jù)格式->統(tǒng)一轉(zhuǎn)類型為yuv420P
? ? ? ? ? ? ? ? //sws_scale作用:進(jìn)行類型轉(zhuǎn)換的
? ? ? ? ? ? ? ? //參數(shù)一:視頻像素?cái)?shù)據(jù)格式上下文
? ? ? ? ? ? ? ? //參數(shù)二:原來的視頻像素?cái)?shù)據(jù)格式->輸入數(shù)據(jù)
? ? ? ? ? ? ? ? //參數(shù)三:原來的視頻像素?cái)?shù)據(jù)格式->輸入畫面每一行大小
? ? ? ? ? ? ? ? //參數(shù)四:原來的視頻像素?cái)?shù)據(jù)格式->輸入畫面每一行開始位置(填寫:0->表示從原點(diǎn)開始讀取)
? ? ? ? ? ? ? ? //參數(shù)五:原來的視頻像素?cái)?shù)據(jù)格式->輸入數(shù)據(jù)行數(shù)
? ? ? ? ? ? ? ? //參數(shù)六:轉(zhuǎn)換類型后視頻像素?cái)?shù)據(jù)格式->輸出數(shù)據(jù)
? ? ? ? ? ? ? ? //參數(shù)七:轉(zhuǎn)換類型后視頻像素?cái)?shù)據(jù)格式->輸出畫面每一行大小
? ? ? ? ? ? ? ? sws_scale(swscontext,
? ? ? ? ? ? ? ? ? ? ? ? ? (const uint8_t *const *)avframe_in->data,
? ? ? ? ? ? ? ? ? ? ? ? ? avframe_in->linesize,
? ? ? ? ? ? ? ? ? ? ? ? ? 0,
? ? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->height,
? ? ? ? ? ? ? ? ? ? ? ? ? avframe_yuv420p->data,
? ? ? ? ? ? ? ? ? ? ? ? ? avframe_yuv420p->linesize);
? ? ? ? ? ? ? ? //方式一:直接顯示視頻上面去
? ? ? ? ? ? ? ? //方式二:寫入yuv文件格式
? ? ? ? ? ? ? ? //5、將yuv420p數(shù)據(jù)寫入.yuv文件中
? ? ? ? ? ? ? ? //5.1 計(jì)算YUV大小
? ? ? ? ? ? ? ? //分析一下原理?
? ? ? ? ? ? ? ? //Y表示:亮度
? ? ? ? ? ? ? ? //UV表示:色度
? ? ? ? ? ? ? ? //有規(guī)律
? ? ? ? ? ? ? ? //YUV420P格式規(guī)范一:Y結(jié)構(gòu)表示一個(gè)像素(一個(gè)像素對(duì)應(yīng)一個(gè)Y)
? ? ? ? ? ? ? ? //YUV420P格式規(guī)范二: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 寫入.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);
? ? ? ? ? ? ? ? current_index++;
? ? ? ? ? ? ? ? NSLog("當(dāng)前解碼第%d幀", current_index);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? //第八步:釋放內(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);