参考链接: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 file
sprintf(szFilename, "frame%d.ppm", index);
pFile = fopen(szFilename, "wb");
if(pFile == NULL)
return ;
//write header
fprintf(pFile, "P6 %d %d 255", width, height);
// write poxel data
for(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)); // 分配一个packet
av_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);
错误显示,找不到文件。原来是我的文件路径写错了,少了一个转译符\
修改后,即可用。(记录此错误,方便自己回顾。原文中的代码,没有问题。)