Reputation: 1
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