ffmpeg面向对象——rtsp拉流探索(1)

标准rtsp协议的基石是tcp,本节探索下ffmpeg的rtsp拉流协议tcp的socket创建及链接。

1.tcp创建及链接的流程图及对象图

tcp创建及链接的流程图,如下:
在这里插入图片描述

tcp创建及链接的对象图,如下:

在这里插入图片描述

2.解析

ffmpeg拉流,从avformat_open_input接口开始,去除与rtsp拉流无关的代码后,如下:


int avformat_open_input(AVFormatContext **ps, const char *filename,
                        const AVInputFormat *fmt, AVDictionary **options)
{
    AVFormatContext *s = *ps;
    FFFormatContext *si;
    AVDictionary *tmp = NULL;
    int ret = 0;

    if (!s && !(s = avformat_alloc_context()))
        return AVERROR(ENOMEM);
    si = ffformatcontext(s);
    if (!s->av_class) {
        av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
        return AVERROR(EINVAL);
    }

    if (options)
        av_dict_copy(&tmp, *options, 0);

    if ((ret = av_opt_set_dict(s, &tmp)) < 0)
        goto fail;

    if (!(s->url = av_strdup(filename ? filename : ""))) {
        ret = AVERROR(ENOMEM);
        goto fail;
    }

    if ((ret = init_input(s, filename, &tmp)) < 0)
        goto fail;
    s->probe_score = ret;


    if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) 
    {
        av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
        ret = AVERROR(EINVAL);
        goto fail;
    }

    /* Check filename in case an image number is expected. */
    if (s->iformat->flags & AVFMT_NEEDNUMBER) {
        if (!av_filename_number_test(filename)) {
            ret = AVERROR(EINVAL);
            goto fail;
        }
    }

    s->duration = s->start_time = AV_NOPTS_VALUE;

    /* Allocate private data. */
    if (s->iformat->priv_data_size > 0) 
    {
        if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
            ret = AVERROR(ENOMEM);
            goto fail;
        }
        if (s->iformat->priv_class) 
        {
            *(const AVClass **) s->priv_data = s->iformat->priv_class;
            av_opt_set_defaults(s->priv_data);
            if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
                goto fail;
        }
    }

    if (s->iformat->read_header)
    {
        if ((ret = s->iformat->read_header(s)) < 0) {
            if (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP)
                goto close;
            goto fail;
        }
    }


    if ((ret = avformat_queue_attached_pictures(s)) < 0)
        goto close;

    si->raw_packet_buffer_size = 0;

    update_stream_avctx(s);

    if (options) {
        av_dict_free(options);
        *options = tmp;
    }
    *ps = s;
    return 0;

close:
    if (s->iformat->read_close)
        s->iformat->read_close(s);
fail:
    av_dict_free(&tmp);
    avformat_free_context(s);
    *ps = NULL;
    return ret;
}

其中留下了字典参数配置流程,因为基本会有所设置,参数配置参见《ffmpeg面向对象——参数配置机制及其设计模式探索》。

流程图中,输入格式匹配参见《ffmpeg面向对象-rtsp拉流相关对象》的第2节。
协议匹配机制参见《ffmpeg面向对象——拉流协议匹配机制探索》。
输入格式类与协议类什么关系参见《ffmpeg面向对象——AVInputFormat与URLProtocol啥关系

使用FFmpeg进行RTSP拉流RTSP推流可以使用C++编写程序来实现。 首先,需要在C++程序中引入FFmpeg的相关头文件和库文件。然后,可以使用以下代码来实现RTSP拉流: ``` #include <iostream> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> } int main(int argc, char* argv[]) { AVFormatContext* formatContext = nullptr; AVCodecContext* codecContext = nullptr; AVCodec* codec = nullptr; AVPacket* packet = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); int videoStreamIndex = -1; av_register_all(); std::string inputUrl = "rtsp://..."; int ret = avformat_open_input(&formatContext, inputUrl.c_str(), NULL, NULL); if (ret < 0) { std::cout << "Could not open input " << inputUrl << std::endl; return -1; } ret = avformat_find_stream_info(formatContext, NULL); if (ret < 0) { std::cout << "Could not find stream information" << std::endl; return -1; } for (unsigned int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { std::cout << "Could not find video stream" << std::endl; return -1; } codecContext = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar); codec = avcodec_find_decoder(codecContext->codec_id); if (codec == nullptr) { std::cout << "Unsupported codec" << std::endl; return -1; } ret = avcodec_open2(codecContext, codec, NULL); if (ret < 0) { std::cout << "Could not open codec" << std::endl; return -1; } while (av_read_frame(formatContext, packet) == 0) { if (packet->stream_index == videoStreamIndex) { ret = avcodec_send_packet(codecContext, packet); if (ret < 0) { std::cout << "Error sending a packet for decoding" << std::endl; break; } while (ret >= 0) { ret = avcodec_receive_frame(codecContext, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { std::cout << "Error during decoding" << std::endl; break; } // 处理解码后的图像... } } av_packet_unref(packet); } avcodec_free_context(&codecContext); avformat_close_input(&formatContext); av_packet_free(&packet); av_frame_free(&frame); return 0; } ``` 接下来,可以使用以下代码来实现RTSP推流: ``` #include <iostream> #include <chrono> extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> } int main(int argc, char* argv[]) { AVFormatContext* outFormatContext = nullptr; AVCodecContext* codecContext = nullptr; AVCodec* codec = nullptr; AVStream* stream = nullptr; AVPacket* packet = av_packet_alloc(); AVFrame* frame = av_frame_alloc(); uint8_t* buffer = nullptr; av_register_all(); std::string inputUrl = "rtsp://..."; std::string outputUrl = "rtsp://..."; int ret = avformat_network_init(); if (ret < 0) { std::cout << "Failed to initialize network" << std::endl; return -1; } ret = avformat_open_input(&outFormatContext, inputUrl.c_str(), NULL, NULL); if (ret < 0) { std::cout << "Could not open input " << inputUrl << std::endl; return -1; } ret = avformat_find_stream_info(outFormatContext, NULL); if (ret < 0) { std::cout << "Could not find stream information" << std::endl; return -1; } codecContext = avcodec_alloc_context3(NULL); codecContext->codec_id = AV_CODEC_ID_H264; codecContext->codec_type = AVMEDIA_TYPE_VIDEO; codecContext->pix_fmt = AV_PIX_FMT_YUV420P; codecContext->width = 640; codecContext->height = 480; codecContext->bit_rate = 400000; codecContext->gop_size = 10; codec = avcodec_find_encoder(codecContext->codec_id); if (codec == nullptr) { std::cout << "Unsupported codec" << std::endl; return -1; } ret = avcodec_open2(codecContext, codec, NULL); if (ret < 0) { std::cout << "Could not open codec" << std::endl; return -1; } stream = avformat_new_stream(outFormatContext, codec); if (stream == nullptr) { std::cout << "Failed allocating output stream" << std::endl; return -1; } ret = avcodec_parameters_from_context(stream->codecpar, codecContext); if (ret < 0) { std::cout << "Failed to copy codec parameters to output stream" << std::endl; return -1; } av_dump_format(outFormatContext, 0, outputUrl.c_str(), 1); ret = avio_open(&outFormatContext->pb, outputUrl.c_str(), AVIO_FLAG_WRITE); if (ret < 0) { std::cout << "Could not open output URL " << outputUrl << std::endl; return -1; } ret = avformat_write_header(outFormatContext, NULL); if (ret < 0) { std::cout << "Error writing header" << std::endl; return -1; } int bufferSize = av_image_get_buffer_size(codecContext->pix_fmt, codecContext->width, codecContext->height, 1); buffer = (uint8_t*)av_malloc(bufferSize); av_image_fill_arrays(frame->data, frame->linesize, buffer, codecContext->pix_fmt, codecContext->width, codecContext->height, 1); int frameCount = 0; auto startTime = std::chrono::steady_clock::now(); while (true) { AVStream* inStream = outFormatContext->streams[0]; ret = av_read_frame(outFormatContext, packet); if (ret < 0) { break; } if (packet->stream_index == 0) { ret = avcodec_send_packet(codecContext, packet); if (ret < 0) { std::cout << "Failed to send packet for encoding" << std::endl; break; } while (ret >= 0) { ret = avcodec_receive_frame(codecContext, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { std::cout << "Failed to receive frame for encoding" << std::endl; break; } frame->pts = frameCount++; ret = avcodec_send_frame(inStream->codecpar->codec_context, frame); if (ret < 0) { std::cout << "Failed to send frame for muxing" << std::endl; break; } while (ret >= 0) { ret = avcodec_receive_packet(inStream->codecpar->codec_context, packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { std::cout << "Failed to receive packet for muxing" << std::endl; break; } av_packet_rescale_ts(packet, codecContext->time_base, inStream->time_base); packet->stream_index = inStream->index; ret = av_interleaved_write_frame(outFormatContext, packet); av_packet_unref(packet); } } } av_packet_unref(packet); auto endTime = std::chrono::steady_clock::now(); if (std::chrono::duration_cast<std::chrono::seconds>(endTime - startTime).count() >= 30) { break; } } av_write_trailer(outFormatContext); avcodec_free_context(&codecContext); avformat_close_input(&outFormatContext); av_packet_free(&packet); av_frame_free(&frame); av_free(buffer); return 0; } ``` 需要注意的是,RTSP协议有时会有一些协议特性,比如需要进行RTSP握手、传输控制、流控制等等,需要在代码中进行相应的处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值