Saving a stream while playing it using LibVLC
Asked Answered
M

5

22

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.

Modulate answered 13/5, 2013 at 4:56 Comment(0)
T
12

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


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.

Taxis answered 26/5, 2013 at 11:56 Comment(7)
I tried this code on Windows, and it doesn't work. :( It only creates the file, but doesn't write anything to it, neither does it play the stream!Modulate
I tried playing a local file of mpg format, then it is able to play and save file to example.mpg simultaneously. I think in case of youtube stream it doesn't work because of format mismatch. Any idea what to do now?Modulate
@Nawaz I figured out a solution for YouTube. See the updated part of my answerTaxis
That is a great answer. Accepted. Thanks a lot. :-)Modulate
BTW, what does deinterlace mean? What does it actually do?Modulate
@Nawaz I'm no expert either... Interlacing is a old technique of doubling the perceived frame rate without consuming extra bandwidth. This is great for older screens, but on modern LCD-based screens interlaced images look horrible so deinterlacing is supposed to restore the image. I guess in case of YouTube this setting is probably superfluous.Taxis
+1 for sout=#duplicate{...} thing. I'd been trying to save the stream while playing at same time since days. Though I have a very different work environment, but this thing made my day ;) love youOverall
A
9

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}}")
Apgar answered 26/5, 2013 at 10:24 Comment(0)
I
1

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")
Interregnum answered 20/5, 2013 at 0:27 Comment(1)
not working, there's record file but no display to screenMiddy
S
1

Some time ago in a sample code in the active state website i saw someone played and recorded a MP3 file using VLC using the vlc.py module. You can take a look at it's sample code to see how to duplicate a stream. I copied th code here for you (I copied it from http://code.activestate.com/recipes/577802-using-vlcpy-to-record-an-mp3-and-save-a-cue-file/):

import vlc
import time
import os

def new_filename(ext = '.mp3'):
    "find a free filename in 00000000..99999999"
    D = set(x[:8] for x in os.listdir('.')
        if (x.endswith(ext) or x.endswith('.cue')) and len(x) == 12)
    for i in xrange(10**8):
        s = "%08i" %i
        if s not in D:         
            return s

def initialize_cue_file(name,instream,audiofile):
    "create a cue file and write some data, then return it"
    cueout = '%s.cue' %name
    outf = file(cueout,'w')
    outf.write('PERFORMER "%s"\n' %instream)
    outf.write('TITLE "%s"\n' %name)
    outf.write('FILE "%s" WAVE\n' %audiofile)
    outf.flush()
    return outf

def initialize_player(instream, audiofile):
    "initialize  a vlc player which plays locally and saves to an mp3file"
    inst = vlc.Instance()   
    p = inst.media_player_new()   
    cmd1 = "sout=#duplicate{dst=file{dst=%s},dst=display}" %audiofile
    cmd2 ="no-sout-rtp-sap"
    cmd3 = "no-sout-standard-sap"
    cmd4 ="sout-keep"
    med=inst.media_new(instream,cmd1,cmd2,cmd3,cmd4)   
    med.get_mrl()    
    p.set_media(med)
    return p, med

def write_track_meta_to_cuefile(outf,instream,idx,meta,millisecs):
    "write the next track info to the cue file"
    outf.write('  TRACK %02i AUDIO\n' %idx)
    outf.write('    TITLE "%s"\n' %meta)
    outf.write('    PERFORMER "%s"\n' %instream)
    m = millisecs // 60000
    s = (millisecs - (m*60000)) // 1000
    hs = (millisecs - (m*60000) - (s*1000)) //10
    ts = '%02i:%02i:%02i'  %(m,s,hs)
    outf.write('    INDEX 01 %s\n' %ts)
    outf.flush()

def test():
    #some online audio stream for which this currently works ....
    instream = 'http://streamer-mtc-aa05.somafm.com:80/stream/1018'
    #if the output filename ends with mp3 vlc knows which mux to use
    ext = '.mp3'
    name = new_filename(ext)
    audiofile = '%s%s' %(name,ext)
    outf = initialize_cue_file(name,instream,audiofile)
    p,med = initialize_player(instream, audiofile)
    p.play()
    np = None
    i = 0
    while 1:
        time.sleep(.1)
        new = med.get_meta(12)
        if new != np:
            i +=1
            t = p.get_time()
            print "millisecs: %i" %t
            write_track_meta_to_cuefile(outf,instream,i,new,t)
            np = new
            print "now playing: %s" %np

if __name__=='__main__':
    test()
Saida answered 24/5, 2013 at 13:6 Comment(0)
H
0

Perhaps you need to clone your output, as suggested on the forum?

Haily answered 22/5, 2013 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.