Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Saving a stream while playing it using LibVLC

Using LibVLC, I'm trying to save a stream while playing it. This is the python code:

import os
import sys
import vlc

if __name__ == '__main__':
    filepath = <either-some-url-or-local-path>
    movie = os.path.expanduser(filepath)
    if 'http://' not in filepath:
        if not os.access(movie, os.R_OK):
            print ( 'Error: %s file is not readable' % movie )
            sys.exit(1)
    instance = vlc.Instance("--sub-source marq --sout=file/ps:example.mpg")
    try:
        media = instance.media_new(movie)
    except NameError:
        print ('NameError: % (%s vs Libvlc %s)' % (sys.exc_info()[1],
                       vlc.__version__, vlc.libvlc_get_version()))
        sys.exit(1)
    player = instance.media_player_new()
    player.set_media(media)
    player.play()

    #dont exit!
    while(1):
        continue

It saves the video stream to a file example.mpg. As per this doc, the command to save a stream is this :

--sout=file/ps:example.mpg

which I've using when creating an instance of vlc.Instance:

instance = vlc.Instance("--sub-source marq --sout=file/ps:example.mpg")

But the problem is that it only saves the stream, it doesn't play the stream simultaneously.

Is there any way (in LibVLC) I can save the stream (to a local file) while paying it?

Although, I'm looking for a solution in Python 3.3.1 but it is fine if there is any C or C++ solution.


I've created a similar, but not duplicate, topic yesterday.

like image 276
Nawaz Avatar asked May 13 '13 04:05

Nawaz


3 Answers

Idea:

The basic idea is simple enough. You have to duplicate the output stream and redirect it to a file. This is done, as Maresh correctly pointed out, using the sout=#duplicate{...} directive.

Working Solution:

The following solution works on my machine ™. I've tested it on Ubuntu 12.10 with VLC v2.0.3 (TwoFlower) and Python 2.7.1. I think it should also work on Python 3 since most of the heavy lifting is done by libVlc anyway.

import os
import sys
import vlc

if __name__ == '__main__':
    #filepath = <either-some-url-or-local-path>
    movie = os.path.expanduser(filepath)
    if 'http://' not in filepath:
        if not os.access(movie, os.R_OK):
            print ( 'Error: %s file is not readable' % movie )
            sys.exit(1)
    instance = vlc.Instance("--sout=#duplicate{dst=file{dst=example.mpg},dst=display}")
    try:
        media = instance.media_new(movie)
    except NameError:
        print ('NameError: % (%s vs Libvlc %s)' % (sys.exc_info()[1],
                       vlc.__version__, vlc.libvlc_get_version()))
        sys.exit(1)
    player = instance.media_player_new()
    player.set_media(media)
    player.play()

    #dont exit!
    while(1):
        continue

Helpful Links

  • The Command-Line help was essential to decipher the plethora of VLCs command line options.
  • Chapter 3 of VLC streaming HowTo. Explains the structure of the stream output, its directives and describes of the various available modules. Chapter 4 shows some examples.
  • LibVLC API documentation in case you want to change media option at runtime

Update - Saving YouTube videos:

The above code doesn't play nice with YouTube. I searched around and discovered that an additional transcode directive can be used to convert YouTube's video stream to a regular video format. I used #transcode{vcodec=mp4v,acodec=mpga,vb=800,ab=128,deinterlace}

  • vcodec=mp4v is the video format you want to encode in (mp4v is MPEG-4, mpgv is MPEG-1, and there is also h263, DIV1, DIV2, DIV3, I420, I422, I444, RV24, YUY2).
  • acodec=mpga is the audio format you want to encode in (mpga is MPEG audio layer 2, a52 is A52 i.e. AC3 sound).
  • vb=800 is the video bitrate in Kbit/s.
  • ab=128 is the audio bitrate in Kbit/s.
  • deinterlace tells VLC to deinterlace the video on the fly.

The updated code looks like this:

import os
import sys
import vlc

if __name__ == '__main__':
    #filepath = <either-some-url-or-local-path>
    filepath = "http://r1---sn-nfpnnjvh-1gil.c.youtube.com/videoplayback?source=youtube&newshard=yes&fexp=936100%2C906397%2C928201%2C929117%2C929123%2C929121%2C929915%2C929906%2C929907%2C929125%2C929127%2C925714%2C929917%2C929919%2C912512%2C912515%2C912521%2C906838%2C904485%2C906840%2C931913%2C904830%2C919373%2C933701%2C904122%2C932216%2C936303%2C909421%2C912711%2C907228%2C935000&sver=3&expire=1373237257&mt=1373214031&mv=m&ratebypass=yes&id=1907b7271247a714&ms=au&ipbits=48&sparams=cp%2Cid%2Cip%2Cipbits%2Citag%2Cratebypass%2Csource%2Cupn%2Cexpire&itag=45&key=yt1&ip=2a02%3A120b%3Ac3c6%3A7190%3A6823%3Af2d%3A732c%3A3577&upn=z3zzcrvPC0U&cp=U0hWSFJOVV9KUUNONl9KSFlDOmt4Y3dEWFo3dDFu&signature=D6049FD7CD5FBD2CC6CD4D60411EE492AA0E9A77.5D0562CCF4E10A6CC53B62AAFFF6CB3BB0BA91C0"
    movie = os.path.expanduser(filepath)
    savedcopy = "yt-stream.mpg"
    if 'http://' not in filepath:
        if not os.access(movie, os.R_OK):
            print ( 'Error: %s file is not readable' % movie )
            sys.exit(1)
    instance = vlc.Instance("--sout=#transcode{vcodec=mp4v,acodec=mpga,vb=800,ab=128,deinterlace}:duplicate{dst=file{dst=%s},dst=display}" % savedcopy)
    try:
        media = instance.media_new(movie)
    except NameError:
        print ('NameError: % (%s vs Libvlc %s)' % (sys.exc_info()[1],
                       vlc.__version__, vlc.libvlc_get_version()))
        sys.exit(1)
    player = instance.media_player_new()
    player.set_media(media)
    player.play()

    #dont exit!
    while(1):
        continue

A couple of important points:

I've used MPEG audio and video codecs in the transcode directive. It seems to be important to use a matching extensions for the output file (mpg in this case). Otherwise VLC gets confused when opening the saved file for playback. Keep that in mind if you decide to switch to another video format.

You cannot add a regular YouTube URL as filepath. Instead you have to specify the location of the video itself. That's the reason why the filepath that I've used looks so cryptic. That filepath corresponds to video at http://www.youtube.com/watch?v=GQe3JxJHpxQ. VLC itself is able to extract the video location from a given YouTube URL, but libVLC doesn't do that out of the box. You'll have to write your own resolver to do that. See this related SO question. I followed this approach to manually resolve the video location for my tests.

like image 86
djf Avatar answered Oct 20 '22 14:10

djf


I think you need to duplicate the output in order to play and record it at the same time:

vlc.Instance("--sub-source marq --sout=#stream_out_duplicate{dst=display,dst=std{access=file,mux=ts,dst=/path/file.mpg}}")

or

libvlc_media_add_option(media, ":sout=#stream_out_duplicate{dst=display,dst=std{access=file,mux=ts,dst=/path/file.mpg}}")
like image 9
Maresh Avatar answered Oct 20 '22 14:10

Maresh


Did you try adding to the list of options the following option?

--sout-display

i.e.

instance = vlc.Instance("--sub-source marq --sout=file/ps:example.mpg --sout-display")
like image 1
Amadeus Avatar answered Oct 20 '22 13:10

Amadeus