【c++】使用FFmpeg库进行视频流处理的

使用FFmpeg库进行视频流处理的C++代码。主要功能是拉取RTSP流,解码视频帧,并将解码后的帧转换成Qt中的QImage,最后通过信号发射将图像传递给界面。

下面是对代码的详细解释:

  1. 初始化FFmpeg库和网络库:

    cppCopy codeavformat_network_init();
    av_register_all();
    

    这两行代码用于初始化FFmpeg库和网络库,确保在使用解封装(flv,mp4,rmvb,avi)功能和编解码器之前进行了初始化。

  2. 打开视频流:

    cppCopy codeAVFormatContext *pFormatCtx = avformat_alloc_context();
    AVDictionary* avdic = NULL;
    av_dict_set(&avdic, "buffer_size", "102400", 0);
    av_dict_set(&avdic, "rtmp_transport", "tcp", 0);
    av_dict_set(&avdic, "stimeout", "2000000", 0);
    av_dict_set(&avdic, "max_delay", "300000", 0);
    if (avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) {
        qDebug("can't open the file.\n");
        return;
    }
    

    这部分代码用于打开RTSP流或文件流,设置了一些参数以优化流的拉取,例如设置缓存大小、超时时间等。

  3. 查找视频流信息:

    cppCopy codeif (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        qDebug("Could't find stream infomation.\n");
        return;
    }
    

    这里通过avformat_find_stream_info函数获取流的相关信息。

  4. 查找视频流:

    cppCopy codeint videoStream = -1;
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
            break;
        }
    }
    

    遍历流,找到视频流的索引。

  5. 查找解码器并打开:

    cppCopy codepCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        qDebug("Codec not found.\n");
        return;
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        qDebug("Could not open codec.\n");
        return;
    }
    

    找到视频流的解码器并打开。

  6. 循环解码视频帧:

    cppCopy codewhile (1) {
        if (av_read_frame(pFormatCtx, packet) < 0) {
            qDebug() << "av_read_frame < 0";
            break;
        }
        if (packet->stream_index == videoStream) {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
            if (ret < 0) {
                qDebug("decode error.\n");
                return;
            }
            if (got_picture) {
                // 进行缩放、格式转换等操作
                sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                // 将RGB数据加载到QImage
                QImage tmpImg((uchar *)out_buffer, pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32);
                QImage image = tmpImg.copy();
                // 发射信号,传递图像给界面
                emit output_imag(image);
            } else {
                qDebug() << "got_picture < 0";
            }
        } else {
            av_free_packet(packet);
        }
    }
    

    在循环中,通过av_read_frame读取视频帧,然后使用avcodec_decode_video2解码。如果解码成功,进行格式转换、缩放,并将RGB数据加载到QImage中。最后通过信号发射将图像传递给界面。

  7. 释放资源:

    cppCopy codeav_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
    

    最后释放分配的资源,包括输出缓冲区、帧以及关闭解码器和输入流。

请注意,这段代码中使用了一些已被弃用的函数(例如avcodec_decode_video2av_free_packet),新版的FFmpeg可能需要做相应的更新。此外,对于实时视频流,循环的方式并不是最优的,因为它会一直运行,最好的方式是将其放在单独的线程中。

封装成函数

void MyVideoCapture::run_stream()  //ffmpeg拉流获取视频帧
{
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB;
    AVPacket *packet;
    uint8_t *out_buffer;
    static struct SwsContext *img_convert_ctx;
    int videoStream, i, numBytes;
    int ret, got_picture;
    avformat_network_init(); // 初始化网络库
    av_register_all();// 初始化所有组件,调用该函数后,才能使用复用器和编解码器
    pFormatCtx = avformat_alloc_context();// 分配AVFormatContext,它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体
    // ffmpeg取rtsp流时av_read_frame阻塞的解决办法 设置参数优化
    AVDictionary* avdic = NULL;
    av_dict_set(&avdic, "buffer_size", "102400", 0); //设置缓存大小,1080p可将值调大
    av_dict_set(&avdic, "rtmp_transport", "tcp", 0); //以udp方式打开,如果以tcp方式打开将udp替换为tcp
    av_dict_set(&avdic, "stimeout", "2000000", 0);   //设置超时断开连接时间,单位微秒
    av_dict_set(&avdic, "max_delay", "300000", 0);   //设置最大时延
    ///rtsp地址,可根据实际情况修改
    char *url;
    //        QString ab = "C:/Users/goby/Desktop/Video_left.mp4";rtsp_address
    url= addrest.toLocal8Bit().data();
    //  qDebug() << url << "url"<<addrest;
    if (avformat_open_input(&pFormatCtx, url, NULL, &avdic) != 0) {  // 打开网络流或文件流
        qDebug("can't open the file. \n");
        return;
    }
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { // 读取流数据包并获取流的相关信息
        qDebug("Could't find stream infomation.\n");
        return;
    }
    videoStream = -1;
    // 确定流格式是否为视频
    for (i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            break;
        }
    }
    ///如果videoStream为-1 说明没有找到视频流
    if (videoStream == -1) {
        qDebug("Didn't find a video stream.\n");
        return;
    }

    ///查找解码器
    pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);  // 根据编码器的ID号查找对应的解码器
    pCodecCtx->bit_rate = 0;   //码率
    pCodecCtx->time_base.num = 1;  //下面两行:一秒钟10帧
    pCodecCtx->time_base.den = 10;
    pCodecCtx->frame_number = 1;  //每包一个视频帧
    if (pCodec == NULL) {
        qDebug("Codec not found.\n");
        return;
    }
    // Initialize the AVCodecContext to use the given AVCodec
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        qDebug("Could not open codec.\n");
        return;
    }
    // alloc AVFrame
    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();
    ///这里我们改成了 将解码后的YUV数据转换成RGB32
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                                     AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
    out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    // 将分配的内存空间给m_AVFrameRGB使用
    avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_RGB32,
                   pCodecCtx->width, pCodecCtx->height);
    // 为AVPacket分别内存空间
    int y_size = pCodecCtx->width * pCodecCtx->height;
    packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
    av_new_packet(packet, y_size); //分配packet的数据
    int j=0;
    while (1)
    {
        j++;
        if (av_read_frame(pFormatCtx, packet) < 0)
        {
            qDebug() << "av_read_frame < 0";
            break; //这里认为视频读取完了
        }
        if (packet->stream_index == videoStream)
        {
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
            if (ret < 0) {
                qDebug("decode error.\n");
                return;
            }
            if (got_picture)
            {// 对解码视频帧进行缩放、格式转换等操作
                sws_scale(img_convert_ctx,(uint8_t const * const *) pFrame->data,pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
                //把这个RGB数据 用QImage加载
                QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
                emit output_imag(image);
            }else qDebug() << "got_picture < 0";
        }else /*qDebug() << "packet->stream_index not video stream";*/
            av_free_packet(packet); //释放资源,否则内存会一直上升
    }
    //对MyFFmpegInit接口中申请的资源进行释放操作
    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值