都0202年了,本文基於FFmpeg4.2.1,將使用最新版的api。讓av_register_all()見鬼去吧!
FFmpeg的文章絕大多數都是3.X的,不少方法都過期了。對於喜新厭舊的潔癖君而言,滿屏飄黃的警告、運行一下全是過期的警告是多麼糟心。本文根據源碼中的exsample進行改編,刪繁就簡,對於判空,校驗返回值,打印錯誤什麼的,本身在使用時注意一下,自行處理。java
爲了讓你明白這篇文章是在幹嗎,講個小故事先:ios
捷特有兩個護體神獸:白皇和黑皇
白皇善鳴,聲震天地。身長千尺,振翅遮天蔽日。
黑皇善視,目之所見錄於腦中,萬世不滅。身高萬丈,舉手可握雲擎天
像這種巨無霸級別的神獸是沒法隨身攜帶的。男主不被打到鎖血,是不會召喚出來的。
那如何凸顯主角的特別:動漫裏的橋段是萌化成兩個黑白服飾的漂亮妹子守護男主。
但兩我的在面前晃晃悠悠也很差帶回家,將兩者封印在一個掛墜中,使用時進行召喚。
打BOSS怎麼打的:
捷特握着掛墜-->召喚黑白萌皇-->萌皇巨大化 --> 打Boss
複製代碼
上面的故事包含音視頻的數據概念:編程
很是大的原始數據: 音頻pcm --> 巨獸白皇
編碼後較小數據: 音頻aac --> 人型萌白皇
很是大的原始數據: 視頻YUV --> 巨獸黑皇
編碼後較小數據: 視頻h264 --> 人型萌黑皇
mp四、ts、avi等封裝體格式: aac + h264 ---->封印掛墜
播放器怎麼播放的:
拆封封裝格式-->召喚尋找編碼流-->解碼流 --> 渲染呈現
複製代碼
這篇的目標是將掛墜
(sh.ts)
中的黑萌娘(sh.h264)
召喚出來,而且轉換成神獸黑皇(sh_768x432.yuv)
api
爲何說YUV是巨獸,看下面的數據就知道了。視頻3分30秒,YUV數據飆到2.48G。也許你會以爲爲何會有YUV這樣逆天的存在,其實渲染層須要YUV,給它壓縮後的數據人家不認識。播放時會先進行解碼, 你能在手機裏存幾百部片,都要感謝壓縮格式。bash
這裏介紹主要的流程,這大白話的註釋你要再看不懂我也沒辦法了。ide
#include <iostream>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};
using namespace std;
int main() {
const char *rec_path = "/Volumes/coder/Projects/Media/TolyFF/res/sh.ts";
AVFormatContext *fmt_ctx;//格式化上下文--召喚場地
fmt_ctx = avformat_alloc_context();// 準備召喚場地
avformat_open_input(&fmt_ctx, rec_path, nullptr, nullptr);//打開封印
avformat_find_stream_info(fmt_ctx, nullptr);//找到召喚物
int v_idx = av_find_best_stream(//尋找視頻流索引 -- 萌黑
fmt_ctx, AVMEDIA_TYPE_VIDEO,-1, -1, nullptr, 0);
AVCodecParameters *c_par;//聲明-召喚器參數
AVCodecContext *cc_ctx;//聲明-召喚器環境
const AVCodec *codec;//聲明-召喚器
c_par = fmt_ctx->streams[v_idx]->codecpar;//實例化-召喚器參數
codec = avcodec_find_decoder(c_par->codec_id);//實例化-召喚器
//用參數c_par實例化編解碼器上下文,,並打開編解碼器
cc_ctx = avcodec_alloc_context3(codec);//實例化-召喚器環境
avcodec_parameters_to_context(cc_ctx, c_par);//召喚器環境參數填充
avcodec_open2(cc_ctx, codec, nullptr);//打開召喚器
AVPacket *pkt; //聲明 萌黑-數據包
AVFrame *frame;//聲明 黑皇-幀
pkt = av_packet_alloc();//準備 黑皇-幀
frame = av_frame_alloc();//準備 萌黑-數據包
FILE *dst_f=fopen("/Volumes/coder/Projects/Media/TolyFF/res/sh.h264","wb+");//萌黑實體sh.h264
FILE *dst_f_yuv=fopen("/Volumes/coder/Projects/Media/TolyFF/res/sh_768x432.yuv","wb+");//黑皇實體sh_768x432.yuv
while (av_read_frame(fmt_ctx, pkt) >= 0) {//持續讀幀
if (pkt->stream_index == v_idx) {
avcodec_send_packet(cc_ctx, pkt);//發送 萌黑-數據包
fwrite(pkt->data,1,pkt->size,dst_f);// 萌黑-數據包拼合集結
vcodec_receive_frame(cc_ctx, frame);//接收解碼 -- 巨大化
fwrite(frame->data[0],1,cc_ctx->width*cc_ctx->height,dst_f_yuv);// 黑皇Y-數據包拼合集結
fwrite(frame->data[1],1,cc_ctx->width/2*cc_ctx->height/2,dst_f_yuv);// 黑皇U-數據包拼合集結
fwrite(frame->data[2],1,cc_ctx->width/2*cc_ctx->height/2,dst_f_yuv);// 黑皇V-數據包拼合集結
}
}
fclose(dst_f);// 萌黑OK , 拔掉鏈接頭
fclose(dst_f_yuv);// 萌黑OK , 拔掉鏈接頭
avcodec_free_context(&cc_ctx);//關閉環境
av_frame_free(&frame);//釋放本體
av_packet_free(&pkt);//釋放載體
}
複製代碼
運行後會生成h264和YUV兩個文件,經過ffplay能夠播放
二者分別是純視頻的壓縮流和原始流,因此播放起來是沒有聲音的。
你也許會問爲嘛要解碼出這兩個流,有一種神技叫作融合,有一種操做叫作變換。少年呦,你對於力量一無所知。post
ffplay sh.h264
ffplay -video_size 768x432 sh_768x432.yuv
複製代碼
下圖是新舊的示意圖:編碼
註冊方法,去了吧spa
attribute_deprecated
void av_register_all(void);
複製代碼
AVStream中的codec方法能夠獲取AVCodecContext,已過期。
取而代之的是codecpar的AVCodecParameters,在根據參數去建立上下文。如上3d
cc_ctx = fmt_ctx->streams[v_idx]->codec;
/**
* @deprecated use the codecpar struct instead
*/
attribute_deprecated
AVCodecContext *codec;
複製代碼
avcodec_decode_video2 方法被拆成了兩個:
avcodec_send_packet()
和avcodec_receive_frame()
分別用於包和幀的處理。透露一下packet用來召喚萌娘,frame用來激活巨獸。
* @deprecated Use avcodec_send_packet() and avcodec_receive_frame().
*/
attribute_deprecated
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const AVPacket *avpkt);
複製代碼
AVFormatContext:
封裝格式上下文,包含一些介紹信息,及最重要的碼流stream,這就是數據的源泉。AVInputFormat記錄了封裝格式的信息。
AVCodecParameters:
編解碼器的參數,從AVStream的codecpar獲取,取代codec屬性。能夠獲取流的參數,如視頻流寬、高、編解碼器類型、編解碼器id等。
AVCodecContext:
編解碼器的上下文,可經過avcodec_parameters_to_context使用AVCodecParameters進行參數填充。也是記錄着視頻流的信息,不一樣的是他包含了編解碼器對象codec。
AVCodec:
編解碼器的上下文,至關於大古的神光棒,能讓大古變成光的東西。
AVPacket: 編碼後的碼流,對應現視頻,data字段也就是以及壓縮後的h264數據。初次以外還有其餘信息:
AVFrame:
解碼後的原始流,YUV份量合併可造成巨大的YUV神獸。
@張風捷特烈 2019.11.26 未允禁轉
個人公衆號:編程之王
~ END ~