264编码基本概念 FFMpeg的解码流程
下面轉自http://topic.csdn.net/u/20081020/16/7156e0b2-dbfb-4b4f-af59-2be04cf9a420.html 的8樓
1、NAL、Slice與frame意思及相互關系
NAL指網絡提取層,里面放一些與網絡相關的信息
Slice是片的意思,264中把圖像分成一幀(frame)或兩場(field),而幀又可以分成一個或幾個片(Slilce);片由宏塊(MB)組成。宏塊是編碼處理的基本單元。
2、NAL nal_unit_type中的1(非IDR圖像的編碼條帶)、2(編碼條帶數據分割塊A)、3(編碼條帶數據分割塊B)、4(編碼條帶數據分割塊C)、5(IDR圖像的編碼條帶)種類型
與 Slice種的三種編碼模式:I_slice、P_slice、B_slice
NAL nal_unit_type 里的五種類型,代表接下來數據是表示啥信息的和具體如何分塊。
I_slice、P_slice、B_slice 表示I類型的片、P類型的片,B類型的片.其中I_slice為幀內預測模式編碼;P_slice為單向預測編碼或幀內模式;B_slice 中為雙向預測或幀內模式。
3、還有frame的3種類型:I frame、P frame、 B frame之間有什么映射關系么?
I frame、P frame、 B frame關系同 I_slice、P_slice、B_slice,slice和frame區別在問題1中已經講明白。
4、最后,NAL nal_unit_type中的6(SEI)、7(SPS)、8(PPS)屬于什么幀呢?
NAL nal_unit_type 為序列參數集(SPS)、圖像參數集(PPS)、增強信息(SEI)不屬于啥幀的概念。表示后面的數據信息為序列參數集(SPS)、圖像參數集(PPS)、增強信息(SEI)。
====================================================================================
NAL單元中首先會有一個H.264 NAL type,根據這個可以判斷是啥信息。如果是
H264NT_SLICE_DPA,H264NT_SLICE_DPB,H264NT_SLICE_DPC, H264NT_SLICE_IDR視頻數據相關的,里面還會有Slice head頭信息,根據這個頭信息,可以判斷屬于I-Slice(P-Slice或B-Slice),之后對于每個宏塊,都會有MB head 信息,根據宏塊頭信息可以判斷塊模式。
H264就是這樣以分層的方式組織信息的。不知道你理解沒有。
====================================================================================
x264_encoder_encode每次會以參數送入一幀待編碼的幀pic_in,函數首先會從空閑隊列中取出一幀用于承載該新幀,而它的i_frame被設定為播放順序計數,如:fenc->i_frame = h->frames.i_input++。
FFMpeg的解碼流程
1. 從基礎談起
先給出幾個概念,以在后面的分析中方便理解
Container:在音視頻中的容器,一般指的是一種特定的文件格式,里面指明了所包含的
??? 音視頻,字幕等相關信息
Stream:這個詞有些微妙,很多地方都用到,比如TCP,SVR4系統等,其實在音視頻,你
??? 可以理解為單純的音頻數據或者視頻數據等
Frames:這個概念不是很好明確的表示,指的是Stream中的一個數據單元,要真正對這
??? 個概念有所理解,可能需要看一些音視頻編碼解碼的理論知識
Packet:是Stream的raw數據
Codec:Coded + Decoded
其實這些概念在在FFmpeg中都有很好的體現,我們在后續分析中會慢慢看到
2.解碼的基本流程
我很懶,于是還是選擇了從<An ffmpeg and SDL Tutorial>中的流程概述:
10 OPEN video_stream FROM video.avi
20 READ packet FROM video_stream INTO frame
30 IF frame NOT COMPLETE GOTO 20
40 DO SOMETHING WITH frame
50 GOTO 20
這就是解碼的全過程,一眼看去,是不是感覺不過如此:),不過,事情有深有淺,從淺
到深,然后從深回到淺可能才是一個有意思的過程,我們的故事,就從這里開始,展開
來講。
3.例子代碼
在<An ffmpeg and SDL Tutorial 1>中,給出了一個陽春版的解碼器,我們來仔細看看
陽春后面的故事,為了方便講述,我先貼出代碼:
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>
#include <stdio.h>
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
sprintf(szFilename, "frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
??? return;
// Write header
fprintf(pFile, "P6/n%d %d/n255/n", width, height);
// Write pixel data
for(y=0; y<height; y++)
??? fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
int main(int argc, char *argv[]) {
AVFormatContext *pFormatCtx;
int???????????? i, videoStream;
AVCodecContext *pCodecCtx;
AVCodec???????? *pCodec;
AVFrame???????? *pFrame;
AVFrame???????? *pFrameRGB;
AVPacket??????? packet;
int???????????? frameFinished;
int???????????? numBytes;
uint8_t???????? *buffer;
if(argc < 2) {
??? printf("Please provide a movie file/n");
??? return -1;
}
// Register all formats and codecs
########################################
[1]
########################################
av_register_all();
// Open video file
########################################
[2]
########################################
if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
??? return -1; // Couldn't open file
// Retrieve stream information
########################################
[3]
########################################
if(av_find_stream_info(pFormatCtx)<0)
??? return -1; // Couldn't find stream information
// Dump information about file onto standard error
dump_format(pFormatCtx, 0, argv[1], 0);
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
??? if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
????? videoStream=i;
????? break;
??? }
if(videoStream==-1)
??? return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
??? fprintf(stderr, "Unsupported codec!/n");
??? return -1; // Codec not found
}
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
??? return -1; // Could not open codec
// Allocate video frame
pFrame=avcodec_alloc_frame();
// Allocate an AVFrame structure
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
??? return -1;
???
// Determine required buffer size and allocate buffer
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
????????????????? pCodecCtx->height);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
???????? pCodecCtx->width, pCodecCtx->height);
// Read frames and save first five frames to disk
########################################
[4]
########################################
i=0;
while(av_read_frame(pFormatCtx, &packet)>=0) {
??? // Is this a packet from the video stream?
??? if(packet.stream_index==videoStream) {
????? // Decode video frame
????? avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
?????????????? packet.data, packet.size);
?????
????? // Did we get a video frame?
????? if(frameFinished) {
??? // Convert the image from its native format to RGB
??? img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,
??????????????????? (AVPicture*)pFrame, pCodecCtx->pix_fmt,
??????????????????? pCodecCtx->width,
??????????????????? pCodecCtx->height);
???
??? // Save the frame to disk
??? if(++i<=5)
????? SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
??????????? i);
????? }
??? }
???
??? // Free the packet that was allocated by av_read_frame
??? av_free_packet(&packet);
}
// Free the RGB image
av_free(buffer);
av_free(pFrameRGB);
// Free the YUV frame
av_free(pFrame);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
return 0;
}
代碼注釋得很清楚,沒什么過多需要講解的,關于其中的什么YUV420,RGB,PPM等格式
,如果不理解,麻煩還是google一下,也可以參考:http://barrypopy.cublog.cn/里面
的相關文章
其實這部分代碼,很好了Demo了怎么樣去抓屏功能的實現,但我們得去看看魔術師在后
臺的一些手法,而不只是簡單的享受其表演。
4.背后的故事
真正的難度,其實就是上面的[1],[2],[3],[4],其他部分,都是數據結構之間的轉換,
如果你認真看代碼的話,不難理解其他部分。
[1]:沒什么太多好說的,如果不明白,看我轉載的關于FFmepg框架的文章
[2]:先說說里面的AVFormatContext *pFormatCtx結構,字面意思理解AVFormatContext
就是關于AVFormat(其實就是我們上面說的Container格式)的所處的Context(場景),自
然是保存Container信息的總控結構了,后面你也可以看到,基本上所有的信息,都可
以從它出發而獲取到
???
我們來看看av_open_input_file()都做了些什么:
[libavformat/utils.c]
int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
?????????????????????? AVInputFormat *fmt,
?????????????????????? int buf_size,
?????????????????????? AVFormatParameters *ap)
{
??? ......
??? if (!fmt) {
??????? /* guess format if no file can be opened */
??????? fmt = av_probe_input_format(pd, 0);
??? }
?? ......
??? err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);
?? ......
}
這樣看來,只是做了兩件事情:
1). 偵測容器文件格式
2). 從容器文件獲取Stream的信息
這兩件事情,實際上就是調用特定文件的demuxer以分離Stream的過程:
具體流程如下:
av_open_input_file
??? |
??? +---->av_probe_input_format從first_iformat中遍歷注冊的所有demuxer以
??? |???? 調用相應的probe函數
??? |
??? +---->av_open_input_stream調用指定demuxer的read_header函數以獲取相關
????????? 流的信息ic->iformat->read_header
如果反過來再參考我轉貼的關于ffmpeg框架的文章,是否清楚一些了呢:)
[3]:簡單從AVFormatContext獲取Stream的信息,沒什么好多說的
[4]:先簡單說一些ffmpeg方面的東西,從理論角度說過來,Packet可以包含frame的部
分數據,但ffmpeg為了實現上的方便,使得對于視頻來說,每個Packet至少包含一
frame,對于音頻也是相應處理,這是實現方面的考慮,而非協議要求.
因此,在上面的代碼實際上是這樣的:
??? 從文件中讀取packet,從Packet中解碼相應的frame;
??? 從幀中解碼;
??? if(解碼幀完成)
??????? do something();
我們來看看如何獲取Packet,又如何從Packet中解碼frame的。
av_read_frame
??? |
??? +---->av_read_frame_internal
??????? |
??????? +---->av_parser_parse調用的是指定解碼器的s->parser->parser_parse函數以從raw packet中重構frame
avcodec_decode_video
??? |
??? +---->avctx->codec->decode調用指定Codec的解碼函數
???
因此,從上面的過程可以看到,實際上分為了兩部分:
一部分是解復用(demuxer),然后是解碼(decode)
使用的分別是:
av_open_input_file()??????????? ---->解復用
av_read_frame()??????????? |
?????????????????????????? |??? ---->解碼???
avcodec_decode_video()???? |
5.后面該做些什么
結合這部分和轉貼的ffmepg框架的文章,應該可以基本打通解碼的流程了,后面的問題則是針對具體容器格式和具體編碼解碼器的分析,后面我們繼續
參考:
[1]. <An ffmpeg and SDL Tutorial>
???? http://www.dranger.com/ffmpeg/tutorial01.HTML
????
[2]. <FFMpeg框架代碼閱讀>
???? http://blog.csdn.NET/wstarx/archive/2007/04/20/1572393.ASPx
總結
以上是生活随笔為你收集整理的264编码基本概念 FFMpeg的解码流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springCloud - 第7篇 -
- 下一篇: C# -WinForm 中英文实现,