We pull the YouTube video and distribute it in real time via WebRTC



    The task is as follows. Conduct a joint viewing of the video with YouTube in real time by several users. Viewers should receive video at the same time, with a minimum delay.

    Roller like a stream


    It is clear that if each of the viewers simply starts playing the video, the goal will not be achieved, because one will receive the video faster, the other slower. Uncontrolled spread will occur.

    In order to prevent scatter, you need to distribute this video to everyone at the same time. This can be realized if you wrap the movie in Live-stream. We will show how to do this using the link of this library with ffmpeg.


    We need to implement the scheme described above. Namely, ydl connects to YouTube and starts downloading the video. FFmpeg picks up the downloaded movie, wraps it in an RTMP stream and sends it to the server. The server distributes the received stream as WebRTC in real time.

    Youtube-dl installation


    We start by installing youtube-dl. The installation process on Linux is extremely simple and described in detail in the Readme under Lin and under Win .

    1. Download.

    curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl

    2. We give launch rights

    chmod a+rx /usr/local/bin/youtube-dl

    That's all. YouTube download is ready to go.

    Take a YouTube video and look at its meta data:

    youtube-dl --list-formats https://www.youtube.com/watch?v=9cQT4urTlXM

    The result will be like this:

    [youtube] 9cQT4urTlXM: Downloading webpage
    [youtube] 9cQT4urTlXM: Downloading video info webpage
    [youtube] 9cQT4urTlXM: Extracting video information
    [youtube] 9cQT4urTlXM: Downloading MPD manifest
    [info] Available formats for 9cQT4urTlXM:
    format code  extension  resolution note
    171          webm       audio only DASH audio    8k , vorbis@128k, 540.24KiB
    249          webm       audio only DASH audio   10k , opus @ 50k, 797.30KiB
    250          webm       audio only DASH audio   10k , opus @ 70k, 797.30KiB
    251          webm       audio only DASH audio   10k , opus @160k, 797.30KiB
    139          m4a        audio only DASH audio   53k , m4a_dash container, mp4a.40.5@ 48k (22050Hz), 10.36MiB
    140          m4a        audio only DASH audio  137k , m4a_dash container, mp4a.40.2@128k (44100Hz), 27.56MiB
    278          webm       256x144    144p   41k , webm container, vp9, 30fps, video only, 6.54MiB
    242          webm       426x240    240p   70k , vp9, 30fps, video only, 13.42MiB
    243          webm       640x360    360p  101k , vp9, 30fps, video only, 20.55MiB
    160          mp4        256x144    DASH video  123k , avc1.4d400c, 15fps, video only, 24.83MiB
    134          mp4        640x360    DASH video  138k , avc1.4d401e, 30fps, video only, 28.07MiB
    244          webm       854x480    480p  149k , vp9, 30fps, video only, 30.55MiB
    135          mp4        854x480    DASH video  209k , avc1.4d401f, 30fps, video only, 42.42MiB
    133          mp4        426x240    DASH video  274k , avc1.4d4015, 30fps, video only, 57.63MiB
    247          webm       1280x720   720p  298k , vp9, 30fps, video only, 59.25MiB
    136          mp4        1280x720   DASH video  307k , avc1.4d401f, 30fps, video only, 62.58MiB
    17           3gp        176x144    small , mp4v.20.3, mp4a.40.2@ 24k
    36           3gp        320x180    small , mp4v.20.3, mp4a.40.2
    43           webm       640x360    medium , vp8.0, vorbis@128k
    18           mp4        640x360    medium , avc1.42001E, mp4a.40.2@ 96k
    22           mp4        1280x720   hd720 , avc1.64001F, mp4a.40.2@192k (best)


    Ffmpeg installation


    Next, set ffmpeg with standard spells:

    wget http://ffmpeg.org/releases/ffmpeg-3.3.4.tar.bz2
    tar -xvjf ffmpeg-3.3.4.tar.bz2
    cd  ffmpeg-3.3.4
    ./configure --enable-shared --disable-logging --enable-gpl --enable-pthreads --enable-libx264 --enable-librtmp
    make
    make install

    Check what happened

    ffmpeg -v

    Now the fun part. The youtube-dl library is for download. It's called YouTube Download. Those. You can download the youtube movie in its entirety and after that just stream it through ffmpeg as a file.

    But imagine such a user case. Marketer, manager and programmer are sitting in a web conference. The marketer wants to show everyone in real time a YouTube video that weighs, say, 300 megabytes. Agree, there will be some awkwardness, if you need to deflate the entire movie before you start showing it.

    1. The marketer says - “And now, colleagues, let's see this video with cats, it fully meets our strategy for entering the market”, and presses the “show everyone video” button.
    2. The preloader appears on the screen: “Wait, the video with the cats is downloading. It takes no more than 10 minutes. ”
    3. The manager is going to drink coffee, and the programmer is reading habr.

    In order not to make people wait, we need real time. You need to pick up the video directly during download, on the fly to wrap in a stream and distribute in real time. Next we will show how to do it.

    Transferring data from youtube-dl to ffmpeg


    The youtube-dl grabber saves the stream to the file system. You need to connect to this stream and organize the reading from the ffmpeg file as it is downloaded using youtube-dl.

    To combine these two processes: downloading and streaming ffmpeg, we need a small binding script.

    #!/usr/bin/python
    import subprocess
    import sys
    def show_help():
        print 'Usage: '
        print './streamer.py url streamName destination'
        print './streamer.py https://www.youtube.com/watch?v=9cQT4urTlXM streamName rtmp://192.168.88.59:1935/live'
        return
    def streamer() :
        url = sys.argv[1]
        if not url :
            print 'Error: url is empty'
            return
        stream_id = sys.argv[2]
        if not stream_id:
            print 'Error: stream name is empty'
            return
        destination = sys.argv[3]
        if not destination:
            print 'Error: destination is empty'
            return
        _youtube_process = subprocess.Popen(('youtube-dl','-f','','--prefer-ffmpeg', '--no-color', '--no-cache-dir', '--no-progress','-o', '-', '-f', '22/18', url, '--reject-title', stream_id),stdout=subprocess.PIPE)
        _ffmpeg_process = subprocess.Popen(('ffmpeg','-re','-i', '-','-preset', 'ultrafast','-vcodec', 'copy', '-acodec', 'copy','-threads','1', '-f', 'flv',destination + "/" + stream_id), stdin=_youtube_process.stdout)
        return
    if len(sys.argv) < 4:
        show_help()
    else:
        streamer()

    This python script does the following:

    1. Creates a _youtube_process sub- process for reading a movie by the youtube-dl library
    2. Creates the second _ffmpeg_process subprocess , which receives data from the first through pipe. This process already creates an RTMP stream and sends it to the server at the specified address.


    Script testing


    To run the script, you need to install python. You can download it here .

    When testing, we used version 2.6.6. Most likely, any version is suitable, because the script is quite simple and its task is to transfer from one process to another.

    Script run:
    python streamer.py https://www.youtube.com/watch?v=9cQT4urTlXM stream1 rtmp://192.168.88.59:1935/live

    As you can see, three arguments are passed:

    1. YouTube address of the video.
      www.youtube.com/watch?v=9cQT4urTlXM
    2. The name of the stream with which the RTMP broadcast will take place.
      stream1
    3. The address of the RTMP server.
      rtmp: //192.168.88.59: 1935 / live

    For testing, we will use Web Call Server . He is able to receive RTMP streams and distribute them via WebRTC. Here you can download and install WCS5 on your VPS or local Linux test server.

    Testing scheme with Web Call Server:


    Below we use one of the demo servers for the test:

    rtmp://wcs5-eu.flashphoner.com:1935/live

    This is the RTMP address that you need to pass to the streamer.py script to quickly test the broadcast with our demo server.

    The launch should look like this:

    python streamer.py https://www.youtube.com/watch?v=9cQT4urTlXM stream1 rtmp://wcs5-eu.flashphoner.com:1935/live

    In the stdout console, we will see the following output:

    # python streamer.py https://www.youtube.com/watch?v=9cQT4urTlXM stream1 rtmp://wcs5-eu.flashphoner.com:1935/live
    ffmpeg version 3.2.3 Copyright (c) 2000-2017 the FFmpeg developers
      built with gcc 4.4.7 (GCC) 20120313 (Red Hat 4.4.7-11)
      configuration: --enable-shared --disable-logging --enable-gpl --enable-pthreads --enable-libx264 --enable-librtmp --disable-yasm
      libavutil      55. 34.101 / 55. 34.101
      libavcodec     57. 64.101 / 57. 64.101
      libavformat    57. 56.101 / 57. 56.101
      libavdevice    57.  1.100 / 57.  1.100
      libavfilter     6. 65.100 /  6. 65.100
      libswscale      4.  2.100 /  4.  2.100
      libswresample   2.  3.100 /  2.  3.100
      libpostproc    54.  1.100 / 54.  1.100
    ]# [youtube] 9cQT4urTlXM: Downloading webpage
    [youtube] 9cQT4urTlXM: Downloading video info webpage
    [youtube] 9cQT4urTlXM: Extracting video information
    [youtube] 9cQT4urTlXM: Downloading MPD manifest
    [download] Destination: -
    Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'pipe:':
      Metadata:
        major_brand     : mp42
        minor_version   : 0
        compatible_brands: isommp42
        creation_time   : 2016-08-23T12:21:06.000000Z
      Duration: 00:29:59.99, start: 0.000000, bitrate: N/A
        Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 288 kb/s, 30 fps, 30 tbr, 90k tbn, 60 tbc (default)
        Metadata:
          creation_time   : 2016-08-23T12:21:06.000000Z
          handler_name    : ISO Media file produced by Google Inc.
        Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)
        Metadata:
          creation_time   : 2016-08-23T12:21:06.000000Z
          handler_name    : ISO Media file produced by Google Inc.
    Output #0, flv, to 'rtmp://192.168.88.59:1935/live/stream1':
      Metadata:
        major_brand     : mp42
        minor_version   : 0
        compatible_brands: isommp42
        encoder         : Lavf57.56.101
        Stream #0:0(und): Video: h264 (Main) ([7][0][0][0] / 0x0007), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 288 kb/s, 30 fps, 30 tbr, 1k tbn, 90k tbc (default)
        Metadata:
          creation_time   : 2016-08-23T12:21:06.000000Z
          handler_name    : ISO Media file produced by Google Inc.
        Stream #0:1(und): Audio: aac (LC) ([10][0][0][0] / 0x000A), 44100 Hz, stereo, 125 kb/s (default)
        Metadata:
          creation_time   : 2016-08-23T12:21:06.000000Z
          handler_name    : ISO Media file produced by Google Inc.
    Stream mapping:
      Stream #0:0 -> #0:0 (copy)
      Stream #0:1 -> #0:1 (copy)
    frame=  383 fps= 30 q=-1.0 size=     654kB time=00:00:12.70 bitrate= 421.8kbits/s speed=   1x

    If you quickly run through this log, you can understand that the following happens:

    1. The video page opens.
    2. Data on video formats is retrieved.
    3. Download mp4 video 1280x720, H.264 + AAC
    4. It starts ffmpeg, picks up downloaded data and streams over RTMP with a bitrate of 421 kbps. Such a meager bitrate is explained by the selected clip with a timer. A normal video will give an order of magnitude greater bitrate.

    After the streaming process has started, we try to play the stream in the WebRTC player . The stream name is specified in the Stream field, and the server address in the Server field. Connection to the server occurs via the Websocket (wss) protocol, and the stream arrives at the player via WebRTC (UDP).


    We specifically took this particular video on YouTube in order to be able to test the realtime stream, because our ultimate goal was to deliver the YouTube stream to all viewers at the same time, with a minimum delay and time spread. A clip with a millisecond timer is perfectly suited for such a test.

    The test itself is very simple. We open two browser tabs (we simulate two viewers), play this stream with a timer according to our scheme, and take several screenshots to capture the difference in video arrival time. Next, we compare the milliseconds and see who received the video earlier, and who later and how much.

    We get the following results:

    Test 1


    Test 2


    Test 3


    As you can see, each of the viewers sees the same video, with a spread of no more than 130 milliseconds.

    Thus, the task of real-time broadcast of the video from YouTube to WebRTC is solved. Viewers received the stream almost simultaneously. The manager didn’t leave to drink coffee, the programmer read the habr, and the marketer successfully showed everyone the video clip.

    Have a good streaming!

    References


    youtube-dl   - a library for downloading videos from YouTube
    ffmpeg - RTMP encoder
    Web Call Server - a server that can split an RTMP stream by WebRTC
    streamer.py - a script for integrating youtube-dl and ffmpeg with sending an RTMP stream

    Also popular now: