基于Qt+FFmpeg的摄像头视频采集及存储系统实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 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, &param) < 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");
  1. “-i”后面接输入文件名;
    1. ”-r”后面接帧率;
    2. ”-s”后面接图像的width*height;
    3. -b:v 4000k 可以提高输出视频清晰度
    4. output.avi为转换后输出文件名。

4、程序界面

视频读取及显示

这里写图片描述

视频转换及存储

这里写图片描述


完整的源码请见本人的git:[摄像头视频采集及存储系统] (https://gitee.com/git-lizhen/Camera_capture_and_save)

猜你喜欢

转载自blog.csdn.net/weixin_38215395/article/details/81023984