視頻解碼流程
在對多媒體文件中的視頻流解碼前轮听,我們先來了解以下流媒體數(shù)據(jù)的播放流程琼腔,可以根據(jù)這個流程梳理一下視頻解碼流程
音視頻播放的原理主要分為:解協(xié)議 -> 解封裝 -> 解碼 -> 音視頻同步 -> 播放宙帝,不過如果播放文件是本地文件就不需要解協(xié)議這一步驟
其中對應(yīng)數(shù)據(jù)格式轉(zhuǎn)換流程為:多媒體文件 -> 流 -> 包 -> 幀
藍色元素塊代表具體的數(shù)據(jù)
紫色元素塊代表數(shù)據(jù)格式
橙色元素塊代表數(shù)據(jù)所對應(yīng)的協(xié)議
白色元素塊代表執(zhí)行的操作
在 FFmpeg 中獲取多媒體文件中視頻流的數(shù)據(jù)具體流程如下圖:
具體解碼流程
接下來斯嚎,我們根據(jù)上述的流程通過FFmpeg
對視頻流進行解碼,具體代碼如下:
extern"C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}
#include <iostream>
using namespace std;
int main() {
int ret = 0;
//文件地址
const char* filePath = "target.mp4";
//聲明所需的變量名
AVFormatContext* fmtCtx = NULL;
AVCodecContext* codecCtx = NULL;
AVCodecParameters* avCodecPara = NULL;
AVCodec* codec = NULL;
//包
AVPacket* pkt = NULL;
//幀
AVFrame* frame = NULL;
do {
//----------------- 創(chuàng)建AVFormatContext結(jié)構(gòu)體 -------------------
//內(nèi)部存放著描述媒體文件或媒體流的構(gòu)成和基本信息
fmtCtx = avformat_alloc_context();
//----------------- 打開本地文件 -------------------
ret = avformat_open_input(&fmtCtx, filePath, NULL, NULL);
if (ret) {
printf("cannot open file\n");
break;
}
//----------------- 獲取多媒體文件信息 -------------------
ret = avformat_find_stream_info(fmtCtx, NULL);
if (ret < 0) {
printf("Cannot find stream information\n");
break;
}
//通過循環(huán)查找多媒體文件中包含的流信息肯腕,直到找到視頻類型的流献宫,并記錄該索引值
int videoIndex = -1;
for (int i = 0; i < fmtCtx->nb_streams; i++) {
if (fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoIndex = i;
break;
}
}
//如果videoIndex為-1 說明沒有找到視頻流
if (videoIndex == -1) {
printf("cannot find video stream\n");
break;
}
//打印流信息
av_dump_format(fmtCtx, 0, filePath, 0);
//----------------- 查找解碼器 -------------------
avCodecPara = fmtCtx->streams[videoIndex]->codecpar;
AVCodec* codec = avcodec_find_decoder(avCodecPara->codec_id);
if (codec == NULL) {
printf("cannot open decoder\n");
break;
}
//根據(jù)解碼器參數(shù)來創(chuàng)建解碼器上下文
codecCtx = avcodec_alloc_context3(codec);
ret = avcodec_parameters_to_context(codecCtx, avCodecPara);
if (ret < 0) {
printf("parameters to context fail\n");
break;
}
//----------------- 打開解碼器 -------------------
ret = avcodec_open2(codecCtx, codec, NULL);
if (ret < 0) {
printf("cannot open decoder\n");
break;
}
//----------------- 創(chuàng)建AVPacket和AVFrame結(jié)構(gòu)體 -------------------
pkt = av_packet_alloc();
frame = av_frame_alloc();
//----------------- 讀取視頻幀 -------------------
int i = 0; //記錄視頻幀數(shù)
while (av_read_frame(fmtCtx, pkt) >= 0) {//讀取的是一幀視頻 數(shù)據(jù)存入AVPacket結(jié)構(gòu)體中
//是否對應(yīng)視頻流的幀
if (pkt->stream_index == videoIndex) {
//發(fā)送包數(shù)據(jù)去進行解析獲得幀數(shù)據(jù)
ret = avcodec_send_packet(codecCtx, pkt);
if (ret == 0) {
//接收的幀不一定只有一個,可能為0個或多個
//比如:h264中存在B幀实撒,會參考前幀和后幀數(shù)據(jù)得出圖像數(shù)據(jù)
//即讀到B幀時不會產(chǎn)出對應(yīng)數(shù)據(jù)姊途,直到后一個有效幀讀取時才會有數(shù)據(jù),此時就有2幀
while (avcodec_receive_frame(codecCtx, frame) == 0) {
//此處就可以獲取到視頻幀中的圖像數(shù)據(jù) -> frame.data
//可以通過openCV奈惑、openGL吭净、SDL方式進行顯示
//也可以保存到文件中(需要添加文件頭)
i++;
}
}
}
av_packet_unref(pkt);//重置pkt的內(nèi)容
}
//此時緩存區(qū)中還存在數(shù)據(jù),需要發(fā)送空包刷新
ret = avcodec_send_packet(codecCtx, NULL);
if (ret == 0) {
while (avcodec_receive_frame(codecCtx, frame) == 0) {
i++;
}
}
printf("There are %d frames int total.\n", i);
} while (0);
//----------------- 釋放所有指針 -------------------
avcodec_close(codecCtx);
avformat_close_input(&fmtCtx);
av_packet_free(&pkt);
av_frame_free(&frame);
return 0;
}
輸出結(jié)果:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'target.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.48.100
Duration: 00:03:10.36, start: 0.000000, bitrate: 773 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720, 442 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 325 kb/s (default)
Metadata:
handler_name : SoundHandler
There are 4759 frames int total.
4759 ÷ 25 + 0.0 = 190.36 ? 03 :10. 36
在 FFmpeg 中將 JPG肴甸、PNG圖片文件視為只有一幀的視頻流寂殉,也可以使用上述解碼流程讀取圖像數(shù)據(jù)
代碼解析
結(jié)構(gòu)體
上述代碼中涉及的結(jié)構(gòu)體有:AVFormatContext
、AVCodecParameters
原在、AVCodecContext
友扰、AVCodec
、AVPacket
庶柿、AVFrame
其中
AVFormatContext
已經(jīng)講解過村怪,此處就不再闡述
AVCodecParameters
和AVCodecContext
新的 FFmpeg 中 AVStream.codecpar(struct AVCodecParameter)
代替 AVStream.codec(struct AVCodecContext)
:AVCodecParameter
是由 AVCodecContext
分離出來的,AVCodecParameter
中沒有函數(shù)浮庐,里面存放著解碼器所需的各種參數(shù)
AVCodecContext
結(jié)構(gòu)體仍然是編解碼時不可或缺的結(jié)構(gòu)體
// 其中截取出部分較為重要的數(shù)據(jù)
typedef struct AVCodecParameters {
enum AVMediaType codec_type; //編解碼器的類型(視頻甚负,音頻...)
enum AVCodecID codec_id; //標(biāo)示特定的編碼器
int bit_rate; //平均比特率
int sample_rate; //采樣率(音頻)
int channels; //聲道數(shù)(音頻)
uint64_t channel_layout; //聲道格式
int width, height; //寬和高(視頻)
int format; //像素格式(視頻)/采樣格式(音頻)
...
} AVCodecParameters;
typedef struct AVCodecContext {
//在AVCodecParameters中的屬性,AVCodecContext都有
struct AVCodec *codec; //采用的解碼器AVCodec(H.264,MPEG2...)
enum AVSampleFormat sample_fmt; //采樣格式(音頻)
enum AVPixelFormat pix_fmt; //像素格式(視頻)
...
}AVCodecContext;
其中
avcodec_parameters_to_context
就是將AVCodecParameter
的參數(shù)傳給AVCodecContext
AVCodec
AVCodec
解碼器結(jié)構(gòu)體审残,對應(yīng)一個具體的解碼器
// 其中截取出部分較為重要的數(shù)據(jù)
typedef struct AVCodec {
const char *name; //編解碼器短名字(形如:"h264")
const char *long_name; //編解碼器全稱(形如:"H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10")
enum AVMediaType type; //媒體類型:視頻梭域、音頻或字母
enum AVCodecID id; //標(biāo)示特定的編碼器
const AVRational *supported_framerates; //支持的幀率(僅視頻)
const enum AVPixelFormat *pix_fmts; //支持的像素格式(僅視頻)
const int *supported_samplerates; //支持的采樣率(僅音頻)
const enum AVSampleFormat *sample_fmts; //支持的采樣格式(僅音頻)
const uint64_t *channel_layouts; //支持的聲道數(shù)(僅音頻)
...
}AVCodec ;
AVPacket
AVPacket
:存儲解碼前數(shù)據(jù)的結(jié)構(gòu)體,即包
// 其中截取出部分較為重要的數(shù)據(jù)
typedef struct AVPacket {
AVBufferRef *buf; //管理data指向的數(shù)據(jù)
uint8_t *data; //壓縮編碼的數(shù)據(jù)
int size; //data的大小
int64_t pts; //顯示時間戳
int64_t dts; //解碼時間戳
int stream_index; //標(biāo)識該AVPacket所屬的視頻/音頻流
...
}AVPacket ;
AVPacket
的內(nèi)存管理
AVPacket
本身并不包含壓縮的數(shù)據(jù)搅轿,通過data指針引用數(shù)據(jù)的緩存空間可以多個
AVPacket
共享同一個數(shù)據(jù)緩存(AVBufferRef
病涨、AVBuffer
)av_read_frame(pFormatCtx, packet); // 讀取Packet av_packet_ref(dst_pkt,packet); // dst_pkt 和 packet 共享同一個數(shù)據(jù)緩存空間,引用計數(shù)+1 av_packet_unref(dst_pkt); // 釋放 pkt_pkt 引用的數(shù)據(jù)緩存空間璧坟,引用計數(shù)-1
AVFrame
AVFrame
:存儲解碼后數(shù)據(jù)的結(jié)構(gòu)體既穆,即幀
// 其中截取出部分較為重要的數(shù)據(jù)
typedef struct AVFrame {
uint8_t *data[AV_NUM_DATA_POINTERS]; //解碼后原始數(shù)據(jù)(對視頻來說是YUV赎懦,RGB,對音頻來說是PCM)
int linesize[AV_NUM_DATA_POINTERS]; //data中“一行”數(shù)據(jù)的大小幻工。注意:未必等于圖像的寬励两,一般大于圖像的寬。
int width, height; //視頻幀寬和高(1920x1080,1280x720...)
int format; //解碼后原始數(shù)據(jù)類型(YUV420会钝,YUV422伐蒋,RGB24...)
int key_frame; //是否是關(guān)鍵幀
enum AVPictureType pict_type; //幀類型(I,B,P...)
AVRational sample_aspect_ratio; //圖像寬高比(16:9,4:3...)
int64_t pts; //顯示時間戳
int coded_picture_number; //編碼幀序號
int display_picture_number; //顯示幀序號
int nb_samples; //音頻采樣數(shù)
...
}AVFrame ;
函數(shù)
avcodec_find_decoder
avcodec_find_decoder
根據(jù)解碼器ID查找到對應(yīng)的解碼器
AVCodec *avcodec_find_decoder(enum AVCodecID id); //通過id查找解碼器
AVCodec *avcodec_find_decoder_by_name(const char *name); //通過解碼器名字查找
/* 與解碼器對應(yīng)的就是編碼器迁酸,也有相應(yīng)的查找函數(shù) */
AVCodec *avcodec_find_encoder(enum AVCodecID id); //通過id查找編碼器
AVCodec *avcodec_find_encoder_by_name(const char *name); //通過編碼器名字查找
參數(shù):
- enum AVCodecID id:解碼器ID,可以從
AVCodecParameters
中獲取
return:
返回一個AVCodec
指針俭正,如果沒有找到就返回NULL
avcodec_alloc_context3
avcodec_alloc_context3
會生成一個AVCodecContext
并根據(jù)解碼器給屬性設(shè)置默認值
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
參數(shù):
- const AVCodec *codec:解碼器指針奸鬓,會根據(jù)解碼器分配私有數(shù)據(jù)并初始化默認值
return:
返回一個AVCodec
指針,如果創(chuàng)建失敗則會返回NULL
話說
avcodec_alloc_context3
函數(shù)名中的 3 是什么含義掸读?
avcodec_parameters_to_context
avcodec_parameters_to_context
將AVCodecParameters
中的屬性賦值給AVCodecContext
int avcodec_parameters_to_context(AVCodecContext *codec,
const AVCodecParameters *par){
//將par中的屬性賦值給codec
codec->codec_type = par->codec_type;
codec->codec_id = par->codec_id;
codec->codec_tag = par->codec_tag;
...
}
參數(shù):
- AVCodecContext *codec:需要被賦值的
AVCodecContext
- const AVCodecParameters *par:提供屬性值的
AVCodecParameters
return:
返回數(shù)值 ≥ 0時代表成功串远,失敗時會返回一個負值
avcodec_open2
avcodec_open2
打開音頻解碼器或者視頻解碼器
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options){
...
avctx->codec = codec;
...
}
參數(shù):
- AVCodecContext *avctx:已經(jīng)初始化完畢的
AVCodecContext
- const AVCodec *codec:用于打開
AVCodecContext
中的解碼器,之后AVCodecContext
會使用該解碼器進行解碼 - AVDictionary **options:指定各種參數(shù)儿惫,基本填NULL即可
return:
返回0表示成功澡罚,若失敗則會返回一個負數(shù)
av_read_frame
av_read_frame
獲取音視頻(編碼)數(shù)據(jù),即從流中獲取一個AVPacket
數(shù)據(jù)肾请。將文件中存儲的內(nèi)容分割成包留搔,并為每個調(diào)用返回一個包
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
參數(shù):
- AVFormatContext *s:
AVFormatContext
結(jié)構(gòu)體 - AVPacket *pkt:通過data指針引用數(shù)據(jù)的緩存空間,本身不存儲數(shù)據(jù)
return:
返回0表示成功铛铁,失敗或讀到了文件結(jié)尾則會返回一個負數(shù)
函數(shù)為什么是
av_read_frame
而不是av_read_packet
隔显,是早期 FFmpeg 設(shè)計時候沒有包的概念,而是編碼前的幀和編碼后的幀饵逐,不容易區(qū)分括眠。之后才產(chǎn)生包的概念,但出于編程習(xí)慣或向前兼容的原因倍权,于是方法名就這樣延續(xù)了下來
avcodec_send_packet
avcodec_send_packet
用于向解碼器發(fā)送一個包掷豺,讓解碼器進行解析
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
參數(shù):
- AVCodecContext *avctx:
AVCodecContext
結(jié)構(gòu)體,必須使用avcodec_open2
打開解碼器 - const AVPacket *avpkt:用于解析的數(shù)據(jù)包
return:
返回0表示成功薄声,失敗則返回負數(shù)的錯誤碼当船,異常值說明:
- AVERROR(EAGAIN):當(dāng)前不接受輸出,必須重新發(fā)送
- AVERROR_EOF:解碼器已經(jīng)刷新奸柬,并且沒有新的包可以發(fā)送
- AVERROR(EINVAL):解碼器沒有打開生年,或者這是一個編碼器
- AVERRO(ENOMEN):無法添加包到內(nèi)部隊列
avcodec_receive_frame
avcodec_receive_frame
獲取解碼后的音視頻數(shù)據(jù)(音視頻原始數(shù)據(jù),如YUV和PCM)
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
參數(shù):
- AVCodecContext *avctx:
AVCodecContext
結(jié)構(gòu)體 - AVFrame *frame:用于接收解碼后的音視頻數(shù)據(jù)的幀
return:
返回0表示成功廓奕,其余情況表示失敗抱婉,異常值說明:
- AVERROR(EAGAIN):此狀態(tài)下輸出不可用档叔,需要發(fā)送新的輸入才能解析
- AVERROR_EOF:解碼器已經(jīng)刷新,并且沒有新的包可以發(fā)送
- AVERROR(EINVAL):解碼器沒有打開蒸绩,或者這是一個編碼器
調(diào)用
avcodec_receive_frame
方法時不需要通過av_packet_unref
解引用衙四,因為在該方法內(nèi)部已經(jīng)調(diào)用過av_packet_unref
方法解引用
嚴(yán)格來說沐悦,除
AVERROR(EAGAIN)
和AVERROR_EOF
兩種錯誤情況之外的報錯遣妥,應(yīng)該直接退出程序
釋放資源函數(shù)
avcodec_close(codecCtx);
avformat_close_input(&fmtCtx);
av_packet_free(&pkt);
av_frame_free(&frame);
只有我們手動申請的資源才需要我們手動進行釋放,其余的資源FFmpeg會自動釋放习寸,重復(fù)調(diào)用會報錯
如:
AVCodecParameters
是AVFormatContext
內(nèi)部的資源步藕,就不需要我們手動釋放惦界,在avformat_close_input
函數(shù)中會對其進行釋放,不需要我們通過avcodec_parameters_free
釋放
使用OpenCV顯示視頻圖像
因為最近還在學(xué)OpenCV處理圖像咙冗,而且相比于SDL沾歪、OpenGL或ANativeWindow流程更加簡潔,所以這里就通過OpenCV顯示視頻圖像
extern"C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
}
#include <iostream>
using namespace std;
#include <opencv2/opencv.hpp>
using namespace cv;
int main() {
int ret = 0;
//文件地址
const char* filePath = "target.mp4";
//聲明所需的變量名
AVFormatContext* fmtCtx = NULL;
AVPacket* pkt = NULL;
AVCodecContext* codecCtx = NULL;
AVCodecParameters* avCodecPara = NULL;
AVCodec* codec = NULL;
//幀雾消,并進行初始化
AVFrame* rgbFrame = av_frame_alloc();
AVFrame* yuvFrame = av_frame_alloc();
do {
...
//----------------- 設(shè)置數(shù)據(jù)轉(zhuǎn)換參數(shù) -------------------
struct SwsContext* img_ctx = sws_getContext(
codecCtx->width, codecCtx->height, codecCtx->pix_fmt, //源地址長寬以及數(shù)據(jù)格式
codecCtx->width, codecCtx->height, AV_PIX_FMT_BGR24, //目的地址長寬以及數(shù)據(jù)格式
SWS_BICUBIC, NULL, NULL, NULL);
//一幀圖像數(shù)據(jù)大小灾搏,會根據(jù)圖像格式、圖像寬高計算所需內(nèi)存字節(jié)大小
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_BGR24, codecCtx->width, codecCtx->height, 1);
unsigned char* out_buffer = (unsigned char*)av_malloc(numBytes * sizeof(unsigned char));
//將rgbFrame中的數(shù)據(jù)以BGR格式存放在out_buffer中
av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, out_buffer, AV_PIX_FMT_BGR24, codecCtx->width, codecCtx->height, 1);
//創(chuàng)建OpenCV中的Mat
Mat img = Mat(Size(codecCtx->width, codecCtx->height), CV_8UC3);
while (av_read_frame(fmtCtx, pkt) >= 0) {
if (pkt->stream_index == videoIndex) {
ret = avcodec_send_packet(codecCtx, pkt);
if (ret == 0) {
while (avcodec_receive_frame(codecCtx, yuvFrame) == 0) {
//mp4文件中視頻流使用的是h.264立润,幀圖像為yuv420格式
//通過sws_scale將數(shù)據(jù)轉(zhuǎn)換為BGR格式
sws_scale(img_ctx,
(const uint8_t* const*)yuvFrame->data,
yuvFrame->linesize,
0,
codecCtx->height,
rgbFrame->data,
rgbFrame->linesize);
img.data = rgbFrame->data[0];
imshow("img", img);
waitKey(40);
}
}
}
sws_freeContext(img_ctx);//釋放SwsContext結(jié)構(gòu)體
av_packet_unref(pkt);//重置pkt的內(nèi)容
}
...
} while (0);
//----------------- 釋放所有指針 -------------------
av_packet_free(&pkt);
avcodec_close(codecCtx);
avformat_close_input(&fmtCtx);
av_free(codec);
av_frame_free(&rgbFrame);
av_frame_free(&yuvFrame);
return 0;
}
代碼解析
結(jié)構(gòu)體
libswscale
庫用于視頻場景比例縮放狂窑、色彩映射轉(zhuǎn)換;圖像顏色空間或格式轉(zhuǎn)換桑腮,而SwsContext
結(jié)構(gòu)體貫穿整個變換流程泉哈,其中存放變換所需的參數(shù)
// 其中截取出部分較為重要的數(shù)據(jù)
typedef struct SwsContext {
int srcW; //源圖像中亮度/alpha的寬度
int srcH; //源圖像中亮度/alpha的高度
int dstH; //目標(biāo)圖像中的亮度/alpha的寬度
int dstW; //目標(biāo)圖像中的亮度/alpha的高度
int chrSrcW; //源圖像中色度的寬度
int chrSrcH; //源圖像中色度的高度
int chrDstW; //目標(biāo)圖像中色度的寬度
int chrDstH; //目標(biāo)圖像中色度的高度
enum AVPixelFormat dstFormat; //目標(biāo)圖像的格式,如:YUV420P到旦、YUV444旨巷、RGB、RGBA添忘、GRAY等
enum AVPixelFormat srcFormat; //源圖像的格式
int needAlpha; //是否存在透明度
int flags; //選擇采呐、優(yōu)化、子采樣算法的flag標(biāo)識
...
} SwsContext;
函數(shù)
sws_getContext
sws_getContext
用來創(chuàng)建并返回SwsContext
結(jié)構(gòu)體
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
參數(shù):
- int srcW:源圖像的寬
- int srcH:源圖像的高
- enum AVPixelFormat srcFormat:源圖像的格式搁骑,如:YUV420P斧吐、YUV444、RGB仲器、RGBA煤率、GRAY等
- int dstW:目標(biāo)圖像的寬
- int dstH:目標(biāo)圖像的高
- enum AVPixelFormat dstFormat:目標(biāo)圖像的格式
- int flags:指定算法進行縮放插值
- SwsFilter *srcFilter、SwsFilter *dstFilter:與Chroma/luminsence濾波相關(guān)乏冀,一般填NULL即可
- const double *param:用于scalar的額外的數(shù)據(jù)蝶糯,一般填NULL即可
return:
返回一個指向SwsContext的指針,或者出現(xiàn)錯誤的時候返回NULL
int flags:指定算法類型
#define SWS_FAST_BILINEAR 1 //選擇快速雙線性縮放算法 #define SWS_BILINEAR 2 //選擇雙線性縮放算法 #define SWS_BICUBIC 4 //選擇雙三次縮放算法 #define SWS_X 8 #define SWS_POINT 0x10 #define SWS_AREA 0x20 #define SWS_BICUBLIN 0x40 #define SWS_GAUSS 0x80 #define SWS_SINC 0x100 #define SWS_LANCZOS 0x200 #define SWS_SPLINE 0x400
其中具體根據(jù)需求選擇合適的算法辆沦,可以看一下如何選擇swscale中的縮放算法
其實還有一個獲取
SwsContext
結(jié)構(gòu)體的函數(shù)——sws_getCachedContext
昼捍,這個函數(shù)會根據(jù)參數(shù)去檢驗參數(shù)中的SwsContext
是否符合之后輸入的參數(shù)识虚,符合就直接返回該結(jié)構(gòu)體指針進行復(fù)用,若不符合則會進行釋放妒茬,然后根據(jù)參數(shù)創(chuàng)建一個新的SwsContext
結(jié)構(gòu)體并返回其指針struct SwsContext *sws_getCachedContext(struct SwsContext *context, int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param);
av_image_fill_arrays
該函數(shù)會根據(jù)圖像類型參數(shù)担锤、數(shù)組參數(shù)和寬高設(shè)置數(shù)據(jù)指針和線寬值
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
const uint8_t *src,
enum AVPixelFormat pix_fmt, int width, int height, int align);
參數(shù):
- uint8_t *dst_data[4]:要進行填充的數(shù)據(jù)指針
- int dst_linesize[4]:填充的圖像的線寬值
- const uint8_t *src:包含或之后會包含實際的圖像數(shù)據(jù)
- enum AVPixelFormat pix_fmt:圖像格式
- int width:圖像的寬
- int height:圖像的高
- int align:是否根據(jù)線寬對src進行對齊調(diào)整
return:
成功返回src所需的字節(jié)大小,失敗會返回一個負數(shù)的錯誤碼
sws_scale
sws_scale
函數(shù)會根據(jù)SwsContext
中設(shè)置的參數(shù)乍钻,將源圖像轉(zhuǎn)換為對應(yīng)屬性的目標(biāo)圖像肛循,其中srcSlice
必須是以圖像中連續(xù)的行序列為順序的二維數(shù)組
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);
參數(shù):
- struct SwsContext *c:之前創(chuàng)建的
SwsContext
結(jié)構(gòu)體 - const uint8_t *const srcSlice[]:包含指向源數(shù)據(jù)平面的指針的數(shù)組,如:yuv420p中 y银择、u多糠、v 數(shù)據(jù)分別存放,可以視為3個uint8_t數(shù)組
- const int srcStride[]:對應(yīng)每個源數(shù)據(jù)平面的長度
- int srcSliceY:開始處理的y坐標(biāo)位置
- int srcSliceH:源數(shù)據(jù)平面的高度浩考,即對應(yīng)數(shù)組的長度
- uint8_t *const dst[]:目標(biāo)圖像數(shù)據(jù)指針
- const int dstStride[]:目標(biāo)圖像每個源數(shù)據(jù)平面的長度
return:
輸出目標(biāo)圖像源數(shù)據(jù)平面的高度
RGB類型的圖像R熬丧、G、B值混合存放所以無法將RGB抽離成對應(yīng)的三個連續(xù)的數(shù)組怀挠,其數(shù)據(jù)全部存放在 data[0] 中,所以可以直接使用
img.data = rgbFrame->data[0]
給Mat賦值而對于YUV格式的圖像數(shù)據(jù)害捕,按照
img.data = rgbFrame->data[0]
的方式給Mat賦值就會報錯