企业自己的网站,第三方商城网站建设,邯郸学校网站建设,珠宝网站开发目的下面转自http://topic.csdn.net/u/20081020/16/7156e0b2-dbfb-4b4f-af59-2be04cf9a420.html 的8楼
1、NAL、Slice与frame意思及相互关系 NAL指网络提取层#xff0c;里面放一些与网络相关的信息Slice是片的意思#xff0c;264中把图像分成一帧#xff08;frame#xff09;…下面转自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、5IDR图像的编码条带种类型 与 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_sliceslice和frame区别在问题1中已经讲明白。4、最后NAL nal_unit_type中的6SEI、7SPS、8PPS属于什么帧呢 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-SliceP-Slice或B-Slice之后对于每个宏块都会有MB head 信息根据宏块头信息可以判断块模式。H264就是这样以分层的方式组织信息的。不知道你理解没有。 x264_encoder_encode每次会以参数送入一帧待编码的帧pic_in函数首先会从空闲队列中取出一帧用于承载该新帧而它的i_frame被设定为播放顺序计数如fenc-i_frame h-frames.i_input。 FFMpeg的解码流程
1. 从基础谈起先给出几个概念以在后面的分析中方便理解Container:在音视频中的容器一般指的是一种特定的文件格式里面指明了所包含的 音视频字幕等相关信息Stream:这个词有些微妙很多地方都用到比如TCPSVR4系统等其实在音视频你 可以理解为单纯的音频数据或者视频数据等Frames:这个概念不是很好明确的表示指的是Stream中的一个数据单元要真正对这 个概念有所理解可能需要看一些音视频编码解码的理论知识Packet:是Stream的raw数据Codec:Coded Decoded其实这些概念在在FFmpeg中都有很好的体现我们在后续分析中会慢慢看到
2.解码的基本流程我很懒于是还是选择了从An ffmpeg and SDL Tutorial中的流程概述:
10 OPEN video_stream FROM video.avi20 READ packet FROM video_stream INTO frame30 IF frame NOT COMPLETE GOTO 2040 DO SOMETHING WITH frame50 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 filesprintf(szFilename, frame%d.ppm, iFrame);pFilefopen(szFilename, wb);if(pFileNULL) return;// Write headerfprintf(pFile, P6/n%d %d/n255/n, width, height);// Write pixel datafor(y0; yheight; y) fwrite(pFrame-data[0]y*pFrame-linesize[0], 1, width*3, pFile);// Close filefclose(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; // Couldnt open file// Retrieve stream information########################################[3]########################################if(av_find_stream_info(pFormatCtx)0) return -1; // Couldnt find stream information// Dump information about file onto standard errordump_format(pFormatCtx, 0, argv[1], 0);// Find the first video streamvideoStream-1;for(i0; ipFormatCtx-nb_streams; i) if(pFormatCtx-streams[i]-codec-codec_typeCODEC_TYPE_VIDEO) { videoStreami; break; }if(videoStream-1) return -1; // Didnt find a video stream// Get a pointer to the codec context for the video streampCodecCtxpFormatCtx-streams[videoStream]-codec;// Find the decoder for the video streampCodecavcodec_find_decoder(pCodecCtx-codec_id);if(pCodecNULL) { fprintf(stderr, Unsupported codec!/n); return -1; // Codec not found}// Open codecif(avcodec_open(pCodecCtx, pCodec)0) return -1; // Could not open codec// Allocate video framepFrameavcodec_alloc_frame();// Allocate an AVFrame structurepFrameRGBavcodec_alloc_frame();if(pFrameRGBNULL) return -1; // Determine required buffer size and allocate buffernumBytesavpicture_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 AVPictureavpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx-width, pCodecCtx-height);// Read frames and save first five frames to disk########################################[4]########################################i0;while(av_read_frame(pFormatCtx, packet)0) { // Is this a packet from the video stream? if(packet.stream_indexvideoStream) { // 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(i5) SaveFrame(pFrameRGB, pCodecCtx-width, pCodecCtx-height, i); } } // Free the packet that was allocated by av_read_frame av_free_packet(packet);}// Free the RGB imageav_free(buffer);av_free(pFrameRGB);// Free the YUV frameav_free(pFrame);// Close the codecavcodec_close(pCodecCtx);// Close the video fileav_close_input_file(pFormatCtx);return 0;}
代码注释得很清楚没什么过多需要讲解的关于其中的什么YUV420RGBPPM等格式如果不理解麻烦还是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