版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38215395/article/details/81023984
本文主要基于Qt利用FFmpeg视频库完成对网络摄像头(H.264)视频流的解码、显示、格式转换及存储。
文章FFmpeg+Qt实现摄像头(rtsp)实时显示实现了摄像头视频流的解码及显示工作。
接下来就是视频格式转换,主要转换思路是:视频存储(YUV420P)—>H.264—>avi
1、视频存储
采用以下方式,将解码后的一帧帧图像(YUV格式)存储到本地:
fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv); //Y
fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv); //U
fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv); //V
其中,fp_yuv为存储文件““
YUV文件播放时需要专门的播放器,比如“YUV Player”。
2、YUV420P—>H.264
此过程主要包含以下几个步骤:
(1)打开输出文件:avio_open()函数
if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
printf("Failed to open output file! \n");
return;
}
(2)寻找编码器:avcodec_find_encoder()
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec) {
printf("Can not find encoder! \n");
return;
}
if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) {
printf("Failed to open encoder! \n");
return;
}
(3)写文件头
avformat_write_header(pFormatCtx, NULL);
av_new_packet(&pkt, picture_size);
y_size = pCodecCtx->width * pCodecCtx->height;
for (int i = 0; i<framenum; i++) {
//Read raw YUV data
if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0) {
printf("Failed to read raw data! \n");
return;
}
else if (feof(in_file)) {
break;
}
pFrame->data[0] = picture_buf; // Y
pFrame->data[1] = picture_buf + y_size; // U
pFrame->data[2] = picture_buf + y_size * 5 / 4; // V
//PTS
//pFrame->pts=i;
pFrame->pts = i*(video_st->time_base.den) / ((video_st->time_base.num) * 25);
int got_picture = 0;
//Encode
int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
if (ret < 0) {
printf("Failed to encode! \n");
exit(0);
}
if (got_picture == 1) {
printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
framecnt++;
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
}
}
(4) Flush Encoder
int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index) {
int ret;
int got_frame;
AVPacket enc_pkt;
if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
AV_CODEC_CAP_DELAY))
return 0;
while (1) {
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = avcodec_encode_video2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,
NULL, &got_frame);
av_frame_free(NULL);
if (ret < 0)
break;
if (!got_frame) {
ret = 0;
break;
}
printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", enc_pkt.size);
/* mux encoded frame */
ret = av_write_frame(fmt_ctx, &enc_pkt);
if (ret < 0)
break;
}
return ret;
}
(5) 写文件尾
av_write_trailer(pFormatCtx);
3、H.264—>avi
直接在程序里调用了FFmpeg命令
system("ffmpeg -i ds.h264 -r 20 -s 512*288 -b:v 4000k output.avi");
- “-i”后面接输入文件名;
- ”-r”后面接帧率;
- ”-s”后面接图像的width*height;
- -b:v 4000k 可以提高输出视频清晰度
- output.avi为转换后输出文件名。
4、程序界面
视频读取及显示
视频转换及存储
完整的源码请见本人的git:[摄像头视频采集及存储系统] (https://gitee.com/git-lizhen/Camera_capture_and_save)