使用FFMPEG——4.2.2版本实现提取视频编码解码文件,ffmpeg基础学习。
FFMPEG基礎(chǔ)學(xué)習(xí)
視頻解碼,并且輸出到文件。
我通過(guò)雷霄驊的博客學(xué)習(xí)FFMPEG,在學(xué)習(xí)過(guò)程中發(fā)現(xiàn)“雷神”的代碼由于版本的問(wèn)題,很多代碼已經(jīng)無(wú)法在FFMPEG——4.2.2版本中使用,而在網(wǎng)上也有很多教程是基于FFMPEG——3.x.x版本的,因此,特別寫(xiě)了這篇文章,解決學(xué)習(xí)“雷神”博客代碼在4.x.x上編譯無(wú)法通過(guò)的問(wèn)題。本篇文章最下面的代碼,配置好環(huán)境之后,直接復(fù)制,然后修改輸入視頻文件的路徑,就可以運(yùn)行。
特別是在4.x.x版本中av_register_all();這個(gè)函數(shù)完全不需要。
本程序的輸入文件只要是編碼數(shù)據(jù)是H264;AAC,解碼數(shù)據(jù)是YUV420P;PCM格式的都可以正常運(yùn)行。
本程序輸出的文件格式是:H264;YUV420P;AAC;PCM。
比較大的改動(dòng)舉例:
1、關(guān)于AVFrame結(jié)構(gòu)體在轉(zhuǎn)碼過(guò)程中的初始化:
雷霄驊的例子代碼如下:
pFrameYUV=av_frame_alloc();out_buffer=(uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);這些在新版本無(wú)法編譯,很多聲明已經(jīng)否決。
我修改后的初始化代碼如下:
AVFrame* p_z_p_deco_AVFrame = av_frame_alloc();p_z_p_deco_AVFrame->width = p_input_file_v_AVCodecContext->width;p_z_p_deco_AVFrame->height = p_input_file_v_AVCodecContext->height;p_z_p_deco_AVFrame->format = AV_PIX_FMT_YUV420P;av_frame_get_buffer(p_z_p_deco_AVFrame, 4);以下是ffmpeg4.2.2版本中對(duì)于AVFrame結(jié)構(gòu)體的說(shuō)明。
/*** Allocate an AVFrame and set its fields to default values. The resulting* struct must be freed using av_frame_free().** @return An AVFrame filled with default values or NULL on failure.** @note this only allocates the AVFrame itself, not the data buffers. Those* must be allocated through other means, e.g. with av_frame_get_buffer() or* manually.*/ AVFrame *av_frame_alloc(void);2、關(guān)于編碼和解碼
雷霄驊的例子使用的函數(shù)如下:
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,int *got_picture_ptr,const AVPacket *avpkt);FFMPEG——4.2.2版本用的解碼的函數(shù)是:
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);FFMPEG——4.2.2版本用的編碼函數(shù)是:
int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame); int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);3、關(guān)于在解碼過(guò)程中AVPacket的內(nèi)存釋放問(wèn)題
使用如下函數(shù)讀取一幀數(shù)據(jù)以后,*pkt指向的內(nèi)存空間在讀取的過(guò)程中不能直接釋放,需要使用void av_packet_unref(AVPacket *pkt)來(lái)減少引用,在左后所有數(shù)據(jù)讀完之后,才用void av_packet_free(AVPacket **pkt)函數(shù)釋放掉空間。
int av_read_frame(AVFormatContext *s, AVPacket *pkt);其它的有關(guān)版本問(wèn)題就不一一列舉,最后推薦初學(xué)者去看看《基于FFmpeg+SDL的視頻播放器的制作——雷霄驊》這些視頻,有利于學(xué)習(xí)FFMPEG。本段代碼是基于視頻中前四小節(jié)內(nèi)容的更新。
感謝雷霄驊。
/** 解碼實(shí)例時(shí)間:2020年4月ffmpeg版本:4.2.2 vs版本:Microsoft Visual Studio Enterprise 2019 開(kāi)發(fā)語(yǔ)言:C++特別聲明:本程序是我通過(guò)學(xué)習(xí)雷霄驊 Lei Xiaohua的博客而編寫(xiě)的,因此,在此向雷霄驊表示敬意。雷霄驊的博客地址:https://blog.csdn.net/leixiaohua1020 所需知識(shí)背景:“視音頻數(shù)據(jù)處理入門(mén):RGB、YUV像素?cái)?shù)據(jù)處理”,鏈接地址:https://blog.csdn.net/leixiaohua1020/article/details/50534150“視音頻數(shù)據(jù)處理入門(mén):PCM音頻采樣數(shù)據(jù)處理”,鏈接地址:https://blog.csdn.net/leixiaohua1020/article/details/50534316“視音頻數(shù)據(jù)處理入門(mén):H.264視頻碼流解析”,鏈接地址:https://blog.csdn.net/leixiaohua1020/article/details/50534369“視音頻數(shù)據(jù)處理入門(mén):AAC音頻碼流解析”,鏈接地址:https://blog.csdn.net/leixiaohua1020/article/details/50535042 說(shuō)明:本程序?qū)崿F(xiàn)了解碼本地視頻,并且分別輸出解碼的音視頻文件到文件。通過(guò)本程序可以了解整個(gè)ffmpeg最簡(jiǎn)單的解碼流程。程序?yàn)榱朔奖汩喿x,沒(méi)有采用面向?qū)ο缶幊獭1境绦蚰芡瓿衫紫鲵懙摹痘贔Fmpeg+SDL的視頻播放器的制作——雷霄驊》視頻前四小節(jié)的內(nèi)容。 */ extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" } #include <iostream> #include <fstream> using namespace std;int main() {//本地輸入的文件路徑const char* p_intputFilePath = "sintel.ts";if(p_intputFilePath == NULL) {cout << "請(qǐng)輸入文件路徑!" << endl;return -1;}//獲取文件格式上下文AVFormatContext* p_inptu_file_AVFormatContext = avformat_alloc_context();//打開(kāi)文件if(avformat_open_input(&p_inptu_file_AVFormatContext, p_intputFilePath, NULL, NULL) != 0) {cout << "文件打開(kāi)失敗" << endl;return -1;};//獲取文件流信息if(avformat_find_stream_info(p_inptu_file_AVFormatContext, NULL) < 0) {cout << "獲取信息失敗" << endl;return -1;}//獲取到音頻,視頻索引int audioIndex = av_find_best_stream(p_inptu_file_AVFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, 0, NULL);int vedioIndex = av_find_best_stream(p_inptu_file_AVFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, 0, NULL);//找到音頻流和視頻流AVStream* p_input_file_a_AVStream = p_inptu_file_AVFormatContext->streams[audioIndex];AVStream* p_input_file_v_AVStream = p_inptu_file_AVFormatContext->streams[vedioIndex];//獲取到解碼器AVCodec* p_input_file_a_AVCodec = avcodec_find_decoder(p_input_file_a_AVStream->codecpar->codec_id);AVCodec* p_input_file_v_AVCodec = avcodec_find_decoder(p_input_file_v_AVStream->codecpar->codec_id);//獲取音頻,視頻的AVCodecContext。特別注意,ffmpeg4.2.2版本提倡使用AVCodecParameters。AVCodecContext* p_input_file_a_AVCodecContext = avcodec_alloc_context3(NULL);avcodec_parameters_to_context(p_input_file_a_AVCodecContext, p_input_file_a_AVStream->codecpar);AVCodecContext* p_input_file_v_AVCodecContext = avcodec_alloc_context3(NULL);avcodec_parameters_to_context(p_input_file_v_AVCodecContext, p_input_file_v_AVStream->codecpar);//是否含有視頻流和音頻流(一般都包含)if(audioIndex < 0) {cout << "此文件不包含音頻流或者沒(méi)有解碼器" << endl;}if(vedioIndex < 0) {cout << "此文件不包含視頻流或者沒(méi)有解碼器" << endl;} //打開(kāi)音頻,視頻的解碼器if(avcodec_open2(p_input_file_a_AVCodecContext, p_input_file_a_AVCodec, NULL) < 0) {cout << "音頻解碼器打開(kāi)失敗!" << endl; return -1;}if(avcodec_open2(p_input_file_v_AVCodecContext, p_input_file_v_AVCodec, NULL) < 0) {cout << "視頻解碼器代開(kāi)失敗!" << endl;return -1;}cout << "-------------------------------------- 輸出信息開(kāi)始位置 ---------------------------------" << endl;av_dump_format(p_inptu_file_AVFormatContext, 0, p_intputFilePath, 0);cout << "-------------------------------------- 輸出信息結(jié)束位置 ---------------------------------" << endl;//讀取一幀數(shù)據(jù)存放空間AVPacket* p_read_frame_AVPacket = av_packet_alloc();//解碼一幀數(shù)據(jù)存放空間AVFrame* p_deco_AVFrame = av_frame_alloc();//轉(zhuǎn)碼一幀數(shù)據(jù)存放空間AVFrame* p_z_p_deco_AVFrame = av_frame_alloc();p_z_p_deco_AVFrame->width = p_input_file_v_AVCodecContext->width;p_z_p_deco_AVFrame->height = p_input_file_v_AVCodecContext->height;p_z_p_deco_AVFrame->format = AV_PIX_FMT_YUV420P;av_frame_get_buffer(p_z_p_deco_AVFrame, 4);//轉(zhuǎn)碼格式設(shè)置SwsContext* img_convert_ctx = NULL;//視頻編碼的數(shù)據(jù)輸出到out_file_h264.h264文件ofstream out_file_h264("out_file_h264.h264", ios::binary);//視頻解碼的數(shù)據(jù)輸出到out_file_yuv240p.yuvofstream out_file_yuv240p("out_file_yuv240p.yuv", ios::binary);//音頻編碼的數(shù)據(jù)輸出到out_file_aac.pcmofstream out_file_acc("out_file_aac.aac", ios::binary);//音頻解碼的數(shù)據(jù)輸出到out_file_pcm.pcmofstream out_file_pcm("out_file_pcm.pcm", ios::binary);int re = 0;int v_lenth = p_input_file_v_AVCodecContext->width * p_input_file_v_AVCodecContext->height;while(av_read_frame(p_inptu_file_AVFormatContext, p_read_frame_AVPacket) >= 0) {//輸出編碼數(shù)據(jù)if(p_read_frame_AVPacket->stream_index == vedioIndex) {//輸出視頻編碼數(shù)據(jù)out_file_h264.write((char*) p_read_frame_AVPacket->data, p_read_frame_AVPacket->size);//解碼視頻re = avcodec_send_packet(p_input_file_v_AVCodecContext, p_read_frame_AVPacket);if(re == 0) {while(avcodec_receive_frame(p_input_file_v_AVCodecContext, p_deco_AVFrame) == 0) {//這里需要轉(zhuǎn)換一下數(shù)據(jù) img_convert_ctx = sws_getContext(p_deco_AVFrame->width, p_deco_AVFrame->height, p_input_file_v_AVCodecContext->pix_fmt,p_input_file_v_AVCodecContext->width, p_input_file_v_AVCodecContext->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);sws_scale(img_convert_ctx, p_deco_AVFrame->data, p_deco_AVFrame->linesize, 0, p_input_file_v_AVCodecContext->height,p_z_p_deco_AVFrame->data, p_z_p_deco_AVFrame->linesize);//輸出視頻解碼數(shù)據(jù)out_file_yuv240p.write((char*) p_z_p_deco_AVFrame->data[0], v_lenth);out_file_yuv240p.write((char*) p_z_p_deco_AVFrame->data[1], v_lenth / 4);out_file_yuv240p.write((char*) p_z_p_deco_AVFrame->data[2], v_lenth / 4);}}else {cout << "視頻解解碼失敗" << endl;break;}}else {//輸出音頻編碼數(shù)據(jù)out_file_acc.write((char*) p_read_frame_AVPacket->data, p_read_frame_AVPacket->size);//解碼音頻re = avcodec_send_packet(p_input_file_a_AVCodecContext, p_read_frame_AVPacket);if(re == 0) {while(avcodec_receive_frame(p_input_file_a_AVCodecContext, p_deco_AVFrame) == 0) {//輸出音頻解碼數(shù)據(jù)for(int i = 0; i < p_deco_AVFrame->nb_samples; i++) {for(int j = 0; j < p_deco_AVFrame->channels; j++) { out_file_pcm.write((char*) p_deco_AVFrame->data[j], av_get_bytes_per_sample(p_input_file_a_AVCodecContext->sample_fmt));} } }}else {cout << "音頻解碼失敗" << endl;break;}}//減少引用av_packet_unref(p_read_frame_AVPacket);}//釋放內(nèi)存out_file_pcm.close();out_file_acc.close();out_file_yuv240p.close();out_file_h264.close();av_frame_free(&p_z_p_deco_AVFrame);av_frame_free(&p_deco_AVFrame);av_packet_free(&p_read_frame_AVPacket);avcodec_free_context(&p_input_file_a_AVCodecContext);avcodec_free_context(&p_input_file_v_AVCodecContext);avformat_free_context(p_inptu_file_AVFormatContext);?
給出觀看雷霄驊錄制的視頻地址:基于FFmpeg+SDL的視頻播放器的制作——雷霄驊
總結(jié)
以上是生活随笔為你收集整理的使用FFMPEG——4.2.2版本实现提取视频编码解码文件,ffmpeg基础学习。的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: html+css制作圣诞树
- 下一篇: python numpy 行 列个数_P