系列索引:FFmpeg入门系列索引
上一篇:FFmpeg入门教程10.03:输出视频信息
FFmpeg使用较多的就是解码视频了,我们先进行正常的解码流程。
解码流程图为:
flowchart TB
F --Yes--> I
K --下一帧--> F
I --No--> F
subgraph init
direction TB
A(开始) --> B[打开文件]
B --> C[查找流信息]
C --> D[查找对应解码器]
D --> E[打开解码器]
E --> F{读取帧}
F --No--> G(结束)
end
subgraph decode
direction TB
I{是视频帧?} --Yes--> J[发送帧给解码器]
J --> K[从解码器获取结果]
end
flowchart TB
F --Yes--> I
K --Next Frame--> F
I --No--> F
subgraph init
direction TB
A(Start) --> B[avformat_open_input]
B --> C[av_find_stream_info]
C --> D[avcodec_find_decoder]
D --> E[avcodec_open]
E --> F{av_read_frame}
F --No--> G(End)
end
subgraph decode
direction TB
I{Video Packet?} --Yes--> J[avcodec_send_packet]
J --> K[avcodec_receive_frame]
end
测试代码如下:
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 #include <stdio.h> #include <stdlib.h> #include "libavcodec/avcodec.h" #include "libavfilter/avfilter.h" #include "libavformat/avformat.h" #include "libavutil/avutil.h" #include "libavutil/ffversion.h" #include "libswresample/swresample.h" #include "libswscale/swscale.h" #include "libpostproc/postprocess.h" int main () { char filePath[] = "beat.mp4" ; int videoStreamIndex = -1 ; int ret=0 ; AVFormatContext *fmtCtx=NULL ; AVPacket *pkt =NULL ; AVCodecContext *codecCtx=NULL ; AVCodecParameters *avCodecPara=NULL ; const AVCodec *codec=NULL ; do { fmtCtx = avformat_alloc_context (); if ((ret=avformat_open_input (&fmtCtx, filePath, NULL , NULL )) != 0 ) { printf ("cannot open video file\n" ); break ; } if ((ret=avformat_find_stream_info (fmtCtx, NULL )) < 0 ) { printf ("cannot retrive video info\n" ); break ; } for (unsigned int i = 0 ; i < fmtCtx->nb_streams; i++) { if (fmtCtx->streams[ i ]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break ; } } if (videoStreamIndex == -1 ) { printf ("cannot find video stream\n" ); break ; } av_dump_format (fmtCtx, 0 , filePath, 0 ); avCodecPara = fmtCtx->streams[ videoStreamIndex ]->codecpar; codec = avcodec_find_decoder (avCodecPara->codec_id); if (codec == NULL ) { printf ("cannot find decoder\n" ); break ; } codecCtx = avcodec_alloc_context3 (codec); avcodec_parameters_to_context (codecCtx, avCodecPara); if (codecCtx == NULL ) { printf ("Cannot alloc context." ); break ; } if ((ret=avcodec_open2 (codecCtx, codec, NULL )) < 0 ) { printf ("cannot open decoder\n" ); break ; } int i = 0 ; pkt = av_packet_alloc (); av_new_packet (pkt, codecCtx->width * codecCtx->height); while (av_read_frame (fmtCtx, pkt) >= 0 ) { if (pkt->stream_index==videoStreamIndex){ i++; } av_packet_unref (pkt); } printf ("There are %d frames int total.\n" , i); }while (0 ); av_packet_free (&pkt); avcodec_close (codecCtx); avformat_close_input (&fmtCtx); avformat_free_context (fmtCtx); return ret; }
编译执行,输出为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Input Metadata: major_brand : isom minor_version : 1 compatible_brands: isomavc1 creation_time : 2015-06-06T14:14:34.000000Z Duration: 00:06:32.89, start: 0.000000, bitrate: 1923 kb/s Stream Metadata: creation_time : 2015-06-06T14:14:34.000000Z Stream Metadata: creation_time : 2015-06-06T14:14:29.000000Z There are 9420 frames int total.%
根据输出,在视频流里面统计每一帧,共计9420帧。
视频共计6分32秒,帧率为24帧/秒,那么视频的视频帧数为:(6 * 60 + 32.89 ) * 23.98 = 9421.5022 。考虑到执行误差,两者非常接近,没有太大的错误。
代码开发要模块化,我们将部分流程合并,按照谷歌编码规范 ,一个函数最多40行,保持简洁。
但是开发才刚开始,并没有太多代码。
既然流程没有错,那么我们进行解码视频数据看看。
GitHub项目地址(源代码):ffmpeg_beginner 中的10.04.video_decode_flow
下一篇我们将解码视频前5帧并保存为图片。
下一篇:FFmpeg入门教程10.05:保存视频帧