Search code examples
pythonc++luavlclibvlc

libvlc - http stream error: local stream 1 error: Cancellation (0x8)


I encounter a bug with libvlc where attempting to play video from YouTube result in error http stream error: local stream 1 error: Cancellation (0x8)

Context

C++ code:

#include <iostream>
#include <thread>
#include <vlc/vlc.h>
#include <vlc/libvlc.h>
#include <vlcpp/vlc.hpp>

int main() {
    const char *const arg[] = { "--no-video", "-vv" };
    VLC::Instance instance = VLC::Instance(2, arg);
    
    const std::string mrl = "link to youtube video here";
    VLC::Media media = VLC::Media(instance, mrl, VLC::Media::FromLocation);
    
    VLC::MediaPlayer player = VLC::MediaPlayer(instance);
    player.setMedia(media);
    
    player.play();
    
    std::this_thread::sleep_for( std::chrono::seconds(215) );
    
    return 0;
}

C++ log: https://gist.github.com/trofchik/39ebc5df6aeb4a82b464db74a48846d7

VLC player app log: https://gist.github.com/trofchik/8028fda99c08f22523800a03f26e168c Note that I set 720p as prefered resolution for video to play due to some quirks of youtube api.

Python code

#!/usr/bin/python3

import pafy 
import vlc
import time
import youtube_dl

url = 'link to youtube video here'

song = pafy.new(url)
duration = song.length
audiostreams = song.audiostreams
best = song.getbest()
play_url = best.url

print(play_url)

instance = vlc.Instance('--no-video')

player = instance.media_player_new()
media = instance.media_new(play_url)
media.get_mrl()
player.set_media(media)

player.play()
time.sleep(duration)

Python log: https://gist.github.com/trofchik/68e797339853dd5607d8c7b3fcb1493a

C++ log when using link generated by python script: https://gist.github.com/trofchik/000a32280ba177a3e8897eb25cc7b9e0

I also replaced the code of lua script for youtube videos with code found on vlc's github page which you can find here: https://github.com/videolan/vlc/blob/master/share/lua/playlist/youtube.lua This script apparently generates proper url for video playback.

Explanation

I've attempted to play video with 3 different ways.

First is using libvlc with normal link to youtube video (e.g. youtube.com/watch?v=videoID) which gave me aforementioned error. (see C++ code and log)

Second is playing it using VLC player app which worked fine. (see VLC player app log)

Third is playing the video using vlc-python and pafy which also worked. (see python code and log)

Fourth is playing it via libvlc but using a link generated by python code (stored in play_url variable) which worked. (see C++ code and C++ log when using python generated link) What's interesting is that in C++ log when using normal link at line 301 you can see that the link has a similar format to the one being generated by python code. The program still doesn't work. What's weird is that if I use a link that was generated by libvlc (one which I point to above and that starts with "r1--") the program will work as intended.

What have I done so far and what do I want?

Most of what I've done I already described above. Besides that I've spent several hours comparing logs from all 4 methods I used to play the video but found nothing that could point me at fix. I've also searched for fix online. Many suggested to set max playback resolution to 720p which I did in case with VLC player app. I doubt that default resolution is the problem since I always run code with --no-video argument which disables video output and leaves only audio.

Edit: Tried running C++ code with --prefered-resolution=720 argument. Didn't work. In fourth case described above nothing changed.

I consider using python in conjunction with libvlc a last resort and rather figure out the problem with lua that is natively used by libvlc. Thus I want my C++ code to work with normal YouTube links while not using python.


Solution

  • Since Youtube URLs are not like other URLs for VLC, in the sense that you give a HTML URL to VLC, and that VLC needs to parse that HTML page and reconstruct the actual video URL (that's what the lua script does), you cannot give the HTML URL directly to the mediaplayer as a media.

    Create a media from the HTML URL, as you have done.

    VLC::Media media = VLC::Media(instance, mrl, VLC::Media::FromLocation);
    

    Parse it.

    media.parseWithOptions(VLC::Media::ParseFlags::Network, -1);
    

    The parsing is done asynchronously. There is an event on the media that can tell you when it's finished.

    Once successfully parsed, you can access the actual video URL from the media subitems (usually the first item from the medialist), and give that to the mediaplayer.

    std::shared_ptr<VLC::Media> video = media.subitems()->itemAtIndex(0);
    player.setMedia(*video);
    player.play();
    

    This is what the VLC apps do under the hood when they detect the video to be a YouTube URL (or Vimeo, dailymotion, m3u8 playlist, etc.).