1

Trying to accept multiple RTSP addresses via command line but I keep on failing. This would be my input down below:

python rtsp.py --rtsp --uri rtsp://admin:password@192.168.1.63:554 rtsp://admin:password@192.168.1.61:554 rtsp://admin:password@192.168.1.62:554

It doesn't show the videos simultaneously which I want it to, it shows one by one after you close them.

import sys
import argparse
import cv2
from multiprocessing import Process 

WINDOW_NAME = 'Network Camera Demo'


def parse_args():
    # Parse input arguments
    desc = 'Capture and display live video feed'
    parser = argparse.ArgumentParser(description=desc)
    parser.add_argument('--rtsp', dest='use_rtsp',
                        help='use IP CAM (remember to also set --uri)',
                        action='store_true')
    parser.add_argument('--uri', dest='rtsp_uri',
                        help='RTSP URI, e.g. rtsp://192.168.1.64:554',
                        default=None, type=str)
    parser.add_argument('--latency', dest='rtsp_latency',
                        help='latency in ms for RTSP [200]',
                        default=25, type=int)
    parser.add_argument('--width', dest='image_width',
                        help='image width [1920]',
                        default=1920, type=int)
    parser.add_argument('--height', dest='image_height',
                        help='image height [1080]',
                        default=1080, type=int)
    args = parser.parse_args()
    return args


def open_cam_rtsp(uri, width, height, latency):
    gst_str = ('rtspsrc location={} latency={} ! '
               'rtph264depay ! h264parse ! omxh264dec ! '
               'nvvidconv ! '
               'video/x-raw, width=(int){}, height=(int){}, '
               'format=(string)BGRx ! '
               'videoconvert ! appsink').format(uri, latency, width, height)
    return cv2.VideoCapture(gst_str, cv2.CAP_GSTREAMER)

def open_window(width, height):
    cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
    cv2.resizeWindow(WINDOW_NAME, width, height)
    cv2.moveWindow(WINDOW_NAME, 0, 0)
    cv2.setWindowTitle(WINDOW_NAME, 'Network Cam Demo')


def read_cam(cap):
    show_help = True
    full_scrn = False
    help_text = '"Esc" to Quit, "H" for Help, "F" to Toggle Fullscreen'
    font = cv2.FONT_HERSHEY_PLAIN
    while True:
        if cv2.getWindowProperty(WINDOW_NAME, 0) < 0:
            # Check to see if the user has closed the window
            # If yes, terminate the program
            break
        _, img = cap.read() # grab the next image frame from camera
        if show_help:
            cv2.putText(img, help_text, (11, 20), font,
                        1.0, (32, 32, 32), 4, cv2.LINE_AA)
            cv2.putText(img, help_text, (10, 20), font,
                        1.0, (240, 240, 240), 1, cv2.LINE_AA)
        cv2.imshow(WINDOW_NAME, img)
        key = cv2.waitKey(10)
        if key == 27: # ESC key: quit program
            break

def main(rtsp_uri):
    # args = parse_args()
    print('Called with args:')
    print(args)
    print('OpenCV version: {}'.format(cv2.__version__))

    cap = open_cam_rtsp(rtsp_uri,
                        args.image_width,
                        args.image_height,
                        args.rtsp_latency)

    if not cap.isOpened():
        sys.exit('Failed to open camera!')

    open_window(args.image_width, args.image_height)
    read_cam(cap)

    cap.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    args = parse_args()
      for rtsp_uri in args.rtsp_uri:
          process = Process(target=main, args=(rtsp_uri))
          process.start()
          process.join()

I am kind of stuck on how to actually get it to accept multiple arguments like certain frameworks e.g.

gst-launch-1.0 rtspsrc location=rtsp://admin:password@192.168.1.61 ! rtph264depay ! h264parse ! omxh264dec ! nvvidconv ! videoconvert ! appsink
rtspsrc location=rtsp://admin:password@192.168.1.62 ! rtph264depay ! h264parse ! omxh264dec ! nvvidconv ! videoconvert ! appsink 

Edit: With the help of Right Leg, I have managed to solve the problem that exists within my code which was due to a misunderstanding of how the Process actually works in multiprocessing.

For some strange reason, action=append and nargs='+ has to be used in order to accept multiple arguments from the command line.

Final code as shown below:

 parser.add_argument('--uri', dest='rtsp_uri', action='append'
                        help='RTSP URI, e.g. rtsp://192.168.1.64:554',
                        default=None, type=str, nargs='+')
...

if __name__ == '__main__':
    args = parse_args()
    processes = []
    for args.rtsp_uri in args.rtsp_uri:
        process = Process(target=main, args=(args.rtsp_uri))
        processes.append(process)
        process.start()

    for process in processes:
        process.join()
penguin
  • 628
  • 2
  • 10
  • 22
  • 1
    Possible duplicate of [argparse option for passing a list as option](https://stackoverflow.com/questions/15753701/argparse-option-for-passing-a-list-as-option) – remeus Jul 30 '19 at 12:57
  • I edited the question, it really isn't. Also I am trying to accept the arguments into different processes as well. Didn't turn out as i expected it'd be. – penguin Jul 30 '19 at 13:07
  • How does `nargs` not solve your problem? – blues Jul 30 '19 at 14:11
  • It does, just partially, not fully. – penguin Jul 31 '19 at 02:46

1 Answers1

1

In your main, for each url, you create a process, start it, and join it.

if __name__ == '__main__':
    args = parse_args()
      for rtsp_uri in args.rtsp_uri:
          process = Process(target=main, args=(rtsp_uri))
          process.start()
          process.join()

The problem is that Process.join waits for the process to end. As a result, the processes are created, started, and thus run sequentially: only once a process has ended will the next start.

You need to start all the processes, and only then join them:

if __name__ == '__main__':
    args = parse_args()
      processes = []
      for rtsp_uri in args.rtsp_uri:
          process = Process(target=main, args=(rtsp_uri))
          processes.append(process)
          process.start()

      for process in processes:
          process.join()
Right leg
  • 16,080
  • 7
  • 48
  • 81
  • I was actually thinking about this a while ago. Will go and test it out. Found out that had to pass `args.rtsp_uri` as the main parameter instead of `rtsp_uri` for the processes. – penguin Jul 31 '19 at 03:58