FFMPEG学习----解码视频
基础概念
H.264编码的视频和MP3编码的音频,会相应的调用H.264解码器和MP3解码器,解码之后得到的就是原始的图像(YUV or RGB)和声音(PCM)数据,然后根据同步好的时间将图像显示到屏幕上,将声音输出到声卡,最终就是我们看到的视频。
解码流程
20 READ packet FROM video_stream INTO frame
30 IF frame NOT COMPLETE GOTO 20
40 DO SOMETHING WITH frame
50 GOTO 20
源码
- /*
- 视频截图
- */
-
- #include <stdio.h>
-
- extern "C"
- {
- #include <libavformat/avformat.h>
- #include <libavcodec/avcodec.h>
- #include <libswscale/swscale.h>
- #include <libavutil/imgutils.h>
- }
-
- void SaveFrame(AVFrame*, int, int, int);
-
- int main(int argc, char *argv[])
- {
- AVFormatContext *pFormatCtx = NULL;
- AVCodecContext *pCodecCtx = NULL;
- AVCodec *pCodec = NULL;
- AVPacket packet;
- AVFrame *pFrame = NULL, *pFrameRGB = NULL;
- unsigned char *buffer = NULL;
- struct SwsContext *img_convert_ctx = NULL;
- int i, VideoStream;
- int FrameFinished;
-
- char filename[32] = "Titanic.ts";
-
- /**
- * 注册所有文件格式和编解码器库
- * Initialize libavformat and register all the muxers, demuxers and
- * protocols. If you do not call this function, then you can select
- * exactly which formats you want to support.
- *
- * @see av_register_input_format()
- * @see av_register_output_format()
- */
- av_register_all();
-
- /**
- * pFormatCtx使用之前必须初始化
- * 1. pFormatCtx = NULL;
- * 或者:
- * 2. pFormatCtx = avformat_alloc_context();
- */
-
- /**
- * 打开视频文件,读取文件头信息(编解码器没有打开)
- * Open an input stream and read the header
- * 函数声明:int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
- * Open an input stream and read the header. The codecs are not opened.
- * The stream must be closed with avformat_close_input().
- *
- * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
- * May be a pointer to NULL, in which case an AVFormatContext is allocated by this
- * function and written into ps.
- * Note that a user-supplied AVFormatContext will be freed on failure.
- * @param url URL of the stream to open.
- * @param fmt If non-NULL, this parameter forces a specific input format.
- * Otherwise the format is autodetected.
- * @param options A dictionary filled with AVFormatContext and demuxer-private options.
- * On return this parameter will be destroyed and replaced with a dict containing
- * options that were not found. May be NULL.
- *
- * @return 0 on success, a negative AVERROR on failure.
- *
- * @note If you want to use custom IO, preallocate the format context and set its pb field.
- */
- if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0)
- {
- printf("Couldn't open an input stream.\n");
- return -1;
- }
-
- /**
- * 读取流信息
- * get stream information
- * 函数声明:int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
- * Read packets of a media file to get stream information. This
- * is useful for file formats with no headers such as MPEG. This
- * function also computes the real framerate in case of MPEG-2 repeat
- * frame mode.
- * The logical file position is not changed by this function;
- * examined packets may be buffered for later processing.
- *
- * @param ic media file handle
- * @param options If non-NULL, an ic.nb_streams long array of pointers to
- * dictionaries, where i-th member contains options for
- * codec corresponding to i-th stream.
- * On return each dictionary will be filled with options that were not found.
- * @return >=0 if OK, AVERROR_xxx on error
- *
- * @note this function isn't guaranteed to open all the codecs, so
- * options being non-empty at return is a perfectly normal behavior.
- *
- * @todo Let the user decide somehow what information is needed so that
- * we do not waste time getting stuff the user does not need.
- */
- if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
- {
- printf("Couldn't find stream information.\n");
- return -1;
- }
-
- /**
- * 打印输入视频文件信息
- * 函数声明:void av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output);
- * Print detailed information about the input or output format, such as
- * duration, bitrate, streams, container, programs, metadata, side data,
- * codec and time base.
- *
- * @param ic the context to analyze
- * @param index index of the stream to dump information about
- * -1表示ffmpeg自己选择
- * @param url the URL to print, such as source or destination file
- * @param is_output Select whether the specified context is an input(0) or output(1)
- **/
- av_dump_format(pFormatCtx, -1, filename, 0);
-
- //Find the first video stream
- VideoStream = -1;
- for (i = 0; i < pFormatCtx->nb_streams; i++)
- {
- if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
- {
- VideoStream = i;
- break;
- }
- }
- if (VideoStream == -1)
- {
- printf("Couldn't find a video stream.\n");
- return -1;
- }
-
- pCodecCtx = pFormatCtx->streams[VideoStream]->codec;
-
- /**
- * 函数声明:AVCodec *avcodec_find_decoder(enum AVCodecID id);
- * Find a registered decoder with a matching codec ID.
- *
- * @param id AVCodecID of the requested decoder
- * @return A decoder if one was found, NULL otherwise.
- */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
- if (pCodec == NULL)
- {
- printf("Codec not found.\n");
- return -1;
- }
-
- //open codec
- if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
- {
- printf("Could not open codec.\n");
- return -1;
- }
-
- /**
- * 函数声明:AVFrame *av_frame_alloc(void);
- * Allocate an AVFrame and set its fields to default values. The resulting
- * struct must be freed using av_frame_free().
- *
- * @return An AVFrame filled with default values or NULL on failure.
- *
- * @note this only allocates the AVFrame itself, not the data buffers. Those
- * must be allocated through other means, e.g. with av_frame_get_buffer() or
- * manually.
- */
-
- /**
- * 分配图像缓存
- * pFrame 用于存储解码后的数据
- * pFrameRGB 用于存储转换后的数据
- */
- pFrame = av_frame_alloc();
- pFrameRGB = av_frame_alloc();
- if (pFrame == NULL || pFrameRGB == NULL)
- {
- printf("memory allocation error\n");
- return -1;
- }
-
- /**
- * 函数声明:void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
- * Allocate a block of size bytes with alignment suitable for all
- * memory accesses (including vectors if available on the CPU).
- * @param size Size in bytes for the memory block to be allocated.
- * @return Pointer to the allocated block, NULL if the block cannot
- * be allocated.
- * @see av_mallocz()
- */
-
- /**
- * 函数声明:int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
- * Return the size in bytes of the amount of data required to store an
- * image with the given parameters.
- *
- * @param[in] align the assumed linesize alignment(按照多少字节对齐)
- */
-
- //计算 RGB24 格式的图像需要占用的空间大小,分配内存空间
- buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1));
-
- /**
- * 函数声明: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);
- * Setup the data pointers and linesizes based on the specified image
- * parameters and the provided array.
- *
- * The fields of the given image are filled in by using the src
- * address which points to the image data buffer. Depending on the
- * specified pixel format, one or multiple image data pointers and
- * line sizes will be set. If a planar format is specified, several
- * pointers will be set pointing to the different picture planes and
- * the line sizes of the different planes will be stored in the
- * lines_sizes array. Call with src == NULL to get the required
- * size for the src buffer.
- *
- * To allocate the buffer and fill in the dst_data and dst_linesize in
- * one call, use av_image_alloc().
- *
- * @param dst_data data pointers to be filled in
- * @param dst_linesizes linesizes for the image in dst_data to be filled in
- * @param src buffer which will contain or contains the actual image data, can be NULL
- * @param pix_fmt the pixel format of the image
- * @param width the width of the image in pixels
- * @param height the height of the image in pixels
- * @param align the value used in src for linesize alignment
- * @return the size in bytes required for src, a negative error code
- * in case of failure
- */
-
- //将 pFrameRGB 跟 buffer 指向的内存关联起来
- av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
-
- /**
- * 函数声明: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);
- *
- * Allocate and return an SwsContext. You need it to perform
- * scaling/conversion operations using sws_scale().
- *
- * @param srcW the width of the source image
- * @param srcH the height of the source image
- * @param srcFormat the source image format
- * @param dstW the width of the destination image
- * @param dstH the height of the destination image
- * @param dstFormat the destination image format
- * @param flags specify which algorithm and options to use for rescaling
- * @param param extra parameters to tune the used scaler
- * For SWS_BICUBIC param[0] and [1] tune the shape of the basis
- * function, param[0] tunes f(1) and param[1] f´(1)
- * For SWS_GAUSS param[0] tunes the exponent and thus cutoff
- * frequency
- * For SWS_LANCZOS param[0] tunes the width of the window function
- * @return a pointer to an allocated context, or NULL in case of error
- * @note this function is to be removed after a saner alternative is
- * written
- */
-
- //获得图像转换上下文
- img_convert_ctx = sws_getContext(pCodecCtx->width,
- pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
- pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,
- NULL, NULL, NULL);
-
- /**
- * 函数声明:int av_read_frame(AVFormatContext *s, AVPacket *pkt);
- * Return the next frame of a stream.
- * This function returns what is stored in the file, and does not validate
- * that what is there are valid frames for the decoder. It will split what is
- * stored in the file into frames and return one for each call. It will not
- * omit invalid data between valid frames so as to give the decoder the maximum
- * information possible for decoding.
- *
- * If pkt->buf is NULL, then the packet is valid until the next
- * av_read_frame() or until avformat_close_input(). Otherwise the packet
- * is valid indefinitely. In both cases the packet must be freed with
- * av_packet_unref when it is no longer needed. For video, the packet contains
- * exactly one frame. For audio, it contains an integer number of frames if each
- * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
- * have a variable size (e.g. MPEG audio), then it contains one frame.
- *
- * pkt->pts, pkt->dts and pkt->duration are always set to correct
- * values in AVStream.time_base units (and guessed if the format cannot
- * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
- * has B-frames, so it is better to rely on pkt->dts if you do not
- * decompress the payload.
- *
- * @return 0 if OK, < 0 on error or end of file
- */
-
- /**
- * 从文件中读取一个packet,对于视频来说一个packet里面包含一帧图像数据,音频可能包含多个帧(当音频帧长度固定时)
- * 读到这一帧后,如果是视频帧,则使用 avcodec_decode_video2 对packet中的帧进行解码
- * 有时候解码器并不能从一个packet中解码得到一帧图像数据(比如在需要其他参考帧的情况下),因此会设置 frameFinished
- * 如果已经得到下一帧图像则设置 frameFinished 非零,否则为零。所以这里我们判断 frameFinished 是否为零来确定 pFrame 中是否已经得到解码的图像
- * 注意在每次处理完后需要调用 av_free_packet 释放读取的packet
- */
- i = 0;
- while (av_read_frame(pFormatCtx, &packet) >= 0)
- {
- if (packet.stream_index == VideoStream)//获得的是视频帧
- {
- avcodec_decode_video2(pCodecCtx, pFrame, &FrameFinished, &packet);
- if (FrameFinished != 0)//获得的是一帧图像数据
- {
- //将图形从解码后的格式转换为 RGB24
- sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data,
- pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
- pFrameRGB->linesize);
- //将前50帧写人 ppm 图像文件
- if (i < 50)
- {
- SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
- i++;
- }
- }
- }
- av_free_packet(&packet);
- }
-
- //清理内存
- sws_freeContext(img_convert_ctx);
- av_free(buffer);
- av_frame_free(&pFrame);
- av_frame_free(&pFrameRGB);
- avcodec_close(pCodecCtx);
- avformat_close_input(&pFormatCtx);
-
- return 0;
- }
-
- static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
- {
- FILE *pFile;
- char szFilename[32];
- int y;
-
- sprintf(szFilename, "images\\frame%d.ppm", iFrame);
- pFile = fopen(szFilename, "wb");
- if (pFile == NULL)
- {
- printf("pFile is 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);
- }
-
- printf("images\\frame%d.ppm\n", iFrame);
-
- // Close file
- fclose(pFile);
- }
- /*
- 视频截图
- */
- #include <stdio.h>
- extern "C"
- {
- #include <libavformat/avformat.h>
- #include <libavcodec/avcodec.h>
- #include <libswscale/swscale.h>
- #include <libavutil/imgutils.h>
- }
- void SaveFrame(AVFrame*, int, int, int);
- int main(int argc, char *argv[])
- {
- AVFormatContext *pFormatCtx = NULL;
- AVCodecContext *pCodecCtx = NULL;
- AVCodec *pCodec = NULL;
- AVPacket packet;
- AVFrame *pFrame = NULL, *pFrameRGB = NULL;
- unsigned char *buffer = NULL;
- struct SwsContext *img_convert_ctx = NULL;
- int i, VideoStream;
- int FrameFinished;
- char filename[32] = "Titanic.ts";
- /**
- * 注册所有文件格式和编解码器库
- * Initialize libavformat and register all the muxers, demuxers and
- * protocols. If you do not call this function, then you can select
- * exactly which formats you want to support.
- *
- * @see av_register_input_format()
- * @see av_register_output_format()
- */
- av_register_all();
- /**
- * pFormatCtx使用之前必须初始化
- * 1. pFormatCtx = NULL;
- * 或者:
- * 2. pFormatCtx = avformat_alloc_context();
- */
- /**
- * 打开视频文件,读取文件头信息(编解码器没有打开)
- * Open an input stream and read the header
- * 函数声明:int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
- * Open an input stream and read the header. The codecs are not opened.
- * The stream must be closed with avformat_close_input().
- *
- * @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
- * May be a pointer to NULL, in which case an AVFormatContext is allocated by this
- * function and written into ps.
- * Note that a user-supplied AVFormatContext will be freed on failure.
- * @param url URL of the stream to open.
- * @param fmt If non-NULL, this parameter forces a specific input format.
- * Otherwise the format is autodetected.
- * @param options A dictionary filled with AVFormatContext and demuxer-private options.
- * On return this parameter will be destroyed and replaced with a dict containing
- * options that were not found. May be NULL.
- *
- * @return 0 on success, a negative AVERROR on failure.
- *
- * @note If you want to use custom IO, preallocate the format context and set its pb field.
- */
- if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0)
- {
- printf("Couldn't open an input stream.\n");
- return -1;
- }
- /**
- * 读取流信息
- * get stream information
- * 函数声明:int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
- * Read packets of a media file to get stream information. This
- * is useful for file formats with no headers such as MPEG. This
- * function also computes the real framerate in case of MPEG-2 repeat
- * frame mode.
- * The logical file position is not changed by this function;
- * examined packets may be buffered for later processing.
- *
- * @param ic media file handle
- * @param options If non-NULL, an ic.nb_streams long array of pointers to
- * dictionaries, where i-th member contains options for
- * codec corresponding to i-th stream.
- * On return each dictionary will be filled with options that were not found.
- * @return >=0 if OK, AVERROR_xxx on error
- *
- * @note this function isn't guaranteed to open all the codecs, so
- * options being non-empty at return is a perfectly normal behavior.
- *
- * @todo Let the user decide somehow what information is needed so that
- * we do not waste time getting stuff the user does not need.
- */
- if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
- {
- printf("Couldn't find stream information.\n");
- return -1;
- }
- /**
- * 打印输入视频文件信息
- * 函数声明:void av_dump_format(AVFormatContext *ic, int index, const char *url, int is_output);
- * Print detailed information about the input or output format, such as
- * duration, bitrate, streams, container, programs, metadata, side data,
- * codec and time base.
- *
- * @param ic the context to analyze
- * @param index index of the stream to dump information about
- * -1表示ffmpeg自己选择
- * @param url the URL to print, such as source or destination file
- * @param is_output Select whether the specified context is an input(0) or output(1)
- **/
- av_dump_format(pFormatCtx, -1, filename, 0);
- //Find the first video stream
- VideoStream = -1;
- for (i = 0; i < pFormatCtx->nb_streams; i++)
- {
- if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
- {
- VideoStream = i;
- break;
- }
- }
- if (VideoStream == -1)
- {
- printf("Couldn't find a video stream.\n");
- return -1;
- }
- pCodecCtx = pFormatCtx->streams[VideoStream]->codec;
- /**
- * 函数声明:AVCodec *avcodec_find_decoder(enum AVCodecID id);
- * Find a registered decoder with a matching codec ID.
- *
- * @param id AVCodecID of the requested decoder
- * @return A decoder if one was found, NULL otherwise.
- */
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
- if (pCodec == NULL)
- {
- printf("Codec not found.\n");
- return -1;
- }
- //open codec
- if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
- {
- printf("Could not open codec.\n");
- return -1;
- }
- /**
- * 函数声明:AVFrame *av_frame_alloc(void);
- * Allocate an AVFrame and set its fields to default values. The resulting
- * struct must be freed using av_frame_free().
- *
- * @return An AVFrame filled with default values or NULL on failure.
- *
- * @note this only allocates the AVFrame itself, not the data buffers. Those
- * must be allocated through other means, e.g. with av_frame_get_buffer() or
- * manually.
- */
- /**
- * 分配图像缓存
- * pFrame 用于存储解码后的数据
- * pFrameRGB 用于存储转换后的数据
- */
- pFrame = av_frame_alloc();
- pFrameRGB = av_frame_alloc();
- if (pFrame == NULL || pFrameRGB == NULL)
- {
- printf("memory allocation error\n");
- return -1;
- }
- /**
- * 函数声明:void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
- * Allocate a block of size bytes with alignment suitable for all
- * memory accesses (including vectors if available on the CPU).
- * @param size Size in bytes for the memory block to be allocated.
- * @return Pointer to the allocated block, NULL if the block cannot
- * be allocated.
- * @see av_mallocz()
- */
- /**
- * 函数声明:int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align);
- * Return the size in bytes of the amount of data required to store an
- * image with the given parameters.
- *
- * @param[in] align the assumed linesize alignment(按照多少字节对齐)
- */
- //计算 RGB24 格式的图像需要占用的空间大小,分配内存空间
- buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1));
- /**
- * 函数声明: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);
- * Setup the data pointers and linesizes based on the specified image
- * parameters and the provided array.
- *
- * The fields of the given image are filled in by using the src
- * address which points to the image data buffer. Depending on the
- * specified pixel format, one or multiple image data pointers and
- * line sizes will be set. If a planar format is specified, several
- * pointers will be set pointing to the different picture planes and
- * the line sizes of the different planes will be stored in the
- * lines_sizes array. Call with src == NULL to get the required
- * size for the src buffer.
- *
- * To allocate the buffer and fill in the dst_data and dst_linesize in
- * one call, use av_image_alloc().
- *
- * @param dst_data data pointers to be filled in
- * @param dst_linesizes linesizes for the image in dst_data to be filled in
- * @param src buffer which will contain or contains the actual image data, can be NULL
- * @param pix_fmt the pixel format of the image
- * @param width the width of the image in pixels
- * @param height the height of the image in pixels
- * @param align the value used in src for linesize alignment
- * @return the size in bytes required for src, a negative error code
- * in case of failure
- */
- //将 pFrameRGB 跟 buffer 指向的内存关联起来
- av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
- /**
- * 函数声明: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);
- *
- * Allocate and return an SwsContext. You need it to perform
- * scaling/conversion operations using sws_scale().
- *
- * @param srcW the width of the source image
- * @param srcH the height of the source image
- * @param srcFormat the source image format
- * @param dstW the width of the destination image
- * @param dstH the height of the destination image
- * @param dstFormat the destination image format
- * @param flags specify which algorithm and options to use for rescaling
- * @param param extra parameters to tune the used scaler
- * For SWS_BICUBIC param[0] and [1] tune the shape of the basis
- * function, param[0] tunes f(1) and param[1] f´(1)
- * For SWS_GAUSS param[0] tunes the exponent and thus cutoff
- * frequency
- * For SWS_LANCZOS param[0] tunes the width of the window function
- * @return a pointer to an allocated context, or NULL in case of error
- * @note this function is to be removed after a saner alternative is
- * written
- */
- //获得图像转换上下文
- img_convert_ctx = sws_getContext(pCodecCtx->width,
- pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width,
- pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR,
- NULL, NULL, NULL);
- /**
- * 函数声明:int av_read_frame(AVFormatContext *s, AVPacket *pkt);
- * Return the next frame of a stream.
- * This function returns what is stored in the file, and does not validate
- * that what is there are valid frames for the decoder. It will split what is
- * stored in the file into frames and return one for each call. It will not
- * omit invalid data between valid frames so as to give the decoder the maximum
- * information possible for decoding.
- *
- * If pkt->buf is NULL, then the packet is valid until the next
- * av_read_frame() or until avformat_close_input(). Otherwise the packet
- * is valid indefinitely. In both cases the packet must be freed with
- * av_packet_unref when it is no longer needed. For video, the packet contains
- * exactly one frame. For audio, it contains an integer number of frames if each
- * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
- * have a variable size (e.g. MPEG audio), then it contains one frame.
- *
- * pkt->pts, pkt->dts and pkt->duration are always set to correct
- * values in AVStream.time_base units (and guessed if the format cannot
- * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
- * has B-frames, so it is better to rely on pkt->dts if you do not
- * decompress the payload.
- *
- * @return 0 if OK, < 0 on error or end of file
- */
- /**
- * 从文件中读取一个packet,对于视频来说一个packet里面包含一帧图像数据,音频可能包含多个帧(当音频帧长度固定时)
- * 读到这一帧后,如果是视频帧,则使用 avcodec_decode_video2 对packet中的帧进行解码
- * 有时候解码器并不能从一个packet中解码得到一帧图像数据(比如在需要其他参考帧的情况下),因此会设置 frameFinished
- * 如果已经得到下一帧图像则设置 frameFinished 非零,否则为零。所以这里我们判断 frameFinished 是否为零来确定 pFrame 中是否已经得到解码的图像
- * 注意在每次处理完后需要调用 av_free_packet 释放读取的packet
- */
- i = 0;
- while (av_read_frame(pFormatCtx, &packet) >= 0)
- {
- if (packet.stream_index == VideoStream)//获得的是视频帧
- {
- avcodec_decode_video2(pCodecCtx, pFrame, &FrameFinished, &packet);
- if (FrameFinished != 0)//获得的是一帧图像数据
- {
- //将图形从解码后的格式转换为 RGB24
- sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data,
- pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
- pFrameRGB->linesize);
- //将前50帧写人 ppm 图像文件
- if (i < 50)
- {
- SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
- i++;
- }
- }
- }
- av_free_packet(&packet);
- }
- //清理内存
- sws_freeContext(img_convert_ctx);
- av_free(buffer);
- av_frame_free(&pFrame);
- av_frame_free(&pFrameRGB);
- avcodec_close(pCodecCtx);
- avformat_close_input(&pFormatCtx);
- return 0;
- }
- static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame)
- {
- FILE *pFile;
- char szFilename[32];
- int y;
- sprintf(szFilename, "images\\frame%d.ppm", iFrame);
- pFile = fopen(szFilename, "wb");
- if (pFile == NULL)
- {
- printf("pFile is 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);
- }
- printf("images\\frame%d.ppm\n", iFrame);
- // Close file
- fclose(pFile);
- }
对于packed格式的数据(例如RGB24),会存到data[0]里面。
对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]...(YUV420P中data[0]存Y,data[1]存U,data[2]存V)
- Input #-1, mpegts, from 'Titanic.ts':
- Duration: 00:00:48.03, start: 1.463400, bitrate: 589 kb/s
- Program 1
- Metadata:
- service_name : Service01
- service_provider: FFmpeg
- Stream #-1:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 6
- 40x272 [SAR 1:1 DAR 40:17], 23.98 fps, 23.98 tbr, 90k tbn
- Stream #-1:1[0x101]: Audio: mp3 ([3][0][0][0] / 0x0003), 48000 Hz, stereo, s
- 16p, 128 kb/s
- images\frame0.ppm
- images\frame1.ppm
- images\frame2.ppm
- images\frame3.ppm
- images\frame4.ppm
- images\frame5.ppm
- images\frame6.ppm
- images\frame7.ppm
- images\frame8.ppm
- images\frame9.ppm
- images\frame10.ppm
- images\frame11.ppm
- images\frame12.ppm
- images\frame13.ppm
- images\frame14.ppm
- images\frame15.ppm
- images\frame16.ppm
- images\frame17.ppm
- images\frame18.ppm
- images\frame19.ppm
- images\frame20.ppm
- images\frame21.ppm
- images\frame22.ppm
- images\frame23.ppm
- images\frame24.ppm
- images\frame25.ppm
- images\frame26.ppm
- images\frame27.ppm
- images\frame28.ppm
- images\frame29.ppm
- images\frame30.ppm
- images\frame31.ppm
- images\frame32.ppm
- images\frame33.ppm
- images\frame34.ppm
- images\frame35.ppm
- images\frame36.ppm
- images\frame37.ppm
- images\frame38.ppm
- images\frame39.ppm
- images\frame40.ppm
- images\frame41.ppm
- images\frame42.ppm
- images\frame43.ppm
- images\frame44.ppm
- images\frame45.ppm
- images\frame46.ppm
- images\frame47.ppm
- images\frame48.ppm
- images\frame49.ppm
- 请按任意键继续. . .
FFMPEG学习----解码视频的更多相关文章
- 【转】学习FFmpeg API – 解码视频
ffmpeg是编解码的利器,用了很久,以前看过dranger 的教程,非常精彩,受益颇多,是学习ffmpeg api很好的材料.可惜的是其针对的ffmpeg版本已经比较老了,而ffmpeg的更新又很快 ...
- 学习FFmpeg API – 解码视频
本文转载 视频播放过程 首先简单介绍以下视频文件的相关知识.我们平时看到的视频文件有许多格式,比如 avi, mkv, rmvb, mov, mp4等等,这些被称为容器(Container), 不同的 ...
- FFMPEG学习----打印视频信息
FFMPEG学习资料少之又少,在此推荐雷神的博客: http://blog.csdn.net/leixiaohua1020 在这里,我们把打印视频里的相关信息作为学习FFMPEG的 Hello Wor ...
- ffmpeg编解码视频导致噪声增大的一种解决方法
一.前言 ffmpeg在视音频编解码领域算是一个比较成熟的解决方案了.公司的一款视频编辑软件正是基于ffmpeg做了二次封装,并在此基础上进行音视频的编解码处理.然而,在观察编码后的视频质量时,发现图 ...
- 关于ffmpeg(libav)解码视频最后丢帧的问题
其实最初不是为了解决这个问题而来的,是Peter兄给我的提示解决另一个问题却让我误打误撞解决了另外一个问题之后也把这个隐藏了很久的bug找到(之前总是有一些特别短的视频产生不知所措还以为是视频素材本身 ...
- FFMPEG学习----分离视频里的H.264与YUV数据
#include <stdio.h> extern "C" { #include "libavcodec/avcodec.h" #include & ...
- FFmpeg学习2:解码数据结构及函数总结
在上一篇文章中,对FFmpeg的视频解码过程做了一个总结.由于才接触FFmpeg,还是挺陌生的,这里就解码过程再做一个总结. 本文的总结分为以下两个部分: 数据读取,主要关注在解码过程中所用到的FFm ...
- FFmpeg 学习(五):FFmpeg 编解码 API 分析
在上一篇文章 FFmpeg学习(四):FFmpeg API 介绍与通用 API 分析 中,我们简单的讲解了一下FFmpeg 的API基本概念,并分析了一下通用API,本文我们将分析 FFmpeg 在编 ...
- 【学习ffmpeg】打开视频文件,帧分析,并bmp保存关键帧
http://www.tuicool.com/articles/jiUzua http://blog.csdn.net/code_future/article/details/8646717 主题 ...
随机推荐
- Appium Mac系统 自动测试环境搭建
一.python 环境准备 Mac 自带 Python 环境,一般为 2.7 版本. 1.查看当前系统默认的Python路径 which python ==> /usr/bin/python 2 ...
- Java容器知识总结
剖析面试最常见问题之Java集合框架 说说List,Set,Map三者的区别? List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象 Set(注重独一 ...
- FTP服务器红帽5.4搭建图文教程!!!
FTP服务器搭建 服务器的环境 红帽5.4 vm15 挂载光盘 mount mount -t iso9660 设备目录 /mnt 表示挂载 软件包安装 FTP服务器安装包命令: rpm -ivh /m ...
- 小小知识点(二十一)如何修改PPT母版上无法直接点击修改的文字
1. 进入PPT后,选择下图右上角红色圈出的“视图”,接着选择下方红色圈出的“幻灯片母版”: 2.点击进入母版,如下图所示,最上面一栏第一个选项变成了“幻灯片母版”,在下面一栏最右边变成了“关闭母版视 ...
- 使用Eureka中遇到的一些问题
1.情况 : 服务已经注册到eureka,eureka中可以看到,但是 consumer和provider这两个服务,一直报错, 错误信息:DiscoveryClient_CONSUMER-DEM ...
- 竹马竹马chikuma
[问题描述] 众所周知,zzh 和 heyi 是一对竹马竹马,他们从小一起学 C++,最后都成了著名的神犇.而时间回溯到他们童年,这天 zzh 邀请 heyi 来参加 zzh 举行的男性家庭聚会. 而 ...
- C#实现的对文件的重命名
如下C#实现对文件的重命名的方法需要传入三个string类型的参数,分别是源文件的文件目录.目的文件目录和重命名的文件名称,实现代码如下: public ExecutionResult FileRen ...
- 【Java编程思想阅读笔记】Java数据存储位置
Java数据存储位置 P46页有感 一.前置知识 栈是由系统自动分配的,Java程序员对栈没有直接的操作权限, 堆是所有线程共享的内存区域,栈 是每个线程独享的. 堆是由程序员自己申请的,在使用new ...
- 使用远程接口库进一步扩展Robot Framework的测试能力
引言: Robot Framework的四层结构已经极大的提高了它的扩展性.我们可以使用它丰富的扩展库来完成大部分测试工作.可是碰到下面两种情况,仅靠四层结构就不好使了: 1.有些复杂的测试可能跨越多 ...
- pymysql总结
一.创建数据库 import pymysql conn = pymysql.connect(host='ip', user='root', password='密码') # 以字典的形式返回操作结果 ...