ffmpeg拉取rtsp视频流

公司项目需要提供实时显示网络摄像头实时视频.

void RTSPFFmpeg::rtsp_open(const char *url) {
    AVFormatContext* format_ctx = avformat_alloc_context();
    AVCodecContext *pAVCodecContext_video = nullptr;
    AVCodec *pAVCodec_video = nullptr;
    AVCodecParameters *pAVCodePar_video = avcodec_parameters_alloc();
    AVPacket *pAVPacket = av_packet_alloc(); ;                                  // ffmpeg单帧数据包
    AVFrame *pAVFrame_video = av_frame_alloc();                                 // ffmpeg单帧缓存
    AVFrame *pAVFrameRGB32_video = av_frame_alloc();                            // ffmpeg单帧缓存转换颜色空间后的缓存
    AVCodecParserContext *pAVCodeParseContext_video = nullptr;
    struct SwsContext *pSwsContext_video = nullptr;                             // ffmpeg编码数据格式转换
    AVDictionary * opts = nullptr;

    int ret = -1;
    int numBytes = 0;                                                           // 解码后的数据长度
    u_char *outBuffer = nullptr;                                                // 解码后的数据存放缓存区
 // open rtsp: Open an input stream and read the header. The codecs are not opened
    //const char* url = "rtsp://admin:genepoint2020@192.168.100.14:554/cam/realmonitor?channel=1&subtype=0";
    av_dict_set(&opts, "rtsp_transport", "tcp", 0);
    av_dict_set(&opts, "stimeout", "2000000", 0);
    // audio/video stream index
    int video_stream_index = -1;
   ret = avformat_open_input(&format_ctx, url, nullptr, &opts);
        if (ret != 0) {
            fprintf(stderr, "fail to open url: %s, return value: %d\n", url, ret);
continue;
        }
        // Read packets of a media file to get stream information
        ret = avformat_find_stream_info(format_ctx, nullptr);
        if (ret < 0) {
            fprintf(stderr, "fail to get stream information: %d\n", ret);
continue;
        }

        fprintf(stdout, "Number of elements in AVFormatContext.streams: %d\n", format_ctx->nb_streams);
        for (int i = 0; i < format_ctx->nb_streams; ++i) {
            const AVStream *stream = format_ctx->streams[i];
            fprintf(stdout, "type of the encoded data: %d\n", stream->codecpar->codec_id);
            if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                video_stream_index = i;
                // 对找到的视频流寻解码器
                pAVCodePar_video = stream->codecpar;
                pAVCodec_video = avcodec_find_decoder(stream->codecpar->codec_id);
                if (!pAVCodec_video) {
                    video_stream_index = -1;
                    break;
                }
                pAVCodeParseContext_video = av_parser_init(pAVCodec_video->id);
                if (!pAVCodeParseContext_video) {
                    video_stream_index = -1;
                    break;
                }
                pAVCodecContext_video = avcodec_alloc_context3(pAVCodec_video);
                if (!pAVCodecContext_video) {
                }
                if (avcodec_open2(pAVCodecContext_video, pAVCodec_video, NULL) < 0) {
                    video_stream_index = -1;
                    break;
                }
                fprintf(stdout, "dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",
                        stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);
            }
        }
        if (video_stream_index == -1) {
            fprintf(stderr, "no video stream\n");
continue;
        }
        // 对拿到的原始数据格式进行缩放转换为指定的格式高宽大小
        pSwsContext_video = sws_getContext(
                pAVCodePar_video->width,
                pAVCodePar_video->height,
                static_cast<AVPixelFormat>(pAVCodePar_video->format),
                pAVCodePar_video->width,
                pAVCodePar_video->height,
                AV_PIX_FMT_RGBA,
                SWS_FAST_BILINEAR,
                nullptr,
                nullptr,
                nullptr
        );
        numBytes = av_image_get_buffer_size(
                AV_PIX_FMT_RGBA,
                pAVCodePar_video->width,
                pAVCodePar_video->height,
                1
        );
        outBuffer = (u_char *) av_malloc(numBytes);
        // pAVFrame32的data指针指向了outBuffer
        av_image_fill_arrays(
                pAVFrameRGB32_video->data,
                pAVFrameRGB32_video->linesize,
                outBuffer,
                AV_PIX_FMT_RGBA,
                pAVCodePar_video->width,
                pAVCodePar_video->height,
                1
        );
 while (1) {
        ret = av_read_frame(format_ctx, pAVPacket);
        if (ret < 0) {
            fprintf(stderr, "error or end of file: %d\n", ret);
continue;
        }
        if (pAVPacket->stream_index == video_stream_index) {
//            fprintf(stdout, "video stream, packet size: %d\n", pAVPacket->size);
            ret = avcodec_send_packet(pAVCodecContext_video,pAVPacket);
            if( 0 != ret){
continue;
            }
while (avcodec_receive_frame(pAVCodecContext_video,pAVFrame_video) == 0){
                sws_scale(pSwsContext_video,
                          (const uint8_t * const *)pAVFrame_video->data,
                          pAVFrame_video->linesize,
                          0,
                          pAVCodePar_video->height,
                          pAVFrameRGB32_video->data,
                          pAVFrameRGB32_video->linesize);
                QImage image((u_char*)outBuffer,pAVCodePar_video->width,pAVCodePar_video->height,QImage::Format_RGBA8888);
                emit rtsp_image_sig(image);
            }
        }
        av_packet_unref(pAVPacket);
    }
    av_parser_close(pAVCodeParseContext_video);
    av_frame_free(&pAVFrame_video);
    av_frame_free(&pAVFrameRGB32_video);
    av_free(outBuffer);
    av_free(pSwsContext_video);
    avcodec_free_context(&pAVCodecContext_video);
    avformat_close_input(&format_ctx);
}

 

上一篇:RTSP拉流平台EasyNVR、EasyDSS如何自主合并TS文件为MP4格式播放?


下一篇:海康|大华网络摄像机RTSP URL格式组成及参数配置