我是靠谱客的博主 悲凉小白菜,这篇文章主要介绍FFmpeg4编程入门---视频篇1.ffmpeg介绍2. Hello World及输出版本号3.截取视频帧保存图片ppm4.解码视频文件(mp4)为yuv文件5.视频解码硬件加速6. 视频编码为h2647. h264封装为MP4文件8. 摄像头数据编码为h264,现在分享给大家,希望可以做个参考。

文章目录

  • 1.ffmpeg介绍
  • 2. Hello World及输出版本号
    • 2.1代码
    • 2.2 编译运行
  • 3.截取视频帧保存图片ppm
    • 3.1代码
    • 3.2 说明
  • 4.解码视频文件(mp4)为yuv文件
    • 4.1 代码
    • 4.2 说明
  • 5.视频解码硬件加速
    • 5.1代码
    • 5.2说明
  • 6. 视频编码为h264
    • 6.1 代码
    • 6.2 说明
  • 7. h264封装为MP4文件
    • 7.1 代码
    • 7.2 说明
  • 8. 摄像头数据编码为h264
    • 8.1 代码
    • 8.2 说明
    • Ref

1.ffmpeg介绍

项目地址:https://github.com/FFmpeg/FFmpeg
音视频处理工具,分为库和命令行工具两部分。
Libraries:

  • libavcodec provides implementation of a wider range of codecs.
  • libavformat implements streaming protocols, container formats and basic I/O access.
  • libavutil includes hashers, decompressors and miscellaneous utility functions.
  • libavfilter provides means to alter decoded audio and video through a directed graph of connected filters.
  • libavdevice provides an abstraction to access capture and playback devices.
  • libswresample implements audio mixing and resampling routines.
  • libswscale implements color conversion and scaling routines.

Tools:

  • ffmpeg is a command line toolbox to manipulate, convert and stream multimedia content.
  • ffplay is a minimalistic multimedia player.
  • ffprobe is a simple analysis tool to inspect multimedia content.
  • Additional small tools such as aviocat, ismindex and qt-faststart.

2. Hello World及输出版本号

2.1代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h> extern "C" { #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(int argc,char* argv[]) { av_log_set_level(AV_LOG_DEBUG); av_log(NULL,AV_LOG_INFO,"Hello ffmpeg:%s %sn",argv[0],argv[1]); unsigned codecVer = avcodec_version(); av_log(NULL,AV_LOG_INFO,"FFmpeg version is: %s, avcodec version is: %dn",FFMPEG_VERSION,codecVer); return 0; }

2.2 编译运行

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
% gcc main.cpp -lavutil -lavcodec -o a.out % ./a.out Hello ffmpeg:./a.out (null) Current ffmpeg version is: 4.2.2 ,avcodec version is: 3815012=58.54.100 % ffmpeg ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1) configuration: libavutil 56. 31.100 / 56. 31.100 libavcodec 58. 54.100 / 58. 54.100 libavformat 58. 29.100 / 58. 29.100 libavdevice 58. 8.100 / 58. 8.100 libavfilter 7. 57.100 / 7. 57.100 libswscale 5. 5.100 / 5. 5.100 libswresample 3. 5.100 / 3. 5.100 Hyper fast Audio and Video encoder usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

3.截取视频帧保存图片ppm

把视频中的某些帧保存为ppm格式的图片,其实就是直接保存rgb的字节内容。ffmpeg解码后得到的帧类型是yuv,所以需要帧类型转换。

3.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
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
#include <stdio.h> #include <stdlib.h> extern "C" { #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" #include "libavutil/imgutils.h" } //将FFmpeg解码后的数据保存到本地文件 void saveFrame(AVFrame* pFrame, int width, int height, int iFrame) { FILE* pFile; char szFilename[32]; int y; // 打开文件 sprintf(szFilename, "/home/chao/testffmpeg/frame%d.ppm", iFrame); pFile = fopen(szFilename, "wb"); if (pFile == NULL) return; // 写入文件头 fprintf(pFile, "P6n%d %dn255n", width, height); // 写入像素数据 for (y = 0; y < height; y++) fwrite(pFrame->data[0] + y * pFrame->linesize[0], 1, width * 3, pFile); // 关闭文件 fclose(pFile); } int main() { char filePath[] = "/home/chao/testffmpeg/19.mp4";//文件地址 int videoStreamIndex = -1;//视频流所在流序列中的索引 int ret = 0;//默认返回值 //需要的变量名并初始化 AVFormatContext* fmtCtx = NULL; AVPacket* pkt = NULL; AVCodecContext* codecCtx = NULL; AVCodecParameters* avCodecPara = NULL; AVCodec* codec = NULL; AVFrame* yuvFrame = av_frame_alloc(); AVFrame* rgbFrame = av_frame_alloc(); do { //=========================== 创建AVFormatContext结构体 ===============================// //分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行 fmtCtx = avformat_alloc_context(); //==================================== 打开文件 ======================================// if ((ret = avformat_open_input(&fmtCtx, filePath, NULL, NULL)) != 0) { printf("cannot open video filen"); break; } //=================================== 获取视频流信息 ===================================// if ((ret = avformat_find_stream_info(fmtCtx, NULL)) < 0) { printf("cannot retrive video infon"); break; } //循环查找视频中包含的流信息,直到找到视频类型的流 //便将其记录下来 保存到videoStreamIndex变量中 for (unsigned int i = 0; i < fmtCtx->nb_streams; i++) { if (fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break;//找到视频流就退出 } } //如果videoStream为-1 说明没有找到视频流 if (videoStreamIndex == -1) { printf("cannot find video streamn"); 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 decodern"); 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) { // 具体采用什么解码器ffmpeg经过封装 我们无须知道 printf("cannot open decodern"); break; } //================================ 设置数据转换参数 ================================// struct SwsContext* img_ctx = sws_getContext( codecCtx->width, codecCtx->height, codecCtx->pix_fmt, //源地址长宽以及数据格式 codecCtx->width, codecCtx->height, AV_PIX_FMT_RGB32, //目的地址长宽以及数据格式 SWS_BICUBIC, NULL, NULL, NULL); //算法类型 AV_PIX_FMT_YUVJ420P AV_PIX_FMT_BGR24 //==================================== 分配空间 ==================================// //一帧图像数据大小 int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, codecCtx->width, codecCtx->height, 1); unsigned char* out_buffer = (unsigned char*)av_malloc(numBytes * sizeof(unsigned char)); //=========================== 分配AVPacket结构体 ===============================// int i = 0;//用于帧计数 pkt = av_packet_alloc(); //分配一个packet av_new_packet(pkt, codecCtx->width * codecCtx->height); //调整packet的数据 //会将pFrameRGB的数据按RGB格式自动"关联"到buffer 即pFrameRGB中的数据改变了 //out_buffer中的数据也会相应的改变 av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, out_buffer, AV_PIX_FMT_RGB32, codecCtx->width, codecCtx->height, 1); //=========================== 读取视频信息 ===============================// while (av_read_frame(fmtCtx, pkt) >= 0) { //读取的是一帧视频 数据存入一个AVPacket的结构中 //printf("ptk->pts:%ldt", pkt->pts); if (pkt->stream_index == videoStreamIndex) { printf("video ptk->pts:%ldt", pkt->pts); if (avcodec_send_packet(codecCtx, pkt) == 0) { while (avcodec_receive_frame(codecCtx, yuvFrame) == 0) { if (++i <= 500 && i >= 200) { sws_scale(img_ctx, (const uint8_t* const*)yuvFrame->data, yuvFrame->linesize, 0, codecCtx->height, rgbFrame->data, rgbFrame->linesize); saveFrame(rgbFrame, codecCtx->width, codecCtx->height, i); } } } } av_packet_unref(pkt);//重置pkt的内容 } printf("There are %d frames int total.n", i); } while (0); //===========================释放所有指针===============================// av_packet_free(&pkt); avcodec_close(codecCtx); //avcodec_parameters_free(&avCodecPara); avformat_close_input(&fmtCtx); avformat_free_context(fmtCtx); av_frame_free(&yuvFrame); av_frame_free(&rgbFrame); return ret; }

3.2 说明

  • 一次avcodec_send_packet后会调用多次avcodec_receive_frame,这是由于视频的编码造成的,比如B帧的存在,ffmpeg的解码器需要积攒后边的多帧后才能解码出B帧。
  • avcodec_receive_frame的第二个参数为返回的解码帧,一般类型为YUV420P, 因为要保存PPM文件,而PPM文件中的内容为RBG的像素值,因此需要调用sws_scale进行帧格式的转换。

4.解码视频文件(mp4)为yuv文件

yuv文件就是完全未经压缩的视频文件,文件大小会比较大。ffmpeg解码后的视频帧是yuv420格式,保存为yuv视频无需格式转换,但是保存的时候需要分别保存y,u,v三路数据。需要理解yuv420的格式定义。

4.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
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
#include <iostream> #include <stdio.h> #include <stdlib.h> extern "C" { #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" #include "libavutil/imgutils.h" } int main() { FILE* fp = fopen("/mnt/d/result.yuv", "w+b"); if (fp == NULL) { printf("Cannot open file.n"); return -1; } char filePath[] = "/home/chao/testffmpeg/19.mp4";//文件地址 int videoStreamIndex = -1;//视频流所在流序列中的索引 int ret = 0;//默认返回值 //需要的变量名并初始化 AVFormatContext* fmtCtx = NULL; AVPacket* pkt = NULL; AVCodecContext* codecCtx = NULL; AVCodecParameters* avCodecPara = NULL; AVCodec* codec = NULL; AVFrame* yuvFrame = av_frame_alloc(); do { //=========================== 创建AVFormatContext结构体 ===============================// //分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行 fmtCtx = avformat_alloc_context(); //==================================== 打开文件 ======================================// if ((ret = avformat_open_input(&fmtCtx, filePath, NULL, NULL)) != 0) { printf("cannot open video filen"); break; } //=================================== 获取视频流信息 ===================================// if ((ret = avformat_find_stream_info(fmtCtx, NULL)) < 0) { printf("cannot retrive video infon"); break; } //循环查找视频中包含的流信息,直到找到视频类型的流 //便将其记录下来 保存到videoStreamIndex变量中 for (unsigned int i = 0; i < fmtCtx->nb_streams; i++) { if (fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break;//找到视频流就退出 } } //如果videoStream为-1 说明没有找到视频流 if (videoStreamIndex == -1) { printf("cannot find video streamn"); 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 decodern"); 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) { // 具体采用什么解码器ffmpeg经过封装 我们无须知道 printf("cannot open decodern"); break; } int w = codecCtx->width;//视频宽度 int h = codecCtx->height;//视频高度 //=========================== 分配AVPacket结构体 ===============================// pkt = av_packet_alloc(); //分配一个packet av_new_packet(pkt, codecCtx->width * codecCtx->height); //调整packet的数据 //=========================== 读取视频信息 ===============================// while (av_read_frame(fmtCtx, pkt) >= 0) { //读取的是一帧视频 数据存入一个AVPacket的结构中 if (pkt->stream_index == videoStreamIndex) { if (avcodec_send_packet(codecCtx, pkt) == 0) { while (avcodec_receive_frame(codecCtx, yuvFrame) == 0) { fwrite(yuvFrame->data[0], 1, w * h, fp);//y fwrite(yuvFrame->data[1], 1, w * h / 4, fp);//u fwrite(yuvFrame->data[2], 1, w * h / 4, fp);//v /*for (int i = 0; i < yuvFrame->height; i++) { //out.write((char*)(yuvFrame->data[0] + i * yuvFrame->linesize[0]), yuvFrame->width); fwrite(yuvFrame->data[0] + i * yuvFrame->linesize[0], 1, yuvFrame->width, fp); } int loop = yuvFrame->height / 2; int len_uv = yuvFrame->width / 2; for (int i = 0; i < loop; i++) { //out.write((char*)(yuvFrame->data[1] + i * yuvFrame->linesize[1]), len_uv); fwrite(yuvFrame->data[1] + i * yuvFrame->linesize[1], 1, len_uv, fp); } for (int i = 0; i < loop; i++) { //out.write((char*)(yuvFrame->data[2] + i * yuvFrame->linesize[2]), len_uv); fwrite(yuvFrame->data[2] + i * yuvFrame->linesize[2], 1, len_uv, fp); } */ } } } av_packet_unref(pkt);//重置pkt的内容 } } while (0); //===========================释放所有指针===============================// av_packet_free(&pkt); avcodec_close(codecCtx); avcodec_parameters_free(&avCodecPara); //avformat_close_input(&fmtCtx); //avformat_free_context(fmtCtx); av_frame_free(&yuvFrame); return ret; }

4.2 说明

  • YUV420格式的说明见《图像格式RGB/HSV/YUV》,《YUV格式简介、YUV444、YUV422、YUV420》,领会了YUV420的格式,才能明白代码中fwrite(yuvFrame->data[1], 1, w * h / 4, fp)的含义。
  • 如何播放YUV视频,可以使用ffplay,也可以使用vlc,如果使用vlc播放,需要点击媒体 -> 高级打开 -> 显示更多选项,在编辑选项中加入demux=rawvideo :rawvid-width=1920 :rawvid-height=1080 :rawvid-chroma=I420 :rawvid-fps=30,以此来指定视频的画面大小,帧格式,帧率信息。 其中的rawvid-chroma可选项见Chroma

5.视频解码硬件加速

使用cuda进行视频解码的加速。需要有nvida的显卡,还需安装开发包,还需ffmpeg编译为支持cuda的版本。cuda对速度的提升比较明显。

5.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
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#include <stdio.h> #include <stdlib.h> extern "C" { #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" #include "libavutil/imgutils.h" #include "libavutil/hwcontext.h" #include "libavutil/pixdesc.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" } static enum AVPixelFormat hw_pix_fmt; static AVBufferRef* hw_device_ctx = NULL; AVPixelFormat get_hw_format(AVCodecContext* ctx, const AVPixelFormat* pix_fmts) { const enum AVPixelFormat* p; for (p = pix_fmts; *p != -1; p++) { if (*p == hw_pix_fmt) return *p; } fprintf(stderr, "Failed to get HW surface format.n"); return AV_PIX_FMT_NONE; } static int hw_decoder_init(AVCodecContext* ctx, const enum AVHWDeviceType type) { int err = 0; if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type, NULL, NULL, 0)) < 0) { fprintf(stderr, "Failed to create specified HW device.n"); return err; } ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); return err; } int main() { //=========================== 查找所有硬件=================================================// AVHWDeviceType type_ = AV_HWDEVICE_TYPE_NONE; while ((type_ = av_hwdevice_iterate_types(type_)) != AV_HWDEVICE_TYPE_NONE) fprintf(stderr, "found: %sn", av_hwdevice_get_type_name(type_)); //=========================== 通过名称查找硬解码类型是否存在 ===============================// AVHWDeviceType type = av_hwdevice_find_type_by_name("cuda"); if (type == AV_HWDEVICE_TYPE_NONE) { fprintf(stderr, "Device type %s is not supported.n", "h264_cuvid"); fprintf(stderr, "Available device types:"); while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) fprintf(stderr, " %s", av_hwdevice_get_type_name(type)); fprintf(stderr, "n"); return -1; } printf("found cuda!!n"); FILE* fp = fopen("/mnt/d/result.yuv", "w+b"); if (fp == NULL) { printf("Cannot open file.n"); return -1; } char filePath[] = "/home/chao/testffmpeg/19.mp4";//文件地址 int videoStreamIndex = -1;//视频流所在流序列中的索引 int ret = 0;//默认返回值 //需要的变量名并初始化 AVFormatContext* fmtCtx = NULL; AVCodec* videoCodec = NULL; AVCodecContext* videoCodecCtx = NULL; AVPacket* pkt = NULL; AVFrame* yuvFrame = NULL; AVFrame* rgbFrame = NULL; AVFrame* nv12Frame = NULL; AVStream* videoStream = NULL; AVBufferRef* hw_device_ctx = NULL; unsigned char* out_buffer; struct SwsContext* img_ctx = NULL; fmtCtx = avformat_alloc_context(); pkt = av_packet_alloc(); yuvFrame = av_frame_alloc(); rgbFrame = av_frame_alloc(); nv12Frame = av_frame_alloc(); /* open the input file */ if (avformat_open_input(&fmtCtx, filePath, NULL, NULL) != 0) { printf("open input mp4 file failedn"); return -1; } if (avformat_find_stream_info(fmtCtx, NULL) < 0) { fprintf(stderr, "Cannot find input stream information.n"); return -1; } /* find the video stream information */ ret = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &videoCodec, 0); if (ret < 0) { fprintf(stderr, "Cannot find a video stream in the input filen"); return -1; } videoStreamIndex = ret; //获取支持该decoder的hw配置型 for (int i = 0;; i++) { const AVCodecHWConfig* config = avcodec_get_hw_config(videoCodec, i); if (!config) { fprintf(stderr, "Decoder %s does not support device type %s.n", videoCodec->name, av_hwdevice_get_type_name(type)); return -1; } if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { hw_pix_fmt = config->pix_fmt; break; } } if (!(videoCodecCtx = avcodec_alloc_context3(videoCodec))) return AVERROR(ENOMEM); videoStream = fmtCtx->streams[videoStreamIndex]; if (avcodec_parameters_to_context(videoCodecCtx, videoStream->codecpar) < 0) return -1; videoCodecCtx->get_format = get_hw_format; if (hw_decoder_init(videoCodecCtx, type) < 0) return -1; if ((ret = avcodec_open2(videoCodecCtx, videoCodec, NULL)) < 0) { fprintf(stderr, "Failed to open codec for stream #%un", videoStreamIndex); return -1; } if ((ret = av_hwdevice_ctx_create(&hw_device_ctx, type, NULL, NULL, 0)) < 0) { fprintf(stderr, "Failed to create specified HW device.n"); return ret; } videoCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx); //if (hw_decoder_init(videoCodecCtx, type) < 0) // return -1; if ((ret = avcodec_open2(videoCodecCtx, videoCodec, NULL)) < 0) { fprintf(stderr, "Failed to open codec for stream #%un", videoStreamIndex); return -1; } img_ctx = sws_getContext(videoCodecCtx->width, videoCodecCtx->height, AV_PIX_FMT_NV12, videoCodecCtx->width, videoCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); unsigned numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, videoCodecCtx->width, videoCodecCtx->height, 1); out_buffer = (unsigned char*)av_malloc(numBytes * sizeof(unsigned char)); int res = av_image_fill_arrays( rgbFrame->data, rgbFrame->linesize, out_buffer, AV_PIX_FMT_RGB32, videoCodecCtx->width, videoCodecCtx->height, 1); if (res < 0) { printf("failed array filln"); return -1; } int w = videoCodecCtx->width;//视频宽度 int h = videoCodecCtx->height;//视频高度 while (av_read_frame(fmtCtx, pkt) >= 0) { if (pkt->stream_index == videoStreamIndex) { if (avcodec_send_packet(videoCodecCtx, pkt) >= 0) { int ret; while ((ret = avcodec_receive_frame(videoCodecCtx, yuvFrame)) >= 0) { if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return -1; else if (ret < 0) { fprintf(stderr, "Error during decodingn"); exit(1); } if (yuvFrame->format == videoCodecCtx->pix_fmt) { if ((ret = av_hwframe_transfer_data(nv12Frame, yuvFrame, 0)) < 0) { continue; } } //sws_scale(img_ctx, // (const uint8_t* const*)nv12Frame->data, // (const int*)nv12Frame->linesize, // 0, // nv12Frame->height, // rgbFrame->data, rgbFrame->linesize); fwrite(yuvFrame->data[0], 1, w * h, fp);//y fwrite(yuvFrame->data[1], 1, w * h / 4, fp);//u fwrite(yuvFrame->data[2], 1, w * h / 4, fp);//v } } av_packet_unref(pkt); } } if (!pkt) av_packet_free(&pkt); if (!yuvFrame) av_frame_free(&yuvFrame); if (!rgbFrame) av_frame_free(&rgbFrame); if (!nv12Frame) av_frame_free(&nv12Frame); if (!videoCodecCtx) avcodec_free_context(&videoCodecCtx); if (!videoCodecCtx) avcodec_close(videoCodecCtx); if (!fmtCtx) avformat_close_input(&fmtCtx); return 0; }

5.2说明

  • 关于硬件加速的官方示例见hw_decode.c
  • 查看ffmpeg支持的硬件加速选项ffmpeg -hwaccels
  • 网上资料看到速度可以提升3-4倍左右

6. 视频编码为h264

也就是把yuv的文件编码为h264的文件,文件会大幅度压缩。

6.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
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <iostream> #include <stdio.h> #include <stdlib.h> extern "C" { #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" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libavutil/channel_layout.h" #include "libavutil/pixdesc.h" } int main() { AVFormatContext* fmtCtx = NULL; AVOutputFormat* outFmt = NULL; AVStream* vStream = NULL; AVCodecContext* codecCtx = NULL; AVCodec* codec = NULL; AVPacket* pkt = av_packet_alloc(); //创建已编码帧 uint8_t* picture_buf = NULL; AVFrame* picFrame = NULL; size_t size; int ret = -1; //[1]!打开视频文件 FILE* in_file = fopen("/mnt/d/media/akiyo_qcif.yuv", "rb"); if (!in_file) { printf("can not open file!n"); return -1; } //[1]! do { //[2]!打开输出文件,并填充fmtCtx数据 int in_w = 176, in_h = 144, frameCnt = 300; const char* outFile = "/mnt/d/media/encoderesult.h264"; if (avformat_alloc_output_context2(&fmtCtx, NULL, NULL, outFile) < 0) { printf("Cannot alloc output file context.n"); break; } outFmt = fmtCtx->oformat; //[2]! //[3]!打开输出文件 if (avio_open(&fmtCtx->pb, outFile, AVIO_FLAG_READ_WRITE) < 0) { printf("output file open failed.n"); break; } //[3]! //[4]!创建h264视频流,并设置参数 vStream = avformat_new_stream(fmtCtx, codec); if (vStream == NULL) { printf("failed create new video stream.n"); break; } vStream->time_base.den = 25; vStream->time_base.num = 1; //[4]! //[5]!编码参数相关 AVCodecParameters* codecPara = fmtCtx->streams[vStream->index]->codecpar; codecPara->codec_type = AVMEDIA_TYPE_VIDEO; codecPara->width = in_w; codecPara->height = in_h; //[5]! //[6]!查找编码器 codec = avcodec_find_encoder(outFmt->video_codec); if (codec == NULL) { printf("Cannot find any endcoder.n"); break; } //[6]! //[7]!设置编码器内容 codecCtx = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codecCtx, codecPara); if (codecCtx == NULL) { printf("Cannot alloc context.n"); break; } codecCtx->codec_id = outFmt->video_codec; codecCtx->codec_type = AVMEDIA_TYPE_VIDEO; codecCtx->pix_fmt = AV_PIX_FMT_YUV420P; codecCtx->width = in_w; codecCtx->height = in_h; codecCtx->time_base.num = 1; codecCtx->time_base.den = 25; codecCtx->bit_rate = 400000; codecCtx->gop_size = 12; if (codecCtx->codec_id == AV_CODEC_ID_H264) { codecCtx->qmin = 10; codecCtx->qmax = 51; codecCtx->qcompress = (float)0.6; } //codecCtx->max_b_frames = 0; if (codecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO) codecCtx->max_b_frames = 2; if (codecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO) codecCtx->mb_decision = 2; //[7]! //[8]!打开编码器 if (avcodec_open2(codecCtx, codec, NULL) < 0) { printf("Open encoder failed.n"); break; } //[8]! av_dump_format(fmtCtx, 0, outFile, 1);//输出 输出文件流信息 //初始化帧 picFrame = av_frame_alloc(); picFrame->width = codecCtx->width; picFrame->height = codecCtx->height; picFrame->format = codecCtx->pix_fmt; size = (size_t)av_image_get_buffer_size(codecCtx->pix_fmt, codecCtx->width, codecCtx->height, 1); picture_buf = (uint8_t*)av_malloc(size); av_image_fill_arrays(picFrame->data, picFrame->linesize, picture_buf, codecCtx->pix_fmt, codecCtx->width, codecCtx->height, 1); //[9] --写头文件 ret = avformat_write_header(fmtCtx, NULL); //[9] int y_size = codecCtx->width * codecCtx->height; av_new_packet(pkt, (int)(size * 3)); //[10] --循环编码每一帧 for (int i = 0; i < frameCnt; i++) { //读入YUV if (fread(picture_buf, 1, (unsigned long)(y_size * 3 / 2), in_file) <= 0) { printf("read file fail!n"); return -1; } else if (feof(in_file)) break; picFrame->data[0] = picture_buf; //亮度Y picFrame->data[1] = picture_buf + y_size; // U picFrame->data[2] = picture_buf + y_size * 5 / 4; // V // AVFrame PTS picFrame->pts = i; //编码 if (avcodec_send_frame(codecCtx, picFrame) >= 0) { while (avcodec_receive_packet(codecCtx, pkt) >= 0) { printf("encoder success!n"); // parpare packet for muxing pkt->stream_index = vStream->index; av_packet_rescale_ts(pkt, codecCtx->time_base, vStream->time_base); pkt->pos = -1; ret = av_interleaved_write_frame(fmtCtx, pkt); if (ret < 0) { //printf("error is: %s.n", av_err2str(ret)); } av_packet_unref(pkt);//刷新缓存 } } } //[10] //[11] --Flush encoder //ret = flush_encoder(fmtCtx, codecCtx, vStream->index); //if (ret < 0) { // printf("flushing encoder failed!n"); // break; //} //[11] //[12] --写文件尾 av_write_trailer(fmtCtx); //[12] } while (0); //释放内存 av_packet_free(&pkt); avcodec_close(codecCtx); av_free(picFrame); av_free(picture_buf); if (fmtCtx) { avio_close(fmtCtx->pb); avformat_free_context(fmtCtx); } fclose(in_file); return 0; }

6.2 说明

  • 控制压缩效率可以通过以下两个参数进行调节:
    • gop_size:指定两个关键帧之间的距离,如果设置为0,则全部都是I帧,压缩效果最差。
    • max_b_frames: 最大B帧数,控制B帧的数量,如果设置为0则不会有B帧
  • 压缩前后文件大小比较,可见MP4和h264文件大小基本上保持一致,二者比起未经压缩的yuv文件缩小24倍左右。
mp4h264yuv
459k457k11M
  • 编码相较于解码耗时更长,在微星笔记本(12代i9)上编码fps约为15,用显卡(RTX3080)加速后效率提升非常明显,h264_nvenc编码的fps约为120.

7. h264封装为MP4文件

MP4就是一个盒子,里边可以封装视频、音频、字幕,这里我们只封装视频,也就是最后产出的是只有视频没有音频的MP4文件。音频的添加到音频编解码时再进行。

7.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
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
169
#include <stdio.h> extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavcodec/avcodec.h" #include "libswscale/swscale.h" #include "libavformat/avformat.h" #include "libavutil/avutil.h" #include "libavutil/mathematics.h" } // 将H264转封装为MP4 int main() { int frame_index = 0;//统计帧数 int inVStreamIndex = -1, outVStreamIndex = -1;//输入输出视频流在文件中的索引位置 const char* inVFileName = "/mnt/d/media/encoderesult.h264"; const char* outFileName = "/mnt/d/media/akiyo_qcif.mp4"; AVFormatContext* inVFmtCtx = NULL, * outFmtCtx = NULL; AVCodecParameters* codecPara = NULL; AVStream* outVStream = NULL; const AVCodec* outCodec = NULL; AVCodecContext* outCodecCtx = NULL; AVCodecParameters* outCodecPara = NULL; AVStream* inVStream = NULL; AVPacket* pkt = av_packet_alloc(); do { //======================输入部分============================// //打开输入文件 if (avformat_open_input(&inVFmtCtx, inVFileName, NULL, NULL) < 0) { printf("Cannot open input file.n"); break; } //查找输入文件中的流 if (avformat_find_stream_info(inVFmtCtx, NULL) < 0) { printf("Cannot find stream info in input file.n"); break; } //查找视频流在文件中的位置 for (size_t i = 0; i < inVFmtCtx->nb_streams; i++) { if (inVFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { inVStreamIndex = (int)i; break; } } codecPara = inVFmtCtx->streams[inVStreamIndex]->codecpar;//输入视频流的编码参数 printf("===============Input information========>n"); av_dump_format(inVFmtCtx, 0, inVFileName, 0); printf("===============Input information========<n"); //=====================输出部分=========================// //打开输出文件并填充格式数据 if (avformat_alloc_output_context2(&outFmtCtx, NULL, NULL, outFileName) < 0) { printf("Cannot alloc output file context.n"); break; } //打开输出文件并填充数据 if (avio_open(&outFmtCtx->pb, outFileName, AVIO_FLAG_READ_WRITE) < 0) { printf("output file open failed.n"); break; } //在输出的mp4文件中创建一条视频流 outVStream = avformat_new_stream(outFmtCtx, NULL); if (!outVStream) { printf("Failed allocating output stream.n"); break; } outVStream->time_base.den = 25; outVStream->time_base.num = 1; outVStreamIndex = outVStream->index; //查找编码器 outCodec = avcodec_find_encoder(codecPara->codec_id); if (outCodec == NULL) { printf("Cannot find any encoder.n"); break; } //从输入的h264编码器数据复制一份到输出文件的编码器中 outCodecCtx = avcodec_alloc_context3(outCodec); outCodecPara = outFmtCtx->streams[outVStream->index]->codecpar; if (avcodec_parameters_copy(outCodecPara, codecPara) < 0) { printf("Cannot copy codec para.n"); break; } if (avcodec_parameters_to_context(outCodecCtx, outCodecPara) < 0) { printf("Cannot alloc codec ctx from para.n"); break; } outCodecCtx->time_base.den = 25; outCodecCtx->time_base.num = 1; //打开输出文件需要的编码器 if (avcodec_open2(outCodecCtx, outCodec, NULL) < 0) { printf("Cannot open output codec.n"); break; } printf("============Output Information=============>n"); av_dump_format(outFmtCtx, 0, outFileName, 1); printf("============Output Information=============<n"); //写入文件头 if (avformat_write_header(outFmtCtx, NULL) < 0) { printf("Cannot write header to file.n"); return -1; } //===============编码部分===============// inVStream = inVFmtCtx->streams[inVStreamIndex]; while (av_read_frame(inVFmtCtx, pkt) >= 0) {//循环读取每一帧直到读完 if (pkt->stream_index == inVStreamIndex) {//确保处理的是视频流 //FIXME:No PTS (Example: Raw H.264) //Simple Write PTS //如果当前处理帧的显示时间戳为0或者没有等等不是正常值 if (pkt->pts == AV_NOPTS_VALUE) { printf("frame_index:%dn", frame_index); //Write PTS AVRational time_base1 = inVStream->time_base; //Duration between 2 frames (us) int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(inVStream->r_frame_rate); //Parameters pkt->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1) * AV_TIME_BASE); pkt->dts = pkt->pts; pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1) * AV_TIME_BASE); frame_index++; } //Convert PTS/DTS pkt->pts = av_rescale_q_rnd(pkt->pts, inVStream->time_base, outVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); pkt->dts = av_rescale_q_rnd(pkt->dts, inVStream->time_base, outVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)); pkt->duration = av_rescale_q(pkt->duration, inVStream->time_base, outVStream->time_base); pkt->pos = -1; pkt->stream_index = outVStreamIndex; printf("Write 1 Packet. size:%5dtpts:%ldn", pkt->size, pkt->pts);//每一帧的字节数 //Write if (av_interleaved_write_frame(outFmtCtx, pkt) < 0) { printf("Error muxing packetn"); break; } //pkt->pts = pkt->dts; av_packet_unref(pkt); } } av_write_trailer(outFmtCtx); } while (0); //=================释放所有指针======================= av_packet_free(&pkt); avformat_close_input(&outFmtCtx); avcodec_close(outCodecCtx); avcodec_free_context(&outCodecCtx); avformat_close_input(&inVFmtCtx); avformat_free_context(inVFmtCtx); //avio_close(outFmtCtx->pb); return 0; }

7.2 说明

  • 整个过程中未作实际的编解码,只是进行了时间戳的转换。
  • 关于ffmpeg中的时间戳可以参考《ffmpeg中timebase理解》,其实就是把一秒划分为n个单元,以1/n秒作为一个单位。

8. 摄像头数据编码为h264

8.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
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#include <unistd.h> #include <libavcodec/avcodec.h> #include <libavdevice/avdevice.h> #include <libavutil/channel_layout.h> #include <libavutil/common.h> #include <libavutil/frame.h> #include <libavutil/samplefmt.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h> #include <libavutil/parseutils.h> #include <libavutil/mem.h> #include <libswscale/swscale.h> #include <libavformat/avformat.h> int flush_encoder(AVFormatContext *fmtCtx, AVCodecContext *codecCtx, int vStreamIndex){ int ret=0; AVPacket *enc_pkt=av_packet_alloc(); enc_pkt->data = NULL; enc_pkt->size = 0; if (!(codecCtx->codec->capabilities & AV_CODEC_CAP_DELAY)) return 0; printf("Flushing stream #%u encodern",vStreamIndex); if(avcodec_send_frame(codecCtx,0)>=0){ while(avcodec_receive_packet(codecCtx,enc_pkt)>=0){ printf("success encoder 1 frame.n"); // parpare packet for muxing enc_pkt->stream_index = vStreamIndex; av_packet_rescale_ts(enc_pkt,codecCtx->time_base, fmtCtx->streams[ vStreamIndex ]->time_base); ret = av_interleaved_write_frame(fmtCtx, enc_pkt); if(ret<0){ break; } } } av_packet_unref(enc_pkt); return ret; } int main() { int ret = 0; avdevice_register_all(); AVFormatContext *inFmtCtx = avformat_alloc_context(); AVCodecContext *inCodecCtx = NULL; const AVCodec *inCodec =NULL; AVPacket *inPkt =av_packet_alloc(); AVFrame *srcFrame =av_frame_alloc(); AVFrame *yuvFrame =av_frame_alloc(); //打开输出文件,并填充fmtCtx数据 AVFormatContext *outFmtCtx = avformat_alloc_context(); const AVOutputFormat *outFmt = NULL; AVCodecContext *outCodecCtx=NULL; const AVCodec *outCodec = NULL; AVStream *outVStream = NULL; AVPacket *outPkt = av_packet_alloc(); struct SwsContext *img_ctx = NULL; int inVideoStreamIndex = -1; do{ /解码器部分// //打开摄像头 const AVInputFormat *inFmt = av_find_input_format("v4l2"); if(avformat_open_input(&inFmtCtx,"/dev/video0",inFmt,NULL)<0){ printf("Cannot open camera.n"); return -1; } if(avformat_find_stream_info(inFmtCtx,NULL)<0){ printf("Cannot find any stream in file.n"); return -1; } for(size_t i=0;i<inFmtCtx->nb_streams;i++){ if(inFmtCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){ inVideoStreamIndex=i; break; } } if(inVideoStreamIndex==-1){ printf("Cannot find video stream in file.n"); return -1; } AVCodecParameters *inVideoCodecPara = inFmtCtx->streams[inVideoStreamIndex]->codecpar; if(!(inCodec=avcodec_find_decoder(inVideoCodecPara->codec_id))){ printf("Cannot find valid video decoder.n"); return -1; } if(!(inCodecCtx = avcodec_alloc_context3(inCodec))){ printf("Cannot alloc valid decode codec context.n"); return -1; } if(avcodec_parameters_to_context(inCodecCtx,inVideoCodecPara)<0){ printf("Cannot initialize parameters.n"); return -1; } if(avcodec_open2(inCodecCtx,inCodec,NULL)<0){ printf("Cannot open codec.n"); return -1; } img_ctx = sws_getContext(inCodecCtx->width, inCodecCtx->height, inCodecCtx->pix_fmt, inCodecCtx->width, inCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL,NULL,NULL); int numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, inCodecCtx->width, inCodecCtx->height,1); uint8_t* out_buffer = (unsigned char*)av_malloc(numBytes*sizeof(unsigned char)); ret = av_image_fill_arrays(yuvFrame->data, yuvFrame->linesize, out_buffer, AV_PIX_FMT_YUV420P, inCodecCtx->width, inCodecCtx->height, 1); if(ret<0){ printf("Fill arrays failed.n"); return -1; } //解码器部分结束/ //编码器部分开始/ const char* outFile = "result.h264"; if(avformat_alloc_output_context2(&outFmtCtx,NULL,NULL,outFile)<0){ printf("Cannot alloc output file context.n"); return -1; } outFmt = outFmtCtx->oformat; //打开输出文件 if(avio_open(&outFmtCtx->pb,outFile,AVIO_FLAG_READ_WRITE)<0){ printf("output file open failed.n"); return -1; } //创建h264视频流,并设置参数 outVStream = avformat_new_stream(outFmtCtx,outCodec); if(outVStream==NULL){ printf("create new video stream fialed.n"); return -1; } outVStream->time_base.den=30; outVStream->time_base.num=1; //编码参数相关 AVCodecParameters *outCodecPara = outFmtCtx->streams[outVStream->index]->codecpar; outCodecPara->codec_type=AVMEDIA_TYPE_VIDEO; outCodecPara->codec_id = outFmt->video_codec; outCodecPara->width = 480; outCodecPara->height = 360; outCodecPara->bit_rate = 110000; //查找编码器 outCodec = avcodec_find_encoder(outFmt->video_codec); if(outCodec==NULL){ printf("Cannot find any encoder.n"); return -1; } //设置编码器内容 outCodecCtx = avcodec_alloc_context3(outCodec); avcodec_parameters_to_context(outCodecCtx,outCodecPara); if(outCodecCtx==NULL){ printf("Cannot alloc output codec content.n"); return -1; } outCodecCtx->codec_id = outFmt->video_codec; outCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; outCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; outCodecCtx->width = inCodecCtx->width; outCodecCtx->height = inCodecCtx->height; outCodecCtx->time_base.num=1; outCodecCtx->time_base.den=30; outCodecCtx->bit_rate=110000; outCodecCtx->gop_size=10; if(outCodecCtx->codec_id==AV_CODEC_ID_H264){ outCodecCtx->qmin=10; outCodecCtx->qmax=51; outCodecCtx->qcompress=(float)0.6; }else if(outCodecCtx->codec_id==AV_CODEC_ID_MPEG2VIDEO){ outCodecCtx->max_b_frames=2; }else if(outCodecCtx->codec_id==AV_CODEC_ID_MPEG1VIDEO){ outCodecCtx->mb_decision=2; } //打开编码器 if(avcodec_open2(outCodecCtx,outCodec,NULL)<0){ printf("Open encoder failed.n"); return -1; } ///编码器部分结束 ///编解码部分// yuvFrame->format = outCodecCtx->pix_fmt; yuvFrame->width = outCodecCtx->width; yuvFrame->height = outCodecCtx->height; ret = avformat_write_header(outFmtCtx,NULL); int count = 0; while(av_read_frame(inFmtCtx,inPkt)>=0 && count<50){ if(inPkt->stream_index == inVideoStreamIndex){ if(avcodec_send_packet(inCodecCtx,inPkt)>=0){ while((ret=avcodec_receive_frame(inCodecCtx,srcFrame))>=0){ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return -1; else if (ret < 0) { fprintf(stderr, "Error during decodingn"); exit(1); } sws_scale(img_ctx, (const uint8_t* const*)srcFrame->data, srcFrame->linesize, 0,inCodecCtx->height, yuvFrame->data,yuvFrame->linesize); yuvFrame->pts=srcFrame->pts; //encode if(avcodec_send_frame(outCodecCtx,yuvFrame)>=0){ if(avcodec_receive_packet(outCodecCtx,outPkt)>=0){ printf("encode one frame.n"); ++count; outPkt->stream_index = outVStream->index; av_packet_rescale_ts(outPkt,outCodecCtx->time_base, outVStream->time_base); outPkt->pos=-1; av_interleaved_write_frame(outFmtCtx,outPkt); av_packet_unref(outPkt); } } usleep(1000*24); } } av_packet_unref(inPkt); } } ret = flush_encoder(outFmtCtx,outCodecCtx,outVStream->index); if(ret<0){ printf("flushing encoder failed.n"); return -1; } av_write_trailer(outFmtCtx); 编解码部分结束 }while(0); ///内存释放部分/ av_packet_free(&inPkt); avcodec_free_context(&inCodecCtx); avcodec_close(inCodecCtx); avformat_close_input(&inFmtCtx); av_frame_free(&srcFrame); av_frame_free(&yuvFrame); av_packet_free(&outPkt); avcodec_free_context(&outCodecCtx); avcodec_close(outCodecCtx); avformat_close_input(&outFmtCtx); return 0; }

8.2 说明

  • V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动
  • 步骤为读取摄像头YUYV422数据->解码为YUV420P->编码为H264流->保存文件

Ref

《FFmpeg4入门系列教程索引》

最后

以上就是悲凉小白菜最近收集整理的关于FFmpeg4编程入门---视频篇1.ffmpeg介绍2. Hello World及输出版本号3.截取视频帧保存图片ppm4.解码视频文件(mp4)为yuv文件5.视频解码硬件加速6. 视频编码为h2647. h264封装为MP4文件8. 摄像头数据编码为h264的全部内容,更多相关FFmpeg4编程入门---视频篇1.ffmpeg介绍2.内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部