基础概念

我们平时看到的视频文件有许多格式,比如 avi, mkv, rmvb, mov, mp4等等,这些被称为容器(Container), 不同的容器格式规定了其中音视频数据的组织方式(也包括其他数据,比如字幕等)。容器中一般会封装有视频和音频轨,也称为视频流(stream)和音频 流,播放视频文件的第一步就是根据视频文件的格式,解析(demux)出其中封装的视频流、音频流以及字幕(如果有的话),解析的数据读到包 (packet)中,每个包里保存的是视频帧(frame)或音频帧,然后分别对视频帧和音频帧调用相应的解码器(decoder)进行解码,比如使用
H.264编码的视频和MP3编码的音频,会相应的调用H.264解码器和MP3解码器,解码之后得到的就是原始的图像(YUV or RGB)和声音(PCM)数据,然后根据同步好的时间将图像显示到屏幕上,将声音输出到声卡,最终就是我们看到的视频。

解码流程

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

源码
  1. /*
  2. 视频截图
  3. */
  4.  
  5. #include <stdio.h>
  6.  
  7. extern "C"
  8. {
  9. #include <libavformat/avformat.h>
  10. #include <libavcodec/avcodec.h>
  11. #include <libswscale/swscale.h>
  12. #include <libavutil/imgutils.h>
  13. }
  14.  
  15. void SaveFrame(AVFrame*, int, int, int);
  16.  
  17. int main(int argc, char *argv[])
  18. {
  19. AVFormatContext *pFormatCtx = NULL;
  20. AVCodecContext *pCodecCtx = NULL;
  21. AVCodec *pCodec = NULL;
  22. AVPacket packet;
  23. AVFrame *pFrame = NULL, *pFrameRGB = NULL;
  24. unsigned char *buffer = NULL;
  25. struct SwsContext *img_convert_ctx = NULL;
  26. int i, VideoStream;
  27. int FrameFinished;
  28.  
  29. char filename[32] = "Titanic.ts";
  30.  
  31. /**
  32. * 注册所有文件格式和编解码器库
  33. * Initialize libavformat and register all the muxers, demuxers and
  34. * protocols. If you do not call this function, then you can select
  35. * exactly which formats you want to support.
  36. *
  37. * @see av_register_input_format()
  38. * @see av_register_output_format()
  39. */
  40. av_register_all();
  41.  
  42. /**
  43. * pFormatCtx使用之前必须初始化
  44. * 1. pFormatCtx = NULL;
  45. * 或者:
  46. * 2. pFormatCtx = avformat_alloc_context();
  47. */
  48.  
  49. /**
  50. * 打开视频文件,读取文件头信息(编解码器没有打开)
  51. * Open an input stream and read the header
  52. * 函数声明:int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
  53. * Open an input stream and read the header. The codecs are not opened.
  54. * The stream must be closed with avformat_close_input().
  55. *
  56. * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
  57. * May be a pointer to NULL, in which case an AVFormatContext is allocated by this
  58. * function and written into ps.
  59. * Note that a user-supplied AVFormatContext will be freed on failure.
  60. * @param url URL of the stream to open.
  61. * @param fmt If non-NULL, this parameter forces a specific input format.
  62. * Otherwise the format is autodetected.
  63. * @param options A dictionary filled with AVFormatContext and demuxer-private options.
  64. * On return this parameter will be destroyed and replaced with a dict containing
  65. * options that were not found. May be NULL.
  66. *
  67. * @return 0 on success, a negative AVERROR on failure.
  68. *
  69. * @note If you want to use custom IO, preallocate the format context and set its pb field.
  70. */
  71. if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0)
  72. {
  73. printf("Couldn't open an input stream.\n");
  74. return -1;
  75. }
  76.  
  77. /**
  78. * 读取流信息
  79. * get stream information
  80. * 函数声明:int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
  81. * Read packets of a media file to get stream information. This
  82. * is useful for file formats with no headers such as MPEG. This
  83. * function also computes the real framerate in case of MPEG-2 repeat
  84. * frame mode.
  85. * The logical file position is not changed by this function;
  86. * examined packets may be buffered for later processing.
  87. *
  88. * @param ic media file handle
  89. * @param options If non-NULL, an ic.nb_streams long array of pointers to
  90. * dictionaries, where i-th member contains options for
  91. * codec corresponding to i-th stream.
  92. * On return each dictionary will be filled with options that were not found.
  93. * @return >=0 if OK, AVERROR_xxx on error
  94. *
  95. * @note this function isn't guaranteed to open all the codecs, so
  96. * options being non-empty at return is a perfectly normal behavior.
  97. *
  98. * @todo Let the user decide somehow what information is needed so that
  99. * we do not waste time getting stuff the user does not need.
  100. */
  101. if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
  102. {
  103. printf("Couldn't find stream information.\n");
  104. return -1;
  105. }
  106.  
  107. /**
  108. * 打印输入视频文件信息
  109. * 函数声明:void av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output);
  110. * Print detailed information about the input or output format, such as
  111. * duration, bitrate, streams, container, programs, metadata, side data,
  112. * codec and time base.
  113. *
  114. * @param ic the context to analyze
  115. * @param index index of the stream to dump information about
  116. * -1表示ffmpeg自己选择
  117. * @param url the URL to print, such as source or destination file
  118. * @param is_output Select whether the specified context is an input(0) or output(1)
  119. **/
  120. av_dump_format(pFormatCtx, -1, filename, 0);
  121.  
  122. //Find the first video stream
  123. VideoStream = -1;
  124. for (i = 0; i < pFormatCtx->nb_streams; i++)
  125. {
  126. if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
  127. {
  128. VideoStream = i;
  129. break;
  130. }
  131. }
  132. if (VideoStream == -1)
  133. {
  134. printf("Couldn't find a video stream.\n");
  135. return -1;
  136. }
  137.  
  138. pCodecCtx = pFormatCtx->streams[VideoStream]->codec;
  139.  
  140. /**
  141. * 函数声明:AVCodec *avcodec_find_decoder(enum AVCodecID id);
  142. * Find a registered decoder with a matching codec ID.
  143. *
  144. * @param id AVCodecID of the requested decoder
  145. * @return A decoder if one was found, NULL otherwise.
  146. */
  147. pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
  148. if (pCodec == NULL)
  149. {
  150. printf("Codec not found.\n");
  151. return -1;
  152. }
  153.  
  154. //open codec
  155. if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
  156. {
  157. printf("Could not open codec.\n");
  158. return -1;
  159. }
  160.  
  161. /**
  162. * 函数声明:AVFrame *av_frame_alloc(void);
  163. * Allocate an AVFrame and set its fields to default values. The resulting
  164. * struct must be freed using av_frame_free().
  165. *
  166. * @return An AVFrame filled with default values or NULL on failure.
  167. *
  168. * @note this only allocates the AVFrame itself, not the data buffers. Those
  169. * must be allocated through other means, e.g. with av_frame_get_buffer() or
  170. * manually.
  171. */
  172.  
  173. /**
  174. * 分配图像缓存
  175. * pFrame 用于存储解码后的数据
  176. * pFrameRGB 用于存储转换后的数据
  177. */
  178. pFrame = av_frame_alloc();
  179. pFrameRGB = av_frame_alloc();
  180. if (pFrame == NULL || pFrameRGB == NULL)
  181. {
  182. printf("memory allocation error\n");
  183. return -1;
  184. }
  185.  
  186. /**
  187. * 函数声明:void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
  188. * Allocate a block of size bytes with alignment suitable for all
  189. * memory accesses (including vectors if available on the CPU).
  190. * @param size Size in bytes for the memory block to be allocated.
  191. * @return Pointer to the allocated block, NULL if the block cannot
  192. * be allocated.
  193. * @see av_mallocz()
  194. */
  195.  
  196. /**
  197. * 函数声明:int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
  198. * Return the size in bytes of the amount of data required to store an
  199. * image with the given parameters.
  200. *
  201. * @param[in] align the assumed linesize alignment(按照多少字节对齐)
  202. */
  203.  
  204. //计算 RGB24 格式的图像需要占用的空间大小,分配内存空间
  205. buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1));
  206.  
  207. /**
  208. * 函数声明: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);
  209. * Setup the data pointers and linesizes based on the specified image
  210. * parameters and the provided array.
  211. *
  212. * The fields of the given image are filled in by using the src
  213. * address which points to the image data buffer. Depending on the
  214. * specified pixel format, one or multiple image data pointers and
  215. * line sizes will be set. If a planar format is specified, several
  216. * pointers will be set pointing to the different picture planes and
  217. * the line sizes of the different planes will be stored in the
  218. * lines_sizes array. Call with src == NULL to get the required
  219. * size for the src buffer.
  220. *
  221. * To allocate the buffer and fill in the dst_data and dst_linesize in
  222. * one call, use av_image_alloc().
  223. *
  224. * @param dst_data data pointers to be filled in
  225. * @param dst_linesizes linesizes for the image in dst_data to be filled in
  226. * @param src buffer which will contain or contains the actual image data, can be NULL
  227. * @param pix_fmt the pixel format of the image
  228. * @param width the width of the image in pixels
  229. * @param height the height of the image in pixels
  230. * @param align the value used in src for linesize alignment
  231. * @return the size in bytes required for src, a negative error code
  232. * in case of failure
  233. */
  234.  
  235. //将 pFrameRGB 跟 buffer 指向的内存关联起来
  236. av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
  237.  
  238. /**
  239. * 函数声明:struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
  240. * int dstW, int dstH, enum AVPixelFormat dstFormat,
  241. * int flags, SwsFilter *srcFilter,
  242. * SwsFilter *dstFilter, const double *param);
  243. *
  244. * Allocate and return an SwsContext. You need it to perform
  245. * scaling/conversion operations using sws_scale().
  246. *
  247. * @param srcW the width of the source image
  248. * @param srcH the height of the source image
  249. * @param srcFormat the source image format
  250. * @param dstW the width of the destination image
  251. * @param dstH the height of the destination image
  252. * @param dstFormat the destination image format
  253. * @param flags specify which algorithm and options to use for rescaling
  254. * @param param extra parameters to tune the used scaler
  255. * For SWS_BICUBIC param[0] and [1] tune the shape of the basis
  256. * function, param[0] tunes f(1) and param[1] f´(1)
  257. * For SWS_GAUSS param[0] tunes the exponent and thus cutoff
  258. * frequency
  259. * For SWS_LANCZOS param[0] tunes the width of the window function
  260. * @return a pointer to an allocated context, or NULL in case of error
  261. * @note this function is to be removed after a saner alternative is
  262. * written
  263. */
  264.  
  265. //获得图像转换上下文
  266. img_convert_ctx = sws_getContext(pCodecCtx->width,
  267. pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
  268. pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,
  269. NULL, NULL, NULL);
  270.  
  271. /**
  272. * 函数声明:int av_read_frame(AVFormatContext *s, AVPacket *pkt);
  273. * Return the next frame of a stream.
  274. * This function returns what is stored in the file, and does not validate
  275. * that what is there are valid frames for the decoder. It will split what is
  276. * stored in the file into frames and return one for each call. It will not
  277. * omit invalid data between valid frames so as to give the decoder the maximum
  278. * information possible for decoding.
  279. *
  280. * If pkt->buf is NULL, then the packet is valid until the next
  281. * av_read_frame() or until avformat_close_input(). Otherwise the packet
  282. * is valid indefinitely. In both cases the packet must be freed with
  283. * av_packet_unref when it is no longer needed. For video, the packet contains
  284. * exactly one frame. For audio, it contains an integer number of frames if each
  285. * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
  286. * have a variable size (e.g. MPEG audio), then it contains one frame.
  287. *
  288. * pkt->pts, pkt->dts and pkt->duration are always set to correct
  289. * values in AVStream.time_base units (and guessed if the format cannot
  290. * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
  291. * has B-frames, so it is better to rely on pkt->dts if you do not
  292. * decompress the payload.
  293. *
  294. * @return 0 if OK, < 0 on error or end of file
  295. */
  296.  
  297. /**
  298. * 从文件中读取一个packet,对于视频来说一个packet里面包含一帧图像数据,音频可能包含多个帧(当音频帧长度固定时)
  299. * 读到这一帧后,如果是视频帧,则使用 avcodec_decode_video2 对packet中的帧进行解码
  300. * 有时候解码器并不能从一个packet中解码得到一帧图像数据(比如在需要其他参考帧的情况下),因此会设置 frameFinished
  301. * 如果已经得到下一帧图像则设置 frameFinished 非零,否则为零。所以这里我们判断 frameFinished 是否为零来确定 pFrame 中是否已经得到解码的图像
  302. * 注意在每次处理完后需要调用 av_free_packet 释放读取的packet
  303. */
  304. i = 0;
  305. while (av_read_frame(pFormatCtx, &packet) >= 0)
  306. {
  307. if (packet.stream_index == VideoStream)//获得的是视频帧
  308. {
  309. avcodec_decode_video2(pCodecCtx, pFrame, &FrameFinished, &packet);
  310. if (FrameFinished != 0)//获得的是一帧图像数据
  311. {
  312. //将图形从解码后的格式转换为 RGB24
  313. sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data,
  314. pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
  315. pFrameRGB->linesize);
  316. //将前50帧写人 ppm 图像文件
  317. if (i < 50)
  318. {
  319. SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
  320. i++;
  321. }
  322. }
  323. }
  324. av_free_packet(&packet);
  325. }
  326.  
  327. //清理内存
  328. sws_freeContext(img_convert_ctx);
  329. av_free(buffer);
  330. av_frame_free(&pFrame);
  331. av_frame_free(&pFrameRGB);
  332. avcodec_close(pCodecCtx);
  333. avformat_close_input(&pFormatCtx);
  334.  
  335. return 0;
  336. }
  337.  
  338. static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
  339. {
  340. FILE *pFile;
  341. char szFilename[32];
  342. int y;
  343.  
  344. sprintf(szFilename, "images\\frame%d.ppm", iFrame);
  345. pFile = fopen(szFilename, "wb");
  346. if (pFile == NULL)
  347. {
  348. printf("pFile is null");
  349. return;
  350. }
  351.  
  352. // Write header
  353. fprintf(pFile, "P6\n%d %d\n255\n", width, height);
  354.  
  355. // Write pixel data
  356. for (y = 0; y < height; y++)
  357. {
  358. fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);
  359. }
  360.  
  361. printf("images\\frame%d.ppm\n", iFrame);
  362.  
  363. // Close file
  364. fclose(pFile);
  365. }

对于packed格式的数据(例如RGB24),会存到data[0]里面。

对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]...(YUV420P中data[0]存Y,data[1]存U,data[2]存V)

  1. Input #-1, mpegts, from 'Titanic.ts':
  2. Duration: 00:00:48.03, start: 1.463400, bitrate: 589 kb/s
  3. Program 1
  4. Metadata:
  5. service_name : Service01
  6. service_provider: FFmpeg
  7. Stream #-1:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 6
  8. 40x272 [SAR 1:1 DAR 40:17], 23.98 fps, 23.98 tbr, 90k tbn
  9. Stream #-1:1[0x101]: Audio: mp3 ([3][0][0][0] / 0x0003), 48000 Hz, stereo, s
  10. 16p, 128 kb/s
  11. images\frame0.ppm
  12. images\frame1.ppm
  13. images\frame2.ppm
  14. images\frame3.ppm
  15. images\frame4.ppm
  16. images\frame5.ppm
  17. images\frame6.ppm
  18. images\frame7.ppm
  19. images\frame8.ppm
  20. images\frame9.ppm
  21. images\frame10.ppm
  22. images\frame11.ppm
  23. images\frame12.ppm
  24. images\frame13.ppm
  25. images\frame14.ppm
  26. images\frame15.ppm
  27. images\frame16.ppm
  28. images\frame17.ppm
  29. images\frame18.ppm
  30. images\frame19.ppm
  31. images\frame20.ppm
  32. images\frame21.ppm
  33. images\frame22.ppm
  34. images\frame23.ppm
  35. images\frame24.ppm
  36. images\frame25.ppm
  37. images\frame26.ppm
  38. images\frame27.ppm
  39. images\frame28.ppm
  40. images\frame29.ppm
  41. images\frame30.ppm
  42. images\frame31.ppm
  43. images\frame32.ppm
  44. images\frame33.ppm
  45. images\frame34.ppm
  46. images\frame35.ppm
  47. images\frame36.ppm
  48. images\frame37.ppm
  49. images\frame38.ppm
  50. images\frame39.ppm
  51. images\frame40.ppm
  52. images\frame41.ppm
  53. images\frame42.ppm
  54. images\frame43.ppm
  55. images\frame44.ppm
  56. images\frame45.ppm
  57. images\frame46.ppm
  58. images\frame47.ppm
  59. images\frame48.ppm
  60. images\frame49.ppm
  61. 请按任意键继续. . .






FFMPEG学习----解码视频的更多相关文章

  1. 【转】学习FFmpeg API – 解码视频

    ffmpeg是编解码的利器,用了很久,以前看过dranger 的教程,非常精彩,受益颇多,是学习ffmpeg api很好的材料.可惜的是其针对的ffmpeg版本已经比较老了,而ffmpeg的更新又很快 ...

  2. 学习FFmpeg API – 解码视频

    本文转载 视频播放过程 首先简单介绍以下视频文件的相关知识.我们平时看到的视频文件有许多格式,比如 avi, mkv, rmvb, mov, mp4等等,这些被称为容器(Container), 不同的 ...

  3. FFMPEG学习----打印视频信息

    FFMPEG学习资料少之又少,在此推荐雷神的博客: http://blog.csdn.net/leixiaohua1020 在这里,我们把打印视频里的相关信息作为学习FFMPEG的 Hello Wor ...

  4. ffmpeg编解码视频导致噪声增大的一种解决方法

    一.前言 ffmpeg在视音频编解码领域算是一个比较成熟的解决方案了.公司的一款视频编辑软件正是基于ffmpeg做了二次封装,并在此基础上进行音视频的编解码处理.然而,在观察编码后的视频质量时,发现图 ...

  5. 关于ffmpeg(libav)解码视频最后丢帧的问题

    其实最初不是为了解决这个问题而来的,是Peter兄给我的提示解决另一个问题却让我误打误撞解决了另外一个问题之后也把这个隐藏了很久的bug找到(之前总是有一些特别短的视频产生不知所措还以为是视频素材本身 ...

  6. FFMPEG学习----分离视频里的H.264与YUV数据

    #include <stdio.h> extern "C" { #include "libavcodec/avcodec.h" #include & ...

  7. FFmpeg学习2:解码数据结构及函数总结

    在上一篇文章中,对FFmpeg的视频解码过程做了一个总结.由于才接触FFmpeg,还是挺陌生的,这里就解码过程再做一个总结. 本文的总结分为以下两个部分: 数据读取,主要关注在解码过程中所用到的FFm ...

  8. FFmpeg 学习(五):FFmpeg 编解码 API 分析

    在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...

  9. 【学习ffmpeg】打开视频文件,帧分析,并bmp保存关键帧

    http://www.tuicool.com/articles/jiUzua   http://blog.csdn.net/code_future/article/details/8646717 主题 ...

随机推荐

  1. Appium Mac系统 自动测试环境搭建

    一.python 环境准备 Mac 自带 Python 环境,一般为 2.7 版本. 1.查看当前系统默认的Python路径 which python ==> /usr/bin/python 2 ...

  2. Java容器知识总结

    剖析面试最常见问题之Java集合框架 说说List,Set,Map三者的区别? List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象 Set(注重独一 ...

  3. FTP服务器红帽5.4搭建图文教程!!!

    FTP服务器搭建 服务器的环境 红帽5.4 vm15 挂载光盘 mount mount -t iso9660 设备目录 /mnt 表示挂载 软件包安装 FTP服务器安装包命令: rpm -ivh /m ...

  4. 小小知识点(二十一)如何修改PPT母版上无法直接点击修改的文字

    1. 进入PPT后,选择下图右上角红色圈出的“视图”,接着选择下方红色圈出的“幻灯片母版”: 2.点击进入母版,如下图所示,最上面一栏第一个选项变成了“幻灯片母版”,在下面一栏最右边变成了“关闭母版视 ...

  5. 使用Eureka中遇到的一些问题

    1.情况 :   服务已经注册到eureka,eureka中可以看到,但是 consumer和provider这两个服务,一直报错, 错误信息:DiscoveryClient_CONSUMER-DEM ...

  6. 竹马竹马chikuma

    [问题描述] 众所周知,zzh 和 heyi 是一对竹马竹马,他们从小一起学 C++,最后都成了著名的神犇.而时间回溯到他们童年,这天 zzh 邀请 heyi 来参加 zzh 举行的男性家庭聚会. 而 ...

  7. C#实现的对文件的重命名

    如下C#实现对文件的重命名的方法需要传入三个string类型的参数,分别是源文件的文件目录.目的文件目录和重命名的文件名称,实现代码如下: public ExecutionResult FileRen ...

  8. 【Java编程思想阅读笔记】Java数据存储位置

    Java数据存储位置 P46页有感 一.前置知识 栈是由系统自动分配的,Java程序员对栈没有直接的操作权限, 堆是所有线程共享的内存区域,栈 是每个线程独享的. 堆是由程序员自己申请的,在使用new ...

  9. 使用远程接口库进一步扩展Robot Framework的测试能力

    引言: Robot Framework的四层结构已经极大的提高了它的扩展性.我们可以使用它丰富的扩展库来完成大部分测试工作.可是碰到下面两种情况,仅靠四层结构就不好使了: 1.有些复杂的测试可能跨越多 ...

  10. pymysql总结

    一.创建数据库 import pymysql conn = pymysql.connect(host='ip', user='root', password='密码') # 以字典的形式返回操作结果 ...