FFmpeg 从视频中提取图片

从视频中提取图片,涉及到以下流程的处理 。

  • 打开输出文件,解封装,找到视频流索引
  • 通过视频context初始化decoder
  • 对视频帧进行解码
  • 初始化输出环境,新建输出流
  • 获取图片编码器
  • 对帧进行编码 并写到图片文件中

了解以上处理流程以后,对于代码的编写就会了然于胸。

https://github.com/kongxs/ffmpeg_demos/tree/master/toPics

1. 找到视频流 
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
	    char *file = "/Users/kongxiangshu/Desktop/1111.mp4";
char *outDir = "/Users/kongxiangshu/Desktop/pics";

AVFormatContext *in_context ;

in_context = avformat_alloc_context();

if (avformat_open_input(&in_context, file, NULL, NULL) < 0 ) {
LOGE("can not open file %s \n" , file);
exit(1);
}

if( avformat_find_stream_info(in_context, NULL) < 0 ) {
LOGE("can not find media info \n");
exit(1);
}

// av_dump_format(in_context, -1, file, 0);

int best_stream_index = -1;

if(best_stream_index =
av_find_best_stream(in_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0) < 0 ){
LOGE("can not find video stream \n ");
exit(1);
}

LOGE("best stream index : %d , \n" , best_stream_index);



for(int i = 0; i< in_context->nb_streams;i++) {

AVStream *stream = in_context->streams[i];

if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
best_stream_index = i;
}
}
2. 通过视频流 获取并打开解码器 
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
AVStream *beststream = in_context->streams[best_stream_index];

AVCodec *codec = avcodec_find_decoder(beststream->codecpar->codec_id);


if( codec == NULL) {

LOGE("can not find codec \n");
exit(1);
}

AVCodecContext *codec_context = avcodec_alloc_context3(codec);

if (codec_context == NULL) {
LOGE("can not alloc codec context \n ");
exit(1);
}

// param
if(avcodec_parameters_to_context(codec_context, beststream->codecpar) < 0){
LOGE("copy codec param error ");
exit(1);
}

if ( avcodec_open2(codec_context, codec, NULL) < 0 ) {
LOGE("can not open codec \n");
exit(1);
}
3. 解码frame
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
	while (av_read_frame(in_context, &pkt) >= 0) {

if (pkt.stream_index == best_stream_index) {

AVStream *stream = in_context->streams[pkt.stream_index] ;

if (stream == NULL) {
LOGE("error to get stream in read freme \n ");
exit(1);
}

// decode frame
if (avcodec_send_packet(codec_context, &pkt) < 0) {
LOGE("packet decode failed \n") ;
exit(1);
}

avcodec_receive_frame(codec_context, frame);

// ready to save pics
snprintf(buf, sizeof(buf), "%s/Demo-%d.jpg", outDir, frame_count);

// sprintf(name,"asdffasd");
if (frame_count % 25 == 0) {
savePics(frame,buf);
}


frame_count ++ ;

av_packet_unref(&pkt);
}
}
4. 编码并输出图片文件
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
	void savePics(AVFrame *frame, char *name) {

//对frame进行编码 ,并保存到文件中

printf("%s \n" , name);

AVFormatContext *out_context ;

out_context = avformat_alloc_context();


if ( avformat_alloc_output_context2(&out_context, NULL, NULL, name)) {
LOGE("输出环境初始化失败 \n ");
exit(1);
}


AVStream *stream = avformat_new_stream(out_context, 0) ;

if (stream == NULL) {
LOGE("scan not get new stream \n");
}


AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);

if (codec == NULL) {
LOGE("can not find jpeg codec");
exit(1);
}

AVCodecContext *avcodec_context = avcodec_alloc_context3(codec);

if (avcodec_context == NULL) {
LOGE("codec context failed ");
exit(1);
}

avcodec_context->width = frame->width;
avcodec_context->height = frame->height;
avcodec_context->time_base.num = 100;
avcodec_context->time_base.den = 1000;
avcodec_context->pix_fmt = AV_PIX_FMT_YUVJ420P;
avcodec_context->codec_id = codec->id;
avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;


avcodec_open2(avcodec_context, codec, NULL);



//writer heaer

avformat_write_header(out_context, NULL);



//编码帧
int ret = avcodec_send_frame(avcodec_context, frame);
if (ret < 0) {
LOGE("encode error \n");
exit(1);
}

int y_size = frame->width * frame->height;

//Encode
// 给AVPacket分配足够大的空间
AVPacket pkt;

av_new_packet(&pkt, y_size * 3);

ret = avcodec_receive_packet(avcodec_context, &pkt);

if (ret < 0) {
LOGE("receive frame error \n ");
exit(1);
}


av_write_frame(out_context, &pkt);


av_write_trailer(out_context);


av_packet_unref(&pkt);

avformat_free_context(out_context);

}