参考链接:http://blog.yundiantech.com/?log=blog&id=8
Windows10 + FFmpeg + VS2010 使用FFmpeg解码视频之保存图片
基于上一篇博文,配置好环境,并且小试牛刀之后,趁热打铁,再进行一个小工程。
打开VS2010,新建一个win32控制台应用程序,在debug64位下,进行编写。各种lib include dll的配置,参看上一篇博文。
功能内容:mp4的视频,截取视频流,保存成图片。代码如下:
#include "stdafx.h"
#include<iostream>
#include<stdio.h>using namespace std;extern "C"
{#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libavutil/pixfmt.h"#include "libswscale/swscale.h"
}// 定义SaveFrame函数,把RGB信息搞到一个PPM格式的文件中
// 生成一个简单的PPM格式的文件
void SaveFrame(AVFrame *pFrame, int width, int height, int index)
{FILE *pFile;char szFilename[32];// open filesprintf(szFilename, "frame%d.ppm", index);pFile = fopen(szFilename, "wb");if(pFile == NULL)return ;//write headerfprintf(pFile, "P6 %d %d 255", width, height);// write poxel datafor(int y = 0; y < height; ++y){fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile);}fclose(pFile);
}int _tmain(int argc, _TCHAR* argv[])
{char *file_path = "D:\\Tkinter14.mp4"; // 据说:打开的视频文件,不要有中文,否则会失败// 定义一些变量。AVFormatContext *pFormatCtx;AVCodecContext *pCodecCtx;AVCodec *pCodec;AVFrame *pFrame, *pFrameRGB;AVPacket *packet;uint8_t *out_buffer;static struct SwsContext *img_convert_ctx;av_register_all(); // 初始化FFMPEG, 调用了这个函数才能正常使用编码器和解码器pFormatCtx = avformat_alloc_context(); // 分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0){printf("Can not open the file.");return -1;}if (avformat_find_stream_info(pFormatCtx, NULL) < 0){printf("Can not find stream information.");return -1;}int videoStream;videoStream = -1;// 循环查找视频中包含的流信息,知道找到视频类型的流// 便将其记录下来,保存到videoStream变量中,// 这里我们现在只处理视频流,音频流先不管for(int i = 0; i < pFormatCtx->nb_streams; ++i){if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){videoStream = i;}}// 如果videoStream为-1,说明没有找到视频流if(videoStream == -1){printf("Can not find a video stream.");return -1;}// *** 根据视频流,打开一个解码器来解码// 查找解码器pCodecCtx = pFormatCtx->streams[videoStream]->codec;pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec == NULL){printf("Codec not found!");return -1;}// 打开解码器if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0){printf("Can not open codec.");return -1;}pFrame = av_frame_alloc(); // 为其开辟空间pFrameRGB = av_frame_alloc();img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL); // 原文件中,PIX_FMT_BGR24改为AV_PIX_FMT_BGR24,即可int numBytes;numBytes = avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);out_buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);// *** 开始读取视频int y_size = pCodecCtx->width * pCodecCtx->height;packet = (AVPacket*)malloc(sizeof(AVPacket)); // 分配一个packetav_new_packet(packet, y_size); // 分配packet的数据av_dump_format(pFormatCtx, 0, file_path, 0); // 输出视频信息int index = 0;int ret, got_picture;while(1){if(av_read_frame(pFormatCtx, packet) < 0){//return -1;break; // 读取完视频,退出.}// *** 对视频进行解码if(packet->stream_index == videoStream){ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if(ret < 0){printf("decode error.");return -1;}// *** 解码之后的图像数据都是YUV420格式,将其保存为图片文件,则需要将YUV420转换为RGB格式if(got_picture){sws_scale(img_convert_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);// *** 将得到的RGB数据 直接写入文件。SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, ++index); // 保存图片if (index > 20){return 0; // 先保存20张图片}}}av_free_packet(packet);}// 释放空间av_free(out_buffer);av_free(pFrameRGB);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;
}
进行测试代码时,每个return 语句处,打一个断点进行跟踪。
结果显示,无法找到文件。
使用下面这段代码,如截图中所示,添加,查看输出的错误是啥。
// 此段主要是检查错误码用。
char buf[] = "";
int err_code = avformat_open_input(&pFormatCtx, file_path, NULL, NULL);
av_strerror(err_code, buf, 1024);
printf("Can not open the file.%s: %d(%s)\n", file_path, err_code, buf);
错误显示,找不到文件。原来是我的文件路径写错了,少了一个转译符\
修改后,即可用。(记录此错误,方便自己回顾。原文中的代码,没有问题。)