rayion
rayion

Reputation: 1

How to read RTSP video streams with low latency

I want to make a code to read rtsp video stream with low latency, I just found the libav library to reduce the frame reading and decoding latency, but it still has a latency of about 230ms, can it be faster?

Here is my code, I don't know why the displayed image is black by running this code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <sstream>
#include <queue>
#include <omp.h>
#include <thread>
#include <chrono>
#include <vector>
#include "opencv2/opencv.hpp"
extern "C"
{
#include <libavformat/avformat.h>
#include <libavutil/error.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}

#define ERR_BUFF_SIZE 1024
bool is_ready = false;
std::mutex mtx;
std::queue<cv::Mat> frames;
std::chrono::_V2::system_clock::time_point td;
std::vector<AVPacket> pkt_queue;

void show()
{
    cv::namedWindow("test");

    while (true)
    {
        if (is_ready == true)
        {
            break;
        }

        if (!frames.empty())
        {

            mtx.lock();
            cv::Mat f = frames.front();
            frames.pop();
            // const auto te = std::chrono::system_clock::now();
            mtx.unlock();
            // int ted = std::chrono::duration_cast<std::chrono::microseconds>(te-td).count();
            // std::cout << "Now  show  time spent :" <<ted << "us " << std::endl;
            // std::cout << "get !"<< std::endl;
            cv::imshow("test", f);
            cv::waitKey(1);
        }
    }

    cv::destroyWindow("test");
}

int main(int argc, char **argv)
{

    const char *url = "rtsp://admin:[email protected]";

    av_register_all();

    AVFormatContext *container = nullptr;
    AVDictionary *options = NULL;
    av_dict_set(&options, "max_delay", "0", 0);

    av_dict_set(&options, "preset", "ultrafast", 0);

    av_dict_set(&options, "rtsp_transport", "tcp", 0);

    av_dict_set(&options, "tune", "zerolatency", 0);

    if (avformat_open_input(&container, url, NULL, &options) != 0)
    {
        char error_buffer[ERR_BUFF_SIZE];
        av_strerror(errno, error_buffer, sizeof(error_buffer));
        std::cerr << "Failed to open input: " << error_buffer << std::endl;
        return 1;
    }

    if (avformat_find_stream_info(container, NULL) < 0)
    {
        std::cerr << "Failed to find stream info" << std::endl;
        return 1;
    }

    av_dump_format(container, 0, url, 0);

    int video_stream = -1;
    AVCodecParameters *codecContext = nullptr;
    AVCodec *codec = nullptr;
    for (unsigned i = 0; i < container->nb_streams; i++)
    {
        auto codecpar = container->streams[i]->codecpar;
        if (codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            video_stream = i;
            if (AVCodecID::AV_CODEC_ID_HEVC != codecpar->codec_id)
                std::cout << "now same codec" << std::endl;

            codec = avcodec_find_decoder(codecpar->codec_id);
            codecContext = codecpar;
            break;
        }
    }

    if (!codec)
    {
        std::cerr << "Failed to find codec" << std::endl;
        return 1;
    }

    AVCodecContext *decoderContext = avcodec_alloc_context3(codec);
    int err = avcodec_parameters_to_context(decoderContext, codecContext);
    if (err < 0)
    {
        std::cerr << "Failed to copy codec parameters to decoder context" << std::endl;
        return 1;
    }

    decoderContext->flags2 |= AV_CODEC_FLAG2_FAST;

    if (avcodec_open2(decoderContext, codec, nullptr) < 0)
    {
        std::cerr << "Failed to open codec" << std::endl;
        return 1;
    }

    int i = 0;
    std::thread t(show);

    while (true)
    {
        const auto ta = std::chrono::system_clock::now();
        AVPacket packet;
        av_init_packet(&packet);
        if (av_read_frame(container, &packet) < 0)
        {
            std::cerr << "Failed to read frame" << std::endl;
            return 1;
        }
        const auto tb = std::chrono::system_clock::now();
        int tba = std::chrono::duration_cast<std::chrono::microseconds>(tb - ta).count();
        std::cout << "Now  read time spent :" << tba << "us " << std::endl;
        if (packet.stream_index == video_stream)
        {
            AVFrame *frame = av_frame_alloc();
            int got_frame = 0;

            int len = avcodec_decode_video2(decoderContext, frame, &got_frame, &packet);

            const auto tc = std::chrono::system_clock::now();
            int tcb = std::chrono::duration_cast<std::chrono::microseconds>(tc - tb).count();
            std::cout << "Now  decode  time spent :" << tcb << "us " << std::endl;

            if (len >= 0 && got_frame)
            {
                cv::Mat image(frame->height, frame->width, CV_8UC1, frame->data[0], frame->linesize[0]);
                mtx.lock();
                frames.push(image);
                td = std::chrono::system_clock::now();
                mtx.unlock();
            }
            int tdc = std::chrono::duration_cast<std::chrono::microseconds>(td - tc).count();
            std::cout << "Now  get frame   time spent :" << tdc << "us " << std::endl;

            av_frame_free(&frame);
        }

        av_packet_unref(&packet);
        i++;
        if (i > 10000)
        {
            is_ready = true;
            break;
        }
    }
    t.join();
    avcodec_close(decoderContext);
    avformat_close_input(&container);

    return 0;
}

I think this delay is still too slow, but I don't know if there's a better setting.

Upvotes: 0

Views: 864

Answers (0)

Related Questions