ffmpeg 学习笔记1——读写文件
来源:互联网 发布:淘宝开企业店铺流程 编辑:程序博客网 时间:2024/05/22 17:42
http://www.guoyb.com/Tech/17.html
ffmpeg 学习笔记1——读写文件
作者:Usher 发布于:2012-4-12 14:38 Thursday 分类:技术杂记
参考:http://dranger.com/ffmpeg/tutorial01.html
0.基本概念
Container:视频文件本身;
Stream:数据流序列,一个文件中会有许多stream,例如一般都至少会有一个audio stream,一个video stream;
Frame:Stream中的数据单元叫做Frame;
Codec:每一个stream都被一种codec编码,codec是由code和decode两个单词合成,表示一个stream应该如何编码和解码;
Packet:从Stream中读出的数据单元是packet,我们可以从packet解码出frame,在ffmpeg中,一个packet可以包含完整的frame,也可以包含多个frame(对于audio stream来说)。
1.基本流程
10 OPEN video_stream FROM video.avi20 READ packet FROM video_stream INTO frame30 IF frame NOT COMPLETE GOTO 2040 DO SOMETHING WITH frame50 GOTO 20
2.打开文件
首先,应该包含相应的头文件,例如:
1
<span style=
"font-size:14px;"
>#include <libavcodec/avcodec.h>
2
#include <libavformat/avformat.h>
3
#include <libswscale/swscale.h></span>
1
<span style=
"font-size:14px;"
> av_register_all();</span>
来初始化ffmpeg库。
之后就可以打开视频文件了:
1
<span style=
"font-size:14px;"
>AVFormatContext *pFormatCtx;
2
if
(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
3
return
-1;
// Couldn't open file</span>
这样pFormatCtx就和相应的视频文件联系到了一起,存储了和视频文件格式相关的信息。pFormatCtx将是整个文件读写过程中很重要的一个结构。
这个函数只检查了视频文件的头部信息,所以接下来需要填充stream相关的结构:
1
<span style=
"font-size:14px;"
>
// Retrieve stream information
2
if
(av_find_stream_info(pFormatCtx)<0)
3
return
-1;
// Couldn't find stream information</span>
这样,pFormatCtx->streams就被初始化好了,接下来就要寻找video stream了:
01
<span style=
"font-size:14px;"
> videoStream=-1;
02
for
(i=0; i<pFormatCtx->nb_streams; i++)
03
if
(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
04
videoStream=i;
05
break
;
06
}
07
if
(videoStream==-1)
08
return
-1;
// Didn't find a video stream
09
10
// Get a pointer to the codec context for the video stream
11
pCodecCtx=pFormatCtx->streams[videoStream]->codec;</span>
在pCodecCtx中存储了该stream中所有有关Codec的相关信息,但是我们接下来仍然需要找到实际使用的decoder来解码:
1
<span style=
"font-size:14px;"
> pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
2
if
(pCodec==NULL) {
3
fprintf
(stderr,
"Unsupported codec!\n"
);
4
return
-1;
// Codec not found
5
}
6
// Open codec
7
if
(avcodec_open(pCodecCtx, pCodec)<0)
8
return
-1;
// Could not open codec</span>
3.为视频数据分配存储空间
接下来我们要为视频帧数据分配存储空间,包括解码得到的视频帧、格式转换得到的视频帧:
01
<span style=
"font-size:14px;"
>
// Allocate video frame
02
pFrame=avcodec_alloc_frame();
03
04
// Allocate an AVFrame structure
05
pFrameRGB=avcodec_alloc_frame();
06
if
(pFrameRGB==NULL)
07
return
-1;
08
09
// Determine required buffer size and allocate buffer
10
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
11
pCodecCtx->height);
12
buffer=(uint8_t *)av_malloc(numBytes*
sizeof
(uint8_t));
13
14
// Assign appropriate parts of buffer to image planes in pFrameRGB
15
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
16
// of AVPicture
17
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
18
pCodecCtx->width, pCodecCtx->height);</span>
其中avpicture_fill是将pFrameRGB和buffer这一内存空间关联起来。而pFrame的实际存储空间将由后面的视频解码函数分配。
4.读取数据
首先来看代码:
01
<span style=
"font-size:14px;"
>
// Read frames and save first five frames to disk
02
i=0;
03
while
(av_read_frame(pFormatCtx, &packet)>=0) {
04
// Is this a packet from the video stream?
05
if
(packet.stream_index==videoStream) {
06
// Decode video frame
07
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,
08
&packet);
09
10
// Did we get a video frame?
11
if
(frameFinished) {
12
// Convert the image from its native format to RGB
13
static
struct
SwsContext *img_convert_ctx;
14
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
15
sws_scale(img_convert_ctx, (
const
uint8_t*
const
*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
16
17
// Save the frame to disk
18
if
(++i<=5)
19
SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
20
i);
21
}
22
}
23
24
// Free the packet that was allocated by av_read_frame
25
av_free_packet(&packet);
26
}</span>
1
<span style=
"font-size:14px;"
> </span>
原来的例程中格式转换使用的是img_convert,在新版本的ffmpeg中已经被sws_scale替换掉了,如上述程序中所示。
另外,每个packet不一定可以解出完整的帧,所以需要设置一个frameFinished标志,ffmpeg会自己确保下一个packet与之前的packet联系起来。
接下来我们只需要完成SaveFrame函数,将转换得到的Frame存储成PPM的格式即可:
01
<span style=
"font-size:14px;"
>
void
SaveFrame(AVFrame *pFrame,
int
width,
int
height,
int
iFrame) {
02
FILE
*pFile;
03
char
szFilename[32];
04
int
y;
05
06
// Open file
07
sprintf
(szFilename,
"frame%d.ppm"
, iFrame);
08
pFile=
fopen
(szFilename,
"wb"
);
09
if
(pFile==NULL)
10
return
;
11
12
// Write header
13
fprintf
(pFile,
"P6\n%d %d\n255\n"
, width, height);
14
15
// Write pixel data
16
for
(y=0; y<height; y++)
17
fwrite
(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
18
19
// Close file
20
fclose
(pFile);
21
}</span>
01
<span style=
"font-size:14px;"
>
// Free the RGB image
02
av_free(buffer);
03
av_free(pFrameRGB);
04
05
// Free the YUV frame
06
av_free(pFrame);
07
08
// Close the codec
09
avcodec_close(pCodecCtx);
10
11
// Close the video file
12
av_close_input_file(pFormatCtx);</span>
001
<span style=
"font-size:14px;"
>
// tutorial01.c
002
// Code based on a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
003
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
004
005
// A small sample program that shows how to use libavformat and libavcodec to
006
// read video from a file.
007
//
008
// Use
009
//
010
// gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lz
011
//
012
// to build (assuming libavformat and libavcodec are correctly installed
013
// your system).
014
//
015
// Run using
016
//
017
// tutorial01 myvideofile.mpg
018
//
019
// to write the first five frames from "myvideofile.mpg" to disk in PPM
020
// format.
021
022
#include <libavcodec/avcodec.h>
023
#include <libavformat/avformat.h>
024
#include <libswscale/swscale.h>
025
026
#include <stdio.h>
027
028
void
SaveFrame(AVFrame *pFrame,
int
width,
int
height,
int
iFrame) {
029
FILE
*pFile;
030
char
szFilename[32];
031
int
y;
032
033
// Open file
034
sprintf
(szFilename,
"frame%d.ppm"
, iFrame);
035
pFile=
fopen
(szFilename,
"wb"
);
036
if
(pFile==NULL)
037
return
;
038
039
// Write header
040
fprintf
(pFile,
"P6\n%d %d\n255\n"
, width, height);
041
042
// Write pixel data
043
for
(y=0; y<height; y++)
044
fwrite
(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
045
046
// Close file
047
fclose
(pFile);
048
}
049
050
int
main(
int
argc,
char
*argv[]) {
051
AVFormatContext *pFormatCtx;
052
int
i, videoStream;
053
AVCodecContext *pCodecCtx;
054
AVCodec *pCodec;
055
AVFrame *pFrame;
056
AVFrame *pFrameRGB;
057
AVPacket packet;
058
int
frameFinished;
059
int
numBytes;
060
uint8_t *buffer;
061
062
if
(argc < 2) {
063
printf
(
"Please provide a movie file\n"
);
064
return
-1;
065
}
066
// Register all formats and codecs
067
av_register_all();
068
069
// Open video file
070
if
(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
071
return
-1;
// Couldn't open file
072
073
// Retrieve stream information
074
if
(av_find_stream_info(pFormatCtx)<0)
075
return
-1;
// Couldn't find stream information
076
077
// Dump information about file onto standard error
078
dump_format(pFormatCtx, 0, argv[1], 0);
079
080
// Find the first video stream
081
videoStream=-1;
082
for
(i=0; i<pFormatCtx->nb_streams; i++)
083
if
(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
084
videoStream=i;
085
break
;
086
}
087
if
(videoStream==-1)
088
return
-1;
// Didn't find a video stream
089
090
// Get a pointer to the codec context for the video stream
091
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
092
093
// Find the decoder for the video stream
094
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
095
if
(pCodec==NULL) {
096
fprintf
(stderr,
"Unsupported codec!\n"
);
097
return
-1;
// Codec not found
098
}
099
// Open codec
100
if
(avcodec_open(pCodecCtx, pCodec)<0)
101
return
-1;
// Could not open codec
102
103
// Allocate video frame
104
pFrame=avcodec_alloc_frame();
105
106
// Allocate an AVFrame structure
107
pFrameRGB=avcodec_alloc_frame();
108
if
(pFrameRGB==NULL)
109
return
-1;
110
111
// Determine required buffer size and allocate buffer
112
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
113
pCodecCtx->height);
114
buffer=(uint8_t *)av_malloc(numBytes*
sizeof
(uint8_t));
115
116
// Assign appropriate parts of buffer to image planes in pFrameRGB
117
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
118
// of AVPicture
119
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
120
pCodecCtx->width, pCodecCtx->height);
121
122
// Read frames and save first five frames to disk
123
i=0;
124
while
(av_read_frame(pFormatCtx, &packet)>=0) {
125
// Is this a packet from the video stream?
126
if
(packet.stream_index==videoStream) {
127
// Decode video frame
128
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,
129
&packet);
130
131
// Did we get a video frame?
132
if
(frameFinished) {
133
// Convert the image from its native format to RGB
134
static
struct
SwsContext *img_convert_ctx;
135
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
136
sws_scale(img_convert_ctx, (
const
uint8_t*
const
*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
137
138
// Save the frame to disk
139
if
(++i<=5)
140
SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
141
i);
142
}
143
}
144
145
// Free the packet that was allocated by av_read_frame
146
av_free_packet(&packet);
147
}
148
149
// Free the RGB image
150
av_free(buffer);
151
av_free(pFrameRGB);
152
153
// Free the YUV frame
154
av_free(pFrame);
155
156
// Close the codec
157
avcodec_close(pCodecCtx);
158
159
// Close the video file
160
av_close_input_file(pFormatCtx);
161
162
return
0;
163
}</span>
欢迎转载,转载请注明出处:http://ushertechblog.sinaapp.com/post-17.html
P.S.抛弃EMLOG,拥抱Github+hexo+markdown,博客再次搬家,这里(http:\\blog.guoyb.com)是新博客的域名。
- ffmpeg 学习笔记1——读写文件
- PHP学习笔记——文件读写
- Python学习笔记——文件读写
- 文件读写学习笔记
- 文件读写学习笔记
- 学习笔记---文件读写
- perl学习笔记八——文件读写
- <44>python学习笔记——读写csv文件
- Java学习笔记(三)——文件读写操作
- C#学习笔记——读写ini文件
- FFMpeg学习笔记(1)
- Python学习笔记--文件读写
- Lua学习笔记-文件读写
- python学习笔记 文件读写
- 11、ffmpeg学习笔记—ffmpeg源码编译-Android
- 读写文件1——以字节为单位,读写文件(笔记实例)
- ffmpeg学习笔记1--下载ffmpeg
- python 笔记 读写文件——12.26
- 深入理解Java注解(1):基础详解
- nand2tetris【1】
- 计算机网络(自动向下方法)学习笔记 1.4 计算机网络中的时延、丢包和吞吐量
- 利用一个form同时进行更新和添加的工作的设计思路是
- s2sh框架整合demo介绍-注解方式
- ffmpeg 学习笔记1——读写文件
- UVa 256 - Quirksome Squares
- 不考研,我这辈子会觉得无比遗憾的
- OpenGL研究3.0 多边形区域填充
- jquery mouseout 和mouseleave区别
- 程序员必须知道的10大基础实用算法及其讲解
- 嵌入式研发人员的核心竞争力浅谈
- random numbers (Expand a random range from 1–5 to 1–7)
- Java高级应用(一)-文件目录监控服务