How to read Youtube live stream using openCV python?
Asked Answered
B

4

23

I want to read a live stream from youtube to perform some basic CV things, probably we have to somehow strip of the youtube URL to convert it in a format that might be readable by openCV like?:

cap = cv2.VideoCapture('https://www.youtube.com/watch?v=_9OBhtLA9Ig')

has anyone done it?

Branching answered 26/3, 2017 at 17:30 Comment(1)
Try using urllib See THIS PAGENieman
F
27

I am sure you already know the answer by now, but I will answer for others searching the same topic. You can do this by using Pafy

(probably together with youtube_dl).

import pafy
import cv2

url = "https://www.youtube.com/watch?v=_9OBhtLA9Ig"
video = pafy.new(url)
best = video.getbest(preftype="mp4")

capture = cv2.VideoCapture(best.url)
while True:
    grabbed, frame = capture.read()
    # ...

And that should be it.

Feleciafeledy answered 6/4, 2018 at 18:52 Comment(5)
It works well for a video. But I am trying the same for a live streaming and the VideoCapture read method gets stuck after around 40 or 50 frames. Any idea?Pinwheel
@Pinwheel This seems to work fine for a live stream as well. Perhaps it was the particular stream?Turbit
@Pinwheel try garbage collectionWickner
this did not work certificate verify failed. maybe some changes in youtube site?Subversion
Since youtube dl is very outdated, you might run into problems like disklike count & etcHubble
E
6

I've added Youtube URL source support in my VidGear Python Library that automatically pipelines YouTube Video into OpenCV by providing its URL only. Here is a complete python example:

# import libraries
from vidgear.gears import CamGear
import cv2

stream = CamGear(source='https://youtu.be/dQw4w9WgXcQ', stream_mode = True, logging=True).start() # YouTube Video URL as input

# infinite loop
while True:
    
    frame = stream.read()
    # read frames

    # check if frame is None
    if frame is None:
        #if True break the infinite loop
        break
    
    # do something with frame here
    
    cv2.imshow("Output Frame", frame)
    # Show output window

    key = cv2.waitKey(1) & 0xFF
    # check for 'q' key-press
    if key == ord("q"):
        #if 'q' key-pressed break out
        break

cv2.destroyAllWindows()
# close output window

# safely close video stream.
stream.stop()

Code Source

Eggnog answered 8/11, 2021 at 4:41 Comment(3)
nice source url. Pity I get invalid source on your example and my test url. Both of which are in fact valid and work in a browser. The exact error code is: RuntimeError: [CamGear:ERROR] :: Source is invalid, CamGear failed to initialize stream on this source!Holography
@MrPurple if they're live youtube video source, then you need GSTREAMER backend(or OpenCV compiled with GSTREAMER support) as mentioned in docs. Otherwise if URL not live then it should work seemlessly, otherwise raise issue here: github.com/abhiTronix/vidgear/issuesEggnog
I think you're comment is sufficiently applicable to warrant updating your answer as the OP is directly asking about live streams.Holography
J
2

After 100-120 frames the answer from @lee hannigan was crashing on me for a live stream on youtube.

I worked out a method with Pafy to just grab x number of frames and splice them together. This ended up poorly stitching the chunks together though, and gave choppy results. Pafy may not be designed for live streams, I couldn't find a way to stitch the frames together seamlessly.

What worked in the end is below, slightly modified from guttentag_liu's answer on this post. It takes a few more packages, and is lengthy, but works. Because the file is live, it needs to be in chunks, hence saving to a temporary file. You could probably do your openCV work on each chunk, then save to a file in the end instead of re-opening.

# pip install urllib
# pip install m3u8
# pip install streamlink
from datetime import datetime, timedelta, timezone
import urllib
import m3u8
import streamlink
import cv2 #openCV

def get_stream(url):

    """
    Get upload chunk url
    input: youtube URL
    output: m3u8 object segment
    """
    #Try this line tries number of times, if it doesn't work, 
    # then show the exception on the last attempt
    # Credit, theherk, https://mcmap.net/q/86001/-how-to-retry-after-exception
    tries = 10
    for i in range(tries):
    try:
        streams = streamlink.streams(url)
    except:
        if i < tries - 1: # i is zero indexed
            print(f"Attempt {i+1} of {tries}")
            time.sleep(0.1) #Wait half a second, avoid overload
            continue
        else:
            raise
    break

    stream_url = streams["best"] #Alternate, use '360p'

    m3u8_obj = m3u8.load(stream_url.args['url'])
    return m3u8_obj.segments[0] #Parsed stream


def dl_stream(url, filename, chunks):
    """
    Download each chunk to file
    input: url, filename, and number of chunks (int)
    output: saves file at filename location
    returns none.
    """
    pre_time_stamp = datetime(1, 1, 1, 0, 0, tzinfo=timezone.utc)

    
    #Repeat for each chunk
    #Needs to be in chunks because 
    #  1) it's live
    #  2) it won't let you leave the stream open forever
    i=1
    while i <= chunks:
       
        #Open stream
        stream_segment = get_stream(url)
    
        #Get current time on video
        cur_time_stamp = stream_segment.program_date_time
        #Only get next time step, wait if it's not new yet
        if cur_time_stamp <= pre_time_stamp:
            #Don't increment counter until we have a new chunk
            print("NO   pre: ",pre_time_stamp, "curr:",cur_time_stamp)
            time.sleep(0.5) #Wait half a sec
            pass
        else:
            print("YES: pre: ",pre_time_stamp, "curr:",cur_time_stamp)
            print(f'#{i} at time {cur_time_stamp}')
            #Open file for writing stream
            file = open(filename, 'ab+') #ab+ means keep adding to file
            #Write stream to file
            with urllib.request.urlopen(stream_segment.uri) as response:
                html = response.read()
                file.write(html)
            
            #Update time stamp
            pre_time_stamp = cur_time_stamp
            time.sleep(stream_segment.duration) #Wait duration time - 1

            i += 1 #only increment if we got a new chunk

    return None

def openCVProcessing(saved_video_file):
    '''View saved video with openCV
    Add your other steps here'''
    capture = cv2.VideoCapture(saved_video_file)

    while capture.isOpened():
        grabbed, frame = capture.read()  #read in single frame
        if grabbed == False:
            break

        #openCV processing goes here
        #
        
        cv2.imshow('frame',frame)  #Show the frame
        
        #Shown in a new window, To exit, push q on the keyboard
        if cv2.waitKey(20) & 0xFF == ord('q'):
            break

    capture.release()
    cv2.destroyAllWindows()  #close the windows automatically
                  
tempFile = "temp.ts"  #files are format ts, open cv can view them
videoURL = "https://www.youtube.com/watch?v=_9OBhtLA9Ig"

dl_stream(videoURL, tempFile, 3)
openCVProcessing(tempFile)
Jackfruit answered 12/5, 2021 at 2:31 Comment(2)
Hey Jeff! Great work! I've tried this out and it has worked inconsistently for me. Sometimes, it's great and works as expected and other times, I get a "PlugIn Error like this: "PluginError: Unable to open URL: youtube.com/get_video_info (404 Client Error: Not Found for url: youtube.com/…) I'm assuming this has something to do with me trying to access the page too often. Not sure. Any ideas?Discursion
@Discursion I'm now getting the same pluginError and HTTPError (I wasn't before). If you go to the error url listed, it gives this error message: status=fail&errorcode=2&reason=Invalid+parameters. I added a loop to retry a few times to the above answer and wait the duration of the segment to avoid overloading the system. If you like the answer, kindly upvote :)Jackfruit
D
-1

Probably, because Youtube does not provide the like/dislike counts anymore, the first solution gives error. As a solution, you should comment the 53rd and 54th lines in the backend_youtube_dl.py in pafy package file as in the image below, after that the code in the first solution will work:

Secondly, you can get not get audio with OpenCV, it is a computer vision library, not multimedia. You should try other options for that.

Deka answered 17/9, 2022 at 8:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.