8

I'm developing a Python module, with OpenCV, that connects to an RTSP stream to perform some preprocessing on the video (mostly, reducing fps and resolution), and then store it in the file system.

But, even after trying several codecs, looking for similar developments... I always end up with an empty video. I've seen this other thread (cv::VideoWriter yields unreadable video), which may be similar, but was developed on C++.

Has anyone worked on this? I normally use a sample RTSP stream as reference, such as rtsp://freja.hiof.no:1935/rtplive/definst/hessdalen03.stream, and can receive and even watch the stream from VLC correctly.

I've seen quite a lot of threads discussing how to capture video from an RTSP stream, or how to work with VideoWriters and VideoReaders classes and video files, but almost nothing combining the two.

Any help would be highly appreciated :) Thanks!!


Edit 1: Sample code used to store the frames.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2
import numpy

# Test frame.
width, height = 400, 300
width_2, height_2 = int(width / 2), int(height / 2)
frame = numpy.zeros((height, width, 3), numpy.uint8)
cv2.rectangle(frame, (0, 0), (width_2, height_2), (255, 0, 0), cv2.FILLED)
cv2.rectangle(frame, (width_2, height_2), (width, height), (0, 255, 0), cv2.FILLED)

frames = [frame for _ in range(100)]
fps = 25

# Define the codec.
#fourcc = cv2.VideoWriter_fourcc(*'X264')
#fourcc = cv2.VideoWriter_fourcc(*'XVID')
fourcc = cv2.VideoWriter_fourcc(*'MJPG')

# Create VideoWriter object
out = cv2.VideoWriter(filename='video.avi',
                      fourcc=fourcc,
                      apiPreference=cv2.CAP_FFMPEG,
                      fps=float(fps),
                      frameSize=(width, height),
                      isColor=True)

result = 0
for frame in frames:
    result += 0 if out.write(frame) is None else 1
print(result)

out.release()

Edit 2: Solution

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2
import numpy

# Test frame.
width, height = 400, 300
width_2, height_2 = int(width / 2), int(height / 2)

frame1 = numpy.zeros((height, width, 3), numpy.uint8)
cv2.rectangle(frame1, (0, 0), (width_2, height_2), (255, 0, 0), cv2.FILLED)
cv2.rectangle(frame1, (width_2, height_2), (width, height), (0, 255, 0), cv2.FILLED)
cv2.imwrite('frame1.jpg', frame1)

frame2 = numpy.zeros((height, width, 3), numpy.uint8)
cv2.rectangle(frame2, (width_2, 0), (width, height_2), (255, 0, 0), cv2.FILLED)
cv2.rectangle(frame2, (0, height_2), (width_2, height), (0, 255, 0), cv2.FILLED)
cv2.imwrite('frame2.jpg', frame2)

range1 = [frame1 for _ in range(10)]
range2 = [frame2 for _ in range(10)]
frames = range1 + range2 + range1 + range2 + range1
fps = 2

# Define the codec.
fourcc = cv2.VideoWriter_fourcc(*'MJPG')

# Create VideoWriter object
out = cv2.VideoWriter('video.avi', fourcc, float(fps), (width, height))

for frame in frames:
    out.write(frame)

out.release()
nathancy
  • 42,661
  • 14
  • 115
  • 137
rafamartinc
  • 125
  • 1
  • 1
  • 10
  • Are you able to see the stream? I mean with imshow instead of saving it to disk? Are you able to create a video with a dummy image, for example an image all red?You should go step by step, first make sure you can connect and retrieve the images, then focus on saving.... the link you provide looks more a rtmp link than a rtsp, usually port 554 is for rtsp and 1935 for rtmp... – api55 Mar 13 '19 at 12:31
  • Hi @api55 ! Yes, I can see the frames retrieved from the RTSP stream. In fact, for now I was storing them as jpeg images instead. But storing them as a video should be more efficient. But, as you said, it's likely the saving what is going wrong. I prepared a dummy frame with numpy, and the video still comes out wrong. See code attached, resulting in a video with 240.924 bytes that can't be displayed by VLC. – rafamartinc Mar 13 '19 at 14:14
  • 1
    Great, now your question has an MCVE. I know that getting the fourcc + extension is most of a time a horrible experience... try first to see what codecs are installed in your machine, instead of a FOURCC pass -1 and a menu should appear and you can select a codec – api55 Mar 13 '19 at 14:37
  • It returns (on Ubuntu) the message 'OpenCV: FFMPEG: format avi / AVI (Audio Video Interleaved)', and a list with 489 tags such as 'fourcc tag 0x34363248/'H264' codec_id 001B'. For that very one, as an example, it replies 'Could not find encoder for codec id 27: Encoder not found' when trying to use it as fourcc tag, even though it's on the list. – rafamartinc Mar 13 '19 at 16:15

1 Answers1

10

Here's a RTSP stream to video widget. I would recommend creating another thread for obtaining the frames as cv2.VideoCapture.read() is blocking. This can be expensive and cause latency as the main thread has to wait until it has obtained a frame. By putting this operation into a separate thread that just focuses on grabbing frames and processing/saving the frames in the main thread, it dramatically improves performance. You also can experiment with other codecs but using MJPG should be safe since its built into OpenCV. I used my IP camera stream and saved the frames to output.avi. Be sure to change rtsp_stream_link to your own RTSP stream link. :)

Output Video Screenshot

from threading import Thread
import cv2

class RTSPVideoWriterObject(object):
    def __init__(self, src=0):
        # Create a VideoCapture object
        self.capture = cv2.VideoCapture(src)

        # Default resolutions of the frame are obtained (system dependent)
        self.frame_width = int(self.capture.get(3))
        self.frame_height = int(self.capture.get(4))
        
        # Set up codec and output video settings
        self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.output_video = cv2.VideoWriter('output.avi', self.codec, 30, (self.frame_width, self.frame_height))

        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()

    def show_frame(self):
        # Display frames in main program
        if self.status:
            cv2.imshow('frame', self.frame)
        
        # Press Q on keyboard to stop recording
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            self.output_video.release()
            cv2.destroyAllWindows()
            exit(1)

    def save_frame(self):
        # Save obtained frame into video output file
        self.output_video.write(self.frame)

if __name__ == '__main__':
    rtsp_stream_link = 'your stream link!'
    video_stream_widget = RTSPVideoWriterObject(rtsp_stream_link)
    while True:
        try:
            video_stream_widget.show_frame()
            video_stream_widget.save_frame()
        except AttributeError:
            pass

Related camera/IP/RTSP/streaming, FPS, video, threading, and multiprocessing posts

  1. Python OpenCV streaming from camera - multithreading, timestamps

  2. Video Streaming from IP Camera in Python Using OpenCV cv2.VideoCapture

  3. How to capture multiple camera streams with OpenCV?

  4. OpenCV real time streaming video capture is slow. How to drop frames or get synced with real time?

  5. Storing RTSP stream as video file with OpenCV VideoWriter

  6. OpenCV video saving

  7. Python OpenCV multiprocessing cv2.VideoCapture mp4

nathancy
  • 42,661
  • 14
  • 115
  • 137
  • Thank you @nathancy !!! My VLC was also displaying some errors, even when the video was already being correctly stored, so that messed up things a bit. But I could fix everything thanks to your code. I'll add the fixed code on top. – rafamartinc Mar 21 '19 at 10:45
  • Glad to help! If you're getting choppy frames, play with the `cv2.VideoWriter()` FPS parameter or access your IP camera to adjust the camera's FPS settings. – nathancy Mar 21 '19 at 20:27
  • Thanks for help is there a way record audio also in another format like mp4 etc.. – sanjay Sep 13 '20 at 08:35
  • That's out of the scope of this question, you should open another question for that – nathancy Oct 23 '20 at 21:21