• ffmpeg视频编解码 demo初探(二)(包含下载指定windows版本ffmpeg)将YUV图片序列作为流读入,编码封装成x264 MP4视频


    参考文章:【FFmpeg编码实战】(1)将YUV420P图片集编码成H.264视频文件

    第二个项目:将YUV图片序列作为流读入,编码封装成x264 MP4视频

    将YUV图片序列编码成.h264文件

    直接把博主的代码拷到我们继承的上一个项目中,感觉应该有一些地方需要修改,果不其然

    #pragma warning(disable : 4996)
    #include 
    #include        // 字符串操作
    #include      // 文件夹
    
    extern "C" {            // ffmpeg 相关头文件
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "libavutil/imgutils.h"
    }
    
    using namespace std;
    
    
    #pragma comment(lib, "avformat.lib")
    #pragma comment(lib, "avutil.lib")
    #pragma comment(lib, "avcodec.lib")
    #pragma comment(lib, "avfilter.lib")
    #pragma comment(lib, "swscale.lib")
    #pragma comment(lib, "swresample.lib")
    #pragma comment(lib, "postproc.lib")
    #pragma comment(lib, "avdevice.lib")
    
    // C++ 中 ffmpeg有些地方报错,修改这样后 OK
    //
    static char* av_ts_make_string1(char* buf, int64_t ts)
    {
    	if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
    	else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%" PRId64, ts);
    	return buf;
    }
    
    static char* av_ts_make_time_string1(char* buf, int64_t ts, AVRational* tb)
    {
    	if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
    	else                      snprintf(buf, AV_TS_MAX_STRING_SIZE, "%.6g", av_q2d(*tb) * ts);
    	return buf;
    }
    //
    
    
    
    /// 
    /// 输入输出文件信息
    /// 
    const char* Out_File_Name = "video.h264";
    
    // 源文件名:video/video_out.yuv420p.0.yuv 
    const char* Folder = "video";
    const char* YUV_File_Name = "video_out.yuv420p";
    const char* YUV = "yuv";			// 图片文件后缀
    #define YUV_Width	1050			// 宽
    #define YUV_Height	540				// 高
    #define Stream_Frame_Rate	25		// 帧率:每秒25帧
    
    // 参考:doc\examples\encode_video.c、doc\examples\muxing.c
    
    int main(int* argc, char* argv[])
    {
    
    	// 1. 初始化视频文件格式上下文
    	AVFormatContext* p_FormatCtx = NULL;
    
    	avformat_alloc_output_context2(&p_FormatCtx, NULL, "h264", Out_File_Name);
    	if (!p_FormatCtx) {
    		cout << "无法从输出文件后缀判断视频格式,默认使用h264" << endl;
    		avformat_alloc_output_context2(&p_FormatCtx, NULL, "h264", Out_File_Name);
    	}
    
    	// 2. 获得输出格式 Format信息
    	AVOutputFormat* p_OutputFmt = NULL;
    	p_OutputFmt = p_FormatCtx->oformat;
    
    
    	bool have_video = false, have_audio = false, encode_video = false, encode_audio = false;
    	AVCodec* p_Video_Enc = NULL, * p_Vudio_Enc = NULL;
    	AVCodecContext* p_Video_Enc_Ctx = NULL, * p_Audio_Enc_Ctx = NULL;
    	AVStream* p_Video_st = NULL, * p_Audio_st = NULL;
    	AVFrame* p_Video_Frame = NULL;
    
    	// 3. 添加 视频输出流,查找并打开视频解码器
    	if (p_OutputFmt->video_codec != AV_CODEC_ID_NONE) {
    		// 3.1 找到视频解码器
    		p_Video_Enc = avcodec_find_encoder(p_OutputFmt->video_codec);
    
    		// 3.2 创建视频流
    		p_Video_st = avformat_new_stream(p_FormatCtx, p_Video_Enc);
    		p_Video_st->id = p_FormatCtx->nb_streams - 1;
    
    		// 3.3 创建编码器上下文
    		p_Video_Enc_Ctx = avcodec_alloc_context3(p_Video_Enc);
    
    		// 3.4 配置视频格式
    		p_Video_Enc_Ctx->codec_type = AVMediaType::AVMEDIA_TYPE_VIDEO;
    		p_Video_Enc_Ctx->codec_id = p_OutputFmt->video_codec;	// Code id
    		p_Video_Enc_Ctx->pix_fmt = AVPixelFormat::AV_PIX_FMT_YUV420P;			// 图片格式
    
    		p_Video_Enc_Ctx->bit_rate = 2000000;	// 码率越高,画面质量越好,相应视频越大	// 采样器码率
    		p_Video_Enc_Ctx->width = YUV_Width;						// 宽
    		p_Video_Enc_Ctx->height = YUV_Height;					// 高
    
    		p_Video_st->time_base.num = 1;					// 音位 1s
    		p_Video_st->time_base.den = 25;					// 帧率,每秒25帧
    
    
    		p_Video_Enc_Ctx->framerate.num = 25;
    		p_Video_Enc_Ctx->framerate.den = 1;
    		p_Video_Enc_Ctx->time_base = p_Video_st->time_base;
    
    		p_Video_Enc_Ctx->gop_size = 12;							// 连续画面组大小
    
    		if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_H264)
    		{
    			p_Video_Enc_Ctx->qmin = 10;				// 最小的量化因子
    			p_Video_Enc_Ctx->qmax = 51;				// 最大的量化因子
    			p_Video_Enc_Ctx->qcompress = 0.6;
    		}
    
    
    		if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
    			p_Video_Enc_Ctx->max_b_frames = 2;
    		if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
    			p_Video_Enc_Ctx->mb_decision = 2;
    
    		if (p_OutputFmt->flags & AVFMT_GLOBALHEADER)
    			p_Video_Enc_Ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    
    		// 3.5 打开编码器
    		avcodec_open2(p_Video_Enc_Ctx, p_Video_Enc, NULL);
    		avcodec_parameters_from_context(p_Video_st->codecpar, p_Video_Enc_Ctx);
    
    		// 3.6 分配视频帧内存
    		p_Video_Frame = av_frame_alloc();
    		p_Video_Frame->format = p_Video_Enc_Ctx->pix_fmt;
    		p_Video_Frame->width = p_Video_Enc_Ctx->width;
    		p_Video_Frame->height = p_Video_Enc_Ctx->height;
    		av_frame_get_buffer(p_Video_Frame, 0);
    
    		have_video = true;
    		encode_video = true;
    	}
    
    	// 预留
    	// 4. 添加 音频输出流,查找并打开音频解码器
    	if (p_OutputFmt->audio_codec != AV_CODEC_ID_NONE) {
    		have_audio = true;
    		encode_video = true;
    	}
    
    	// 5. 打印输出信息
    	av_dump_format(p_FormatCtx, 0, Out_File_Name, 1);
    
    	// 6. 打开输出文件
    	if (!(p_OutputFmt->flags & AVFMT_NOFILE))
    		avio_open(&p_FormatCtx->pb, Out_File_Name, AVIO_FLAG_WRITE);
    
    	// 7. 写头信息
    	avformat_write_header(p_FormatCtx, NULL);
    
    	char pic_file_name[50] = "";
    	int pic_index = 0;
    	const int pic_size = YUV_Height * YUV_Width * 3 / 2;
    	FILE* pic_file = NULL;
    	uint8_t* pic_buff = (uint8_t*)av_malloc(pic_size);
    
    	int file_end = 0;
    
    	int ret;
    	char errbuf[AV_ERROR_MAX_STRING_SIZE] = { 0 };
    
    	int y, x;
    
    	// 8. 开始循环写入数据
    	while (file_end == 0) {
    		// 打开文件 获取数据
    		{
    			memset(pic_file_name, '\0', 50);
    			sprintf_s(pic_file_name, 50, "%s/%s.%d.%s", Folder, YUV_File_Name, pic_index, YUV);
    			cout << "打开文件:" << pic_file_name << endl;
    
    			// 注意,此处一定要,使用 "rb+" 否则一次读取的数据大小远小于预期的大小
    			pic_file = fopen(pic_file_name, "rb+");			// fopen(&pic_file, pic_file_name, "rb+")
    			if (pic_file == NULL) {
    				cout << "打开失败:" << pic_file_name << ", 开始刷新编码缓冲区\n";
    				file_end = 1;
    			}
    
    			if (file_end == 0) {
    				//ret = fread_s(pic_buff, pic_size, 1, pic_size, pic_file);
    				ret = fread(pic_buff, 1, pic_size, pic_file);
    				cout << "读取文件大小为:" << ret << "  应读大小:" << pic_size << endl;
    				if (ret <= 0) {
    					cout << "读取内容失败,ret=" << ret << endl;
    					break;
    				}
    				fclose(pic_file);
    				pic_file = NULL;
    
    				/* make sure the frame data is writable */
    				ret = av_frame_make_writable(p_Video_Frame);
    				if (ret < 0)
    				{
    					cout << "p_Video_Frame data 不可写,强制退出\n";
    					exit(1);
    				}
    
    				for (x = 0, y = 0; x < YUV_Height; x++)
    				{
    					memcpy(p_Video_Frame->data[0] + x * p_Video_Frame->linesize[0], pic_buff + y, YUV_Width);
    					y += YUV_Width;
    				}
    				for (x = 0, y = 0; x < YUV_Height / 2; x++)
    				{
    					memcpy(p_Video_Frame->data[1] + x * p_Video_Frame->linesize[1], pic_buff + y + YUV_Width * YUV_Height, YUV_Width / 2);
    					y += YUV_Width / 2;
    				}
    				for (x = 0, y = 0; x < YUV_Height / 2; x++)
    				{
    					memcpy(p_Video_Frame->data[2] + x * p_Video_Frame->linesize[2], pic_buff + y + YUV_Width * YUV_Height * 5 / 4, YUV_Width / 2);
    					y += YUV_Width / 2;
    				}
    
    				//赋值
    					///* prepare a dummy image */
    					///* Y */
    					//for (y = 0; y < p_Video_Enc_Ctx->height; y++) {
    					//	for (x = 0; x < p_Video_Frame->linesize[0]; x++) {
    					//		if (x < YUV_Width)
    					//			p_Video_Frame->data[0][y * p_Video_Frame->linesize[0] + x] = pic_buff[y * YUV_Height + x];								//x + y + pic_index * 3
    					//		else
    					//			p_Video_Frame->data[0][y * p_Video_Frame->linesize[0] + x] = 128;
    					//	}
    					//}
    					//
    					///* Cb and Cr */
    					//for (y = 0; y < p_Video_Enc_Ctx->height / 2; y++) {
    					//	for (x = 0; x < p_Video_Frame->linesize[1]; x++) {
    					//		if (x < YUV_Width / 2) {
    					//			p_Video_Frame->data[1][y * p_Video_Frame->linesize[1] + x] = pic_buff[y * YUV_Height/2 + x + YUV_Height * YUV_Width];	//128 + y + pic_index * 2;
    					//			p_Video_Frame->data[2][y * p_Video_Frame->linesize[2] + x] = pic_buff[y * YUV_Height/2 + x + YUV_Height * YUV_Width * 5 / 4];	// 64 + x + pic_index * 5;
    					//		}
    					//		else {
    					//			p_Video_Frame->data[1][y * p_Video_Frame->linesize[1] + x] = 128;
    					//			p_Video_Frame->data[2][y * p_Video_Frame->linesize[2] + x] = 128;
    					//		}
    
    					//	}
    					//}
    					//
    					//cout << " linesize: " << p_Video_Frame->linesize[0] << "  " << p_Video_Frame->linesize[1] << "  " << p_Video_Frame->linesize[2] << "  " << p_Video_Frame->linesize[3] << endl;
    			}
    		}
    
    		// 开始编码
    		{
    			// PTS: 设置播放时间
    			p_Video_Frame->pts = pic_index * (p_Video_st->time_base.den) / (p_Video_st->time_base.num * 25);
    			// Encode 开始编码
    			if (file_end == 0)
    				ret = avcodec_send_frame(p_Video_Enc_Ctx, p_Video_Frame);
    			else
    				ret = avcodec_send_frame(p_Video_Enc_Ctx, NULL);
    			if (ret < 0) {
    				cout << "编码失败:" << av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE) << endl;
    				exit(1);
    			}
    			// 接收编码后的数据
    			while (ret >= 0) {
    				AVPacket pkt = { 0 };
    				ret = avcodec_receive_packet(p_Video_Enc_Ctx, &pkt);
    				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    					break;
    				else if (ret < 0) {
    					cout << "编码失败:" << av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE) << endl;
    					break;
    				}
    
    				av_packet_rescale_ts(&pkt, p_Video_Enc_Ctx->time_base, p_Video_st->time_base);
    				pkt.stream_index = p_Video_st->index;
    
    				AVRational* time_base = &p_FormatCtx->streams[pkt.stream_index]->time_base;
    				cout << "pts:" << av_ts_make_string1(errbuf, pkt.pts) << " pts_time:" << av_ts_make_time_string1(errbuf, pkt.pts, time_base);
    				cout << " dts:" << av_ts_make_string1(errbuf, pkt.dts) << " duration:" << av_ts_make_string1(errbuf, pkt.duration);
    				cout << " duration_time:" << av_ts_make_time_string1(errbuf, pkt.duration, time_base) << " stream_index:" << pkt.stream_index << endl;
    
    				// 将编码后的数据写入文件中
    				ret = av_interleaved_write_frame(p_FormatCtx, &pkt);
    				av_packet_unref(&pkt);
    
    				if (ret < 0) {
    					cout << "Error while writing output packet: " << av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE) << endl;
    					break;
    				}
    			}
    		}
    
    		pic_index++;
    	}
    
    	// 9. 写入尾信息
    	av_write_trailer(p_FormatCtx);
    
    	// 10.关闭文件
    	if (!(p_FormatCtx->flags & AVFMT_NOFILE))
    		avio_closep(&p_FormatCtx->pb);
    
    
    
    	// 11. 释放资源
    	if (have_video) {
    		if (pic_buff)
    			av_free(pic_buff);
    		if (p_Video_Frame)
    			av_frame_free(&p_Video_Frame);
    		if (p_Video_Enc_Ctx)
    			avcodec_free_context(&p_Video_Enc_Ctx);	// 释放视频解码器
    	}
    
    	if (p_FormatCtx)
    		avformat_free_context(p_FormatCtx);
    
    	system("ffplay video.h264");
    
    	return 0;
    }
    
    
    • 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
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332

    在这里插入图片描述
    跑完后,项目根目录多了个video.h264文件

    VLC播放器能打开浏览

    在这里插入图片描述

    在这里插入图片描述

    将YUV图片序列编码成mp4文件

    参考文章:ffmpeg:将YUV原始数据编码封装为mp4格式

    还是老样子,把博主代码搞下来,vs配置一下,跑

    #pragma warning(disable : 4996)
    
    // 2-muxing编码视频.cpp
    
    #include 
    #include        // 字符串操作
    #include      // 文件夹
    #include 
    
    extern "C" {            // ffmpeg 相关头文件
    #include "libavcodec/avcodec.h"
    #include 
    #include "libavutil/imgutils.h"
    }
    
    using namespace std;
    
    #pragma comment(lib, "avformat.lib")
    #pragma comment(lib, "avutil.lib")
    #pragma comment(lib, "avcodec.lib")
    #pragma comment(lib, "avfilter.lib")
    #pragma comment(lib, "swscale.lib")
    #pragma comment(lib, "swresample.lib")
    #pragma comment(lib, "postproc.lib")
    #pragma comment(lib, "avdevice.lib")
    
    /// 输入输出文件信息
    //const char* Out_File_Name = "video.h264";
    const char* Out_File_Name = "video_out.mp4";
    
    // 源文件名:video/video_out.yuv420p.0.yuv 
    const char* Folder = "video";
    const char* YUV_File_Name = "video_out.yuv420p";
    const char* YUV = "yuv";			// 图片文件后缀
    #define YUV_Width	1050			// 宽
    #define YUV_Height	540				// 高
    #define Stream_Frame_Rate	25		// 帧率:每秒25帧
    
    // 参考:doc\examples\encode_video.c
    
    int main(int* argc, char* argv[])
    {
    	// 1. 通过名字查找Codec 编码器
    	AVCodec* p_Video_Enc = NULL;
    	//p_Video_Enc = avcodec_find_encoder(AV_CODEC_ID_H264);	// 可改进为通过后缀判断
    	p_Video_Enc = avcodec_find_encoder(AV_CODEC_ID_MPEG4);	// 可改进为通过后缀判断
    	if (!p_Video_Enc) {
    		cout << "h264 Codec not foud\n";
    		return -1;
    	}
    	// 2. 获取 Codec 上下文
    	AVCodecContext* p_Video_Enc_Ctx = NULL;
    	p_Video_Enc_Ctx = avcodec_alloc_context3(p_Video_Enc);
    
    	// 3. 配置编码信息
    	p_Video_Enc_Ctx->bit_rate = 2000000;	// 配置码率
    	p_Video_Enc_Ctx->width = YUV_Width;		// 宽
    	p_Video_Enc_Ctx->height = YUV_Height;	// 高
    	// 配置帧率,25
    	p_Video_Enc_Ctx->time_base.num = 1;
    	p_Video_Enc_Ctx->time_base.den = Stream_Frame_Rate;
    	p_Video_Enc_Ctx->framerate.num = Stream_Frame_Rate;
    	p_Video_Enc_Ctx->framerate.den = 1;
    
    	p_Video_Enc_Ctx->gop_size = 12;				// 连续画面组大小
    	//p_Videc_Enc_Ctx->max_b_frames = 30;		// 最大连续b 帧数
    	p_Video_Enc_Ctx->pix_fmt = AV_PIX_FMT_YUV420P;
    	p_Video_Enc_Ctx->codec_type = AVMEDIA_TYPE_VIDEO;
    	p_Video_Enc_Ctx->codec_id = p_Video_Enc->id;
    
    	if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_H264)
    	{
    		p_Video_Enc_Ctx->qmin = 10;				// 最小的量化因子
    		p_Video_Enc_Ctx->qmax = 51;				// 最大的量化因子
    		p_Video_Enc_Ctx->qcompress = 0.6;
    	}
    
    	if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
    		p_Video_Enc_Ctx->max_b_frames = 2;
    	if (p_Video_Enc_Ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
    		p_Video_Enc_Ctx->mb_decision = 2;
    
    	if (p_Video_Enc->id == AV_CODEC_ID_H264)
    		av_opt_set(p_Video_Enc_Ctx->priv_data, "preset", "slow", 0);
    
    	// 4. 打开编码器
    	if (avcodec_open2(p_Video_Enc_Ctx, p_Video_Enc, NULL) < 0) {
    		cout << "打开编码器失败\n";
    		avcodec_free_context(&p_Video_Enc_Ctx);
    		return -1;
    	}
    
    	// 5. 分配 Pkt,frame包内存
    	AVPacket* p_Video_Pkt = NULL;
    	p_Video_Pkt = av_packet_alloc();
    	AVFrame* p_Video_Frame = NULL;
    	p_Video_Frame = av_frame_alloc();
    	p_Video_Frame->format = p_Video_Enc_Ctx->pix_fmt;
    	p_Video_Frame->width = p_Video_Enc_Ctx->width;
    	p_Video_Frame->height = p_Video_Enc_Ctx->height;
    	av_frame_get_buffer(p_Video_Frame, 0);
    
    	// 6. 打开输出文件
    	FILE* p_Out_File = NULL;
    	p_Out_File = fopen(Out_File_Name, "wb");
    	if (!p_Out_File) {
    		cout << "打开 《" << Out_File_Name << "》 文件失败\n";
    		return -1;
    	}
    
    	// 7. 开始编码并写入文件
    	char pic_file_name[50] = "";
    	int pic_index = 0, x, y, ret;
    	uint8_t* pic_buff = (uint8_t*)av_malloc(YUV_Height * YUV_Width * 3 / 2);
    	FILE* pic_file = NULL;
    	bool file_end = false;
    
    	while (file_end == false) {
    		// 打开文件 获取数据
    		memset(pic_file_name, '\0', 50);
    		sprintf_s(pic_file_name, 50, "%s/%s.%d.%s", Folder, YUV_File_Name, pic_index, YUV);
    		cout << "打开文件:" << pic_file_name << " ";
    
    		// 注意,此处一定要,使用 "rb+" 否则一次读取的数据大小远小于预期的大小
    		pic_file = fopen(pic_file_name, "rb+");			// fopen(&pic_file, pic_file_name, "rb+")
    		if (pic_file == NULL) {
    			cout << "打开失败:" << pic_file_name << ", 开始刷新编码缓冲区\n";
    			file_end = true;
    		}
    
    		if (file_end == false) {
    			ret = fread(pic_buff, 1, YUV_Height * YUV_Width * 3 / 2, pic_file);
    			cout << "读取文件大小为:" << ret;
    			if (ret <= 0) {
    				cout << "读取内容失败,ret=" << ret << endl;
    				break;
    			}
    			fclose(pic_file);
    			pic_file = NULL;
    
    			/* make sure the frame data is writable */
    			ret = av_frame_make_writable(p_Video_Frame);
    			if (ret < 0)
    			{
    				cout << "p_Video_Frame data 不可写,强制退出\n";
    				exit(1);
    			}
    
    			for (x = 0, y = 0; x < YUV_Height; x++) {
    				memcpy(p_Video_Frame->data[0] + x * p_Video_Frame->linesize[0], pic_buff + y, YUV_Width);
    				y += YUV_Width;
    			}
    			for (x = 0, y = 0; x < YUV_Height / 2; x++) {
    				memcpy(p_Video_Frame->data[1] + x * p_Video_Frame->linesize[1], pic_buff + y + YUV_Width * YUV_Height, YUV_Width / 2);
    				y += YUV_Width / 2;
    			}
    			for (x = 0, y = 0; x < YUV_Height / 2; x++) {
    				memcpy(p_Video_Frame->data[2] + x * p_Video_Frame->linesize[2], pic_buff + y + YUV_Width * YUV_Height * 5 / 4, YUV_Width / 2);
    				y += YUV_Width / 2;
    			}
    
    			// PTS: 设置播放时间
    			p_Video_Frame->pts = pic_index * (p_Video_Enc_Ctx->time_base.den) / (p_Video_Enc_Ctx->time_base.num * 25);
    			pic_index++;
    
    			cout << "  Frame: pts = " << p_Video_Frame->pts << " linesize = " << p_Video_Frame->linesize[0] << " " << p_Video_Frame->linesize[1] << " " << p_Video_Frame->linesize[2] << endl;
    		}
    
    		// Encode 开始编码
    		if (file_end == false)
    			ret = avcodec_send_frame(p_Video_Enc_Ctx, p_Video_Frame);
    		else {
    			cout << "发送一个空 Frame 包,用于刷新编码缓冲区\n";
    			ret = avcodec_send_frame(p_Video_Enc_Ctx, NULL);
    		}
    
    		// 接收编码后的数据
    		while (ret >= 0) {
    			ret = avcodec_receive_packet(p_Video_Enc_Ctx, p_Video_Pkt);
    			if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
    				break;
    			else if (ret < 0) {
    				cout << "编码失败:" << endl;
    				break;
    			}
    
    			cout << "编码完毕 Video Packet " << p_Video_Pkt->pts << " (size = " << p_Video_Pkt->size << ")\n";
    
    			fwrite(p_Video_Pkt->data, 1, p_Video_Pkt->size, p_Out_File);
    
    			av_packet_unref(p_Video_Pkt);
    		}
    	}
    
    	/* add sequence end code to have a real MPEG file */
    	if (p_Video_Enc->id == AV_CODEC_ID_MPEG1VIDEO || p_Video_Enc->id == AV_CODEC_ID_MPEG2VIDEO) {
    		uint8_t endcode[] = { 0, 0, 1, 0xb7 };
    		fwrite(endcode, 1, sizeof(endcode), p_Out_File);
    	}
    
    	fclose(p_Out_File);
    
    	if (p_Video_Frame)
    		av_frame_free(&p_Video_Frame);
    	if (p_Video_Pkt)
    		av_packet_free(&p_Video_Pkt);
    	if (p_Video_Enc_Ctx) {
    		avcodec_close(p_Video_Enc_Ctx);
    		avcodec_free_context(&p_Video_Enc_Ctx);
    	}
    
    	//system("ffplay video.h264");
    	return 0;
    }
    
    • 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

    在这里插入图片描述
    生成了一个.mp4文件

    在这里插入图片描述
    用qq影音和windows media player能正常打开

    在这里插入图片描述

  • 相关阅读:
    Linux ubuntu22.04安装kvm虚拟机
    配置iSCSI实现Linux的远程块存储
    【Django笔记】认证系统
    php安装ldap扩展模块
    【Linux】线程同步
    View 自定义 - 属性 xml
    M3 MacBook Pro 能提效?程序员、产品经理自证后,CTO:你赢了,新电脑在路上了...
    protobuf编码解码与通讯传输原理
    单光子偏振量子比特的高效量子存储器
    开源DMS文档管理系统 Nuxeo Vs Alfresco对比及 API 使用概述
  • 原文地址:https://blog.csdn.net/Dontla/article/details/127865087