0基础学习音视频编程技术(四) 使用FFMPEG解码视频之保存成图片
原文地址:http://blog.yundiantech.com/?log=blog&id=8
前面我们已经知道怎么使用FFMPEG了。
现在,开始着手使用FFMPEG吧:
首先来个简单的例子,使用FFMPEG打开视频文件,并解码保存成一张张的图片。
具体的步骤如下所示:
1.首先需要先初始化一下,使用如下函数:
1 |
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器 |
使用这个函数完成编码器和解码器的初始化,只有初始化了编码器和解码器才能正常使用,否则会在打开编解码器的时候失败。
2.接着需要分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行
1 |
AVFormatContext *pFormatCtx = avformat_alloc_context(); |
3.接着调用打开视频文件
这里文件名先不要使用中文,否则会打开失败,后期再讲解如何处理中文。
1 2 |
char *file_path = "E:in.mp4" ; avformat_open_input(&pFormatCtx, file_path, NULL, NULL); |
4.文件打开成功后就是查找文件中的视频流了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
///循环查找视频中包含的流信息,直到找到视频类型的流 ///便将其记录下来 保存到videoStream变量中 ///这里我们现在只处理视频流 音频流先不管他 for (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 ("Didn't find a video stream. "); return -1; } |
5.现在根据视频流 打开一个解码器来解码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
///查找解码器 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 ("Could not open codec. "); return -1; } |
可以看出 我们可以直接根据查找到的视频流信息获取到解码器。
而且我们并不知道他实际用的是什么编码器。
这就是为什么一开始我们使用FFMPEG来操作,因为很多东西我们可以不关系。
6.现在开始读取视频了:
1 2 3 4 5 6 7 8 |
int y_size = pCodecCtx->width * pCodecCtx->height; AVPacket *packet = (AVPacket *) malloc ( sizeof (AVPacket)); //分配一个packet av_new_packet(packet, y_size); //分配packet的数据
if (av_read_frame(pFormatCtx, packet) < 0) { break ; //这里认为视频读取完了 } |
可以看出 av_read_frame读取的是一帧视频,并存入一个AVPacket的结构中。
7.前面我们说过 视频里面的数据是经过编码压缩的,因此这里我们需要将其解码:
1 2 3 4 5 6 7 8 9 10 |
if (packet->stream_index == videoStream) { ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
if (ret < 0) { printf ("decode error. "); return -1; } } |
8.基本上所有解码器解码之后得到的图像数据都是YUV420的格式,而这里我们需要将其保存成图片文件,因此需要将得到的YUV420数据转换成RGB格式,转换格式也是直接使用FFMPEG来完成:
1 2 3 4 5 6 |
if (got_picture) { sws_scale(img_convert_ctx, (uint8_t const * const *) pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); } |
至于YUV420和RGB图像格式的具体内容,这里不用去了解。这里只需要知道有这么个东西就行了,对我们使用FFMPEG转换没有影响。
9.得到RGB数据之后就是直接写入文件了:
1 |
SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height,index++); //保存图片 if (index > 50) return 0; //这里我们就保存50张图片 |
至此读取视频解码保存成图片就写好了:
完整的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/pixfmt.h" #include "libswscale/swscale.h" }
#include <stdio.h>
///现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。 ///我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。 void SaveFrame(AVFrame *pFrame, int width, int height, int index) {
FILE *pFile; char szFilename[32]; int y;
// 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 pixel data for (y=0; y<height; y++) { fwrite (pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile); }
// Close file fclose (pFile);
}
int main( int argc, char *argv[]) { char *file_path = "E:in.mp4" ;
AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVFrame *pFrame, *pFrameRGB; AVPacket *packet; uint8_t *out_buffer;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes; int ret, got_picture;
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
//Allocate an AVFormatContext. pFormatCtx = avformat_alloc_context();
if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) { printf ("can't open the file. "); return -1; }
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { printf ("Could't find stream infomation. "); return -1; }
videoStream = -1;
///循环查找视频中包含的流信息,直到找到视频类型的流 ///便将其记录下来 保存到videoStream变量中 ///这里我们现在只处理视频流 音频流先不管他 for (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 ("Didn't 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 ("Could 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, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof (uint8_t)); avpicture_fill((AVPicture *) pFrameRGB, out_buffer, 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;
while (1) { if (av_read_frame(pFormatCtx, packet) < 0) { break ; //这里认为视频读取完了 }
if (packet->stream_index == videoStream) { ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
if (ret < 0) { printf ("decode error. "); return -1; }
if (got_picture) { sws_scale(img_convert_ctx, (uint8_t const * const *) pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height,index++); //保存图片 if (index > 50) return 0; //这里我们就保存50张图片 } } av_free_packet(packet); } av_free(out_buffer); av_free(pFrameRGB); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx);
return 0; } |
- 从零开始学习音视频编程技术(五) 使用FFMPEG解码视频之保存成图片
- 从零开始学习音视频编程技术(五) 使用FFMPEG解码视频之保存成图片
- 从零开始学习音视频编程技术(七) FFMPEG Qt视频播放器之SDL的使用
- 从零开始学习音视频编程技术(四) FFMPEG的使用
- 使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码
- FFmpeg基础库编程开发学习笔记——视频常见格式
- Android音视频学习第1章:使用ffmpeg进行视频解码
- ffmpeg将视频每帧画面保存为PPM格式图片,使用最新的ffmpeg官网15-7-2日更新的版本
- [原]零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构
- 从零开始学习音视频编程技术(六) FFMPEG Qt视频播放器之显示图像
- 从零开始学习音视频编程技术(八) FFMPEG Qt视频播放器之音视频同步
- Android音视频学习第2章:使用ffmpeg进行音频解码
- 从零开始学习音视频编程技术(六) FFMPEG Qt视频播放器之显示图像
- FFMPEG教程1_解码后保存图片(使用2014年新SDK重新整理编译通过)
- 零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构(转)
- 从零开始学习音视频编程技术(十一) FFMPEG Qt视频播放器之美化界面
- 从零开始学习音视频编程技术(九) FFMPEG Qt视频播放器之同步进阶篇
- [原]零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构
- [原]零基础学习视频解码之安装ffmpeg
- 使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码