本文中的函数详细解析可参照:GO>> 我的博客:
FFmpeg部分函数解析
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
int dstwidth = 0; // 生成目标bmp的宽度
int dstheight = 0; // 生成目标bmp的高度
char bmp_header[54] = { 0 }; // bmp头文件的54个字节
void readHeader(const char* filename) // 随便用一个bmp,得到bmp文件的头54个字节,并设置宽高度
{
FILE* fp = fopen(filename, "r");
fread(bmp_header, 54, 1, fp);
*(uint32_t*)(bmp_header + 0x0012) = dstwidth; // 0x0012 指宽度在54个字节的位置
*(uint32_t*)(bmp_header + 0x0016) = dstheight;
fclose(fp);
}
void saveBMP(const char* filename, AVFrame* frame) // 将获取到的数据写入文件(bmp的图片)
{
if (NULL == filename || NULL == frame)
return;
FILE* fp = fopen(filename, "w");
readHeader("../images/t.bmp");
fwrite(bmp_header, 54, 1, fp);
fwrite(frame->data[0], dstwidth * dstheight * 3, 1, fp);
fclose(fp);
}
int main(int argc, char* argv[])
{
av_register_all(); // 为了偷懒,一次性注册所有的编码器
AVFormatContext *avfc = NULL;
const char* filename = "/home/tuser/test/test.mp4";
int ret = avformat_open_input(&avfc, filename, NULL, NULL); // 打开媒体,并得到上下文的结构
if (ret != 0)
{
printf("open input err!\n");
return 1;
}
ret = avformat_find_stream_info(avfc, NULL); // 读取音频数据并得到相关信息
if (ret != 0)
{
printf("find stream info err!\n");
return 2;
}
int i;
int audio_stream_info = -1;
int video_stream_info = -1;
for (i = 0; i < avfc->nb_streams; ++i) // 得到音频的句柄
{
AVStream *stream = avfc->streams[i];
if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
video_stream_info = i;
printf("video index stream is %d\n", i);
}
else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
audio_stream_info = i;
printf("audio index stream is %d\n", i);
}
else
{
printf("stream is %d, is not audio and video\n", stream->codecpar->codec_type);
}
}
if (video_stream_info == -1)
{
printf("not find video_stream_info\n");
return 3;
}
AVCodecContext *avcc = avfc->streams[video_stream_info]->codec;
avfc->video_codec = avcodec_find_decoder(avcc->codec_id); // 查找ffmpeg解码器,用于解码视频
avcodec_open2(avcc, avfc->video_codec, NULL); // 初始化音频编解码的AVCodecContext
dstwidth = avcc->width / 2; // 将宽度压缩
dstheight = avcc->height / 2;
// 转化为bmp格式
struct SwsContext* swc = sws_getContext(avcc->width, avcc->height, AV_PIX_FMT_YUV420P,
dstwidth, dstheight, AV_PIX_FMT_RGB24,
SWS_BICUBLIN, NULL, NULL, NULL);
AVPacket* packet = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
AVFrame* rgbFrame = av_frame_alloc();
uint8_t* data = (uint8_t*)malloc(dstwidth * dstheight * 3);
// 为已经分配的空间结构体AVPicture挂上一段用于保存数据的空间
avpicture_fill((AVPicture*)rgbFrame, data, AV_PIX_FMT_RGB24, dstwidth, dstheight);
while (1)
{
if (av_read_frame(avfc, packet) < 0) // 读取码流中的音频若干帧或者视频一帧
{
break;
}
if (packet->stream_index != video_stream_info)
{
printf("stream index is %d\n", packet->stream_index);
continue;
}
int got = 0;
avcodec_decode_video2(avcc, frame, &got, packet); // 解码一帧视频数据 packet-->frame
if (got == 0)
{
printf("frame is not decompress\n");
continue;
}
// 转换像素,(bmp的frame->data中只有第一行有用,所以srcSliceY为0, 不确定。。。)
sws_scale(swc, frame->data, frame->linesize, 0, avcc->height,
rgbFrame->data, rgbFrame->linesize);
// 写入bmp文件
static int fileIndex = 0;
fileIndex++;
char buf[1024] = { 0 };
sprintf(buf, "../images/%03d.bmp", fileIndex);
saveBMP(buf, rgbFrame);
}
av_packet_free(&packet);
av_frame_free(&frame);
av_frame_free(&rgbFrame);
avcodec_close(codecCtx);
avformat_close_input(&fc);
return 0;
}
Makefile:
all: a.out
a.out: main.c
gcc main.c -pthread -lavdevice -lavfilter -lswscale -lpostproc -lavformat -lavcodec -lxcb-xfixes -lxcb-render -lxcb-shape -lxcb -lX11 -lasound -lSDL -lx264 -lpthread -ldl -lfaac -lz -lswresample -lavutil -lm -g