1. 项目背景

下位机里安装里摄像头,并处理摄像头的实时数据。上位机通过rstp协议连接下位机,接收下位机传输的视频流数据。

 

2. 前言

ffmpeg是一个开源库,可制作跨平台视频播放器。

ffmpeg库的安装:《Linu下安装ffmpeg》

 

涉及的库有如下几个:

avcodec avformat swscale avutil avcodec-ffmpeg swresample

 

3. 实现思路

ffmpeg视频流播放,比较占用系统资源,所以要使用多线程技术处理耗时操作。

ffmpeg解析完成后,使用QPainter绘制图像

 

4. 代码主要实现部分

1、ffmpge初始化接口

int FFmpeg::initial(QString & url)
{
    qDebug() << "FFmpeg initial start.";

    int ret;
    rtspURL=url;
    const AVCodec *pCodec;
    //初始化
    //av_register_all();        //新版的不需要注册
    avformat_network_init();    //支持网络流

    //打开码流前,设置参数
    AVDictionary *optionsDict = NULL;
    av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0);
    av_dict_set(&optionsDict, "stimeout", "30000000", 0);
    pFormatCtx = avformat_alloc_context();  //初始化AVFormatContext, 分配一块内存,保存视频属性信息
    ret = avformat_open_input(&pFormatCtx, rtspURL.toStdString().c_str(), NULL, &optionsDict);
    char buf[1024] = {0};
    if (ret < 0)
    {
        qDebug() << "Can not open this file";
        av_strerror(ret, buf, 1024);
        return -1;
    }

    //查找流信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        qDebug() << "Unable to get stream info";
        return -1;
    }

    //获取视频流ID
    int i = 0;
    videoStream = -1;
    for (i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1)
    {
        qDebug() << "Unable to find video stream";
        return -1;
    }

    //AVCodecParameters转AVCodecContext
    pCodecCtx = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(pCodecCtx,pFormatCtx->streams[videoStream]->codecpar);

    //查找解码器
    pCodec    = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL)
    {
        qDebug() << "Unsupported codec";
        return -1;
    }

    //打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        qDebug() << "Unable to open codec";
        return -1;
    }

    //打印关于输入或输出格式的详细信息
    av_dump_format(pFormatCtx, 0, rtspURL.toStdString().c_str(), 0);

    //存储解码后AVFrame
    pFrame = av_frame_alloc();

    height = pCodecCtx->height;
    width = pCodecCtx->width;

    qDebug() << "FFmpeg initial successfully";


    return 0;
}

2、ffmpeg解码接口

int FFmpeg::h264Decodec()
{
    qDebug() << "h264Decodec start...";

    //为packet分配内存
    AVPacket * packet = NULL;
    packet = (AVPacket *)av_malloc(sizeof(AVPacket));

    //循环 获取视频的数据
    while (av_read_frame(pFormatCtx, packet) >= 0)
    {
        //停止读取视频流
        if(h264DecodecStop)
        {
            //进行内存释放
            av_packet_unref(packet);
            break;
        }

        //当视频流的ID跟读取到的视频流ID一致的时候
        if(packet->stream_index==videoStream)
        {
            //解码
            int re = avcodec_send_packet(pCodecCtx, packet);
            av_packet_unref(packet);
            if(re!=0)
            {
               continue;
            }

            //从解码器中获取解码的输出数据,存入pFrame
            re = avcodec_receive_frame(pCodecCtx, pFrame);
            if(re!=0)
            {
                qDebug() << "avcodec_receive_frame failed !";
               continue;
            }

            //初始化img_convert_ctx
            struct SwsContext * img_convert_ctx;
            img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
            if (img_convert_ctx == NULL)
            {
                break;
            }

            mutex.lock();

            char *rgb = new char[width*height*3];
            memset(rgb, 0 , width*height*3);
            pictureData[0] = (uint8_t *)rgb;
            int lines[AV_NUM_DATA_POINTERS] = {0};
            lines[0] = width * 3;

            //开始转换图像数据
            int rs = 0;
            rs = sws_scale(img_convert_ctx,
                                (const uint8_t**) pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                                pictureData, lines);

            if (img_convert_ctx)
            {
                sws_freeContext(img_convert_ctx);
                img_convert_ctx = NULL;
            }

            mutex.unlock();
            if (rs == -1)
            {
                qDebug() << "__________Can open to change to des imag_____________e\n";
                return -1;
            }

        }
    }

    if (packet!=NULL)
    {
        av_packet_unref (packet);
        packet = NULL;
    }
    if(pFrame!=NULL)
    {
        av_frame_free(&pFrame);
        pFrame = NULL;
    }

    qDebug() << "h264Decodec end...";
    return 0;
}

3、qt界面绘制接口

void Video::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    if(ffmpeg->mutex.tryLock(1000))
    {
        QImage image=QImage(ffmpeg->pictureData[0],ffmpeg->width,ffmpeg->height,QImage::Format_RGB888);
        QPixmap  pix =  QPixmap::fromImage(image);
        painter.drawPixmap(0, 0, 640, 480, pix);
        update();
        ffmpeg->mutex.unlock();
    }
}

5. 源码

RTSPClient.tar.gz

Logo

更多推荐