10

I'm processing a bunch of frames from an RTSP stream using ffmpeg. I end up doing a lot of processing on these frames, which means that I'm not always pulling in real-time. If the buffer gets full, the process hangs. I'm wondering if one of the following solutions is feasible/fixes the problem, and if so, how I would implement it using the ffmpeg libraries:

1) Is there a way to clear the buffer if I ever reach a point where it's hanging? (I can determine when it's hung, I just don't know what to do about it).

2) Is there a way to make the buffer overwrite the old data, and just always read the most recent data? It doesn't matter to me if I lose frames.

3) I've already discovered that I can make the buffer arbtrarily large with: av_dict_set(&avd, "buffer_size", "655360", 0);. This could be a solution, but I don't know how large/small it needs to be, because I don't know how long the stream will post video for?

4) Is this just a bug that I need to bring up with the ffmpeg people?

5) Something else I haven't considered?

while(av_read_frame(context, &(packet)) >= 0 && fcount < fps*SECONDS) {
    clock_t start, end;
    int ret = avcodec_send_packet(codec_context, packet);
    if(!(packet->stream_index == video_stream_index)) {
      continue;
    }

    if (ret == AVERROR(EAGAIN) || ret == AVERROR(EINVAL)) {
      continue;
    } else if (ret < 0) {
      cerr << "Error while decoding frame " << fcount << endl;
      exit(1);
    }

    ret = avcodec_receive_frame(codec_context, frame);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR(EINVAL)) {
      continue;
    } else if (ret < 0) {
      cerr << "Error while decoding frame " << fcount << endl;
      exit(1);
    }

    sws_scale(img_convert_ctx, frame->data, frame->linesize, 0,
              codec_context->height, picture_rgb->data, picture_rgb->linesize);

    if(!frame) {
      cerr << "Could not allocate video frame" << endl;
      exit(1);
    }

    if(codec_context == NULL) {
      cerr << "Cannot initialize the conversion context!" << endl;
      exit(1);
    }

    // Do something with the frame here

    fcount++;
    av_packet_unref(&(packet));

}

I have added the code that causes the program to hang.

Derek Halden
  • 2,223
  • 4
  • 17
  • 26
  • 2
    There's no code here, so it's anyone's guess what's wrong. This isn't enough information to reproduce the problem. – tadman Aug 29 '17 at 16:00
  • 1
    @tadman, Is that enough code for you to guess what's wrong? I was trying to suggest that I had debugged the problem, and discovered that the buffer was the problem. Which is why I had suggested specific solutions that I was asking about. I was really mainly looking for if those solutions were feasible. But I understand why you wanted to see code, so I have added it. – Derek Halden Aug 29 '17 at 16:24
  • 1
    Context is important. Your original question was a little too hypothetical. – tadman Aug 29 '17 at 17:11
  • Does this solution here help? https://stackoverflow.com/questions/14558172/ffmpeg-av-read-frame-need-very-long-time-to-stop – kvr Sep 05 '17 at 04:05
  • @kvr I will test, probably tomorrow, and let you know if that fixes it. – Derek Halden Sep 05 '17 at 20:56
  • @kvr, that didn't appear to help. – Derek Halden Sep 05 '17 at 23:13
  • Are you displaying the frames? If so, how? Plus, the conversion to RGB will have a massive overhead. – WLGfx Sep 06 '17 at 15:22
  • @WLGfx no, I am not displaying the frames. I do write the frames to a disc. I also do convert to RGB. I'm not especially interested in changing any part of the process of what I do with the frames. I would like to adjust the streaming part of the process to deal with the problem. – Derek Halden Sep 06 '17 at 16:46
  • What format are you writing the frames to disk in? – WLGfx Sep 06 '17 at 17:55
  • @WLGfx jpg when I do write them. Writing the frames to disk is actually not something the code has to (or will do). It's just something I'm doing for testing purposes. – Derek Halden Sep 07 '17 at 20:37
  • I'm curious as to why you don't just record the TS stream and save 99% overhead. Processing/decoding can be done at anytime. – WLGfx Sep 07 '17 at 20:40
  • @WLGfx, It needs to be somewhere close to realtime processing of the stream. It's just one of the constraints of what I am doing. – Derek Halden Sep 07 '17 at 20:43
  • https://stackoverflow.com/a/33939577/2979092 might help. But my thoughts are reducing overhead for realtime and cutting out the conversion to RGB which means handling the YUV frame directly when writing the frames out. – WLGfx Sep 07 '17 at 20:49
  • @WLGfx, I mean, that might be helpful, but I don't think that fixes my problem. Which has to do with the rate at which I read frames from the stream is slower than the rate at which the stream adds frames. I could probably improve that rate with the info above, but I doubt that gets me to a fast enough rate, because of hardware limitations. – Derek Halden Sep 07 '17 at 21:00
  • FFMpeg under the hood will do a lot of processing you don't see and uses a lot of CPU, so long as you can handle all the input fast enough you are basically clearing out its internal buffers. I've faced that problem myself with an 800Mhz single core Android device decoding live TV streams. I still used threading even on the single core because idle time was handed to other threads. Your main problem at the moment is clearing out ffmpegs internal buffers and handling them elsewhere. One thread to read the packets, another to decode. – WLGfx Sep 07 '17 at 21:06

3 Answers3

0

You could try you hand at multithreading. Each frame gets processed by a separate thread (use a threadpool).

If you require a sequential finishing order you will have to use some structure (queue?) to bring order in unordered threads finishing times.

Surt
  • 15,501
  • 3
  • 23
  • 39
  • Unfortunately, I don't think that means I would be able to process the frames faster than they are being added from the stream. One of the reasons this is slow, is that I am running it on something akin to a raspberry pi in terms of processing power. Also, I am already multithreading another piece of the process. – Derek Halden Aug 29 '17 at 16:37
0

Buffer size is a socket option, and under the hood ffmpeg may block on their socket recv call if the buffer is full. After looking at the ffmpeg code, it appears they have a FIFO size option for a circular buffer on pure UDP streams, but this is disabled for RTP streams.

To ensure the buffer never gets full I would run all pkt recv and frame decode operations in one thread and feed it to my own thread-safe circular buffer. A raspi should be able to keep up with decoding, but if not I would look into hardware assisted decoding. The point is, make sure your demuxing and decoding are maintaining realtime speed. You can handle all your resizing, processing, and frame writing in other threads and set the circular buffer size accordingly to handle overflow.

It appears you are trying to write raw frames in realtime so a word of warning: obviously, fps and frame size plays a role in the speed of everything, but I suspect you will see a lot of dropped frames.

Mike
  • 562
  • 3
  • 15
  • Dropping frames is fine. I seem to be running into a different problem when I'm decoding too fast. I.e. it was erroring on me when I put the demuxing/decoding into a separate thread. – Derek Halden Sep 06 '17 at 21:51
  • Also, I get separate performance boosts from running my other process in a separate thread. For that reason multithreading this part of the process isn't ideal to me. – Derek Halden Sep 06 '17 at 22:15
  • Do you have any code for that? I'm not very familiar with ffmpeg – Saeed Masoomi Jan 01 '21 at 09:41
0

This should be a hint not a direct solution for your problem. Buffering while converting stream to frames with ffmpeg

What you should try is to record the stream and cache it to the storage, then you convert it. What you also can try is caching the buffer to the disk and refeed the ffmpeg process.

A hacky solution would be to convert the RTSP Stream to UDP which is in fact easy af and then use the ffmpeg internal buffer options available for UDP which are disabled for RTSP