0

I see some streamlink api available, but see hardly any info like examples on how to use it.

What I am essentially trying to do is capture a live video from a specific youtube channel.

I ONLY CARE ABOUT THE AUDIO, AND NOT THE VIDEO. So, a part of my script will be after I capture the .ts video file using streamlink, to convert it to .mp3 using ffmpeg. Because I dont think streamlink can capture a live stream as mp3?

What is the best way to convert the file to mp3? Would it be to run the ffmpeg command AFTER the video is captured so that the script will wait to run further until the convert is finished?

What I think is probably better is to run the ffmpeg command as a separate thread or thread separate from streamlink. So that way, streamlink can continue capturing/downloading while ffmpeg can run parallel at the same time converting the past .ts file to mp3.

It seems much easier to run streamlink through os.system or subprocess and just run streamlink as a command passing the arguments it needs.

Is there any advantage to running using the streamlink module?

I found this code sample here: Handle stream as individual frames using streamlink , but I have questions as to which parts I can omit that are related to video (since I don't care about the video part of the stream)?

import numpy as np
import subprocess as sp
import threading
import cv2
import ffmpeg

#stream_url = 'https://www.nimo.tv/v/v-1712291636586087045'
stream_url = 'https://www.twitch.tv/esl_csgo'

# Assume video resolution is known.
width, height = 1920, 1080


# Writer thread (read from streamlink and write to FFmpeg in chunks of 1024 bytes).
def writer(streamlink_proc, ffmpeg_proc):
    while (not streamlink_proc.poll()) and (not ffmpeg_proc.poll()):
        try:
            chunk = streamlink_proc.stdout.read(1024)
            ffmpeg_proc.stdin.write(chunk)
        except (BrokenPipeError, OSError) as e:
            pass


streamlink_args = [r'c:\Program Files (x86)\Streamlink\bin\streamlink.exe', stream_url, "best", "-O"]  # Windows executable downloaded from: https://github.com/streamlink/streamlink/releases/tag/2.4.0
streamlink_process = sp.Popen(streamlink_args, stdout=sp.PIPE)  # Execute streamlink as sub-process


# Execute FFmpeg sub-process with URL as input and raw (BGR) output format.
ffmpeg_process = (
    ffmpeg
    .input('pipe:')
    .video
    .output('pipe:', format='rawvideo', pix_fmt='bgr24')
    .run_async(pipe_stdin=True, pipe_stdout=True) # In case ffmpeg in not in executable path, add cmd=fullpath like: .run_async(pipe_stdout=True, cmd=r'c:\FFmpeg\bin\ffmpeg.exe')
)


thread = threading.Thread(target=writer, args=(streamlink_process, ffmpeg_process))
thread.start()


# Read decoded video (frame by frame), and display each frame (using cv2.imshow)
while True:
    # Read raw video frame from stdout as bytes array.
    in_bytes = ffmpeg_process.stdout.read(width * height * 3)

    if not in_bytes:
        break

    # Transform the byte read into a NumPy array
    frame = np.frombuffer(in_bytes, np.uint8).reshape([height, width, 3])

    # Display the frame
    cv2.imshow('frame', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

ffmpeg_process.stdout.close()
ffmpeg_process.wait()
#streamlink_process.stdin.close()
streamlink_process.kill()
cv2.destroyAllWindows()

I'm not sure if cv2, numpy, or "video chunks" are needed or helpful since I"m not interested in video quality?

What is the purpose of downloading using video chunks? Is it helpful in the reliability of the stream download?

I need help understanding how to put my script together and which parts/modules would be helpful?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
samas
  • 1
  • 1

1 Answers1

0

In case the streamlink is not broken, you may pass the link to FFmpeg directly.
The suggested solution records the audio while downloading.

  • Use Streamlink module for getting direct video stream URL (like m3u8) from the WEB address.
  • Pass the video stream URL to FFmpeg.

Deciding when and how to terminate the recording may be a little tricky, because we want to close FFmpeg gracefully.
I used pynput for checking if Esc key is pressed.

Here is a complete code sample:

from streamlink import Streamlink
import ffmpeg
import signal
from pynput.keyboard import Key, Listener
from threading import Thread
import time

web_url = 'https://www.twitch.tv/esl_csgo'

esc_key_pressed = False  # Global variable.


def stream_to_url(url, quality='audio_only'):
    # The "audio_only" quality may be invalid for some streams (check).
    session = Streamlink()
    streams = session.streams(url)
    return streams[quality].to_url()

 
# Wait for Esc key to be pressed (and released).
def wait_for_esc_key():
    global esc_key_pressed
    # Collect events until released https://stackoverflow.com/questions/24072790/how-to-detect-key-presses
    with Listener(on_press=None, on_release=lambda key: (False if key == Key.esc else True)) as listener:
        listener.join()
        esc_key_pressed = True    


# Get the stream URL from the WEB site URL:
stream_url = stream_to_url(web_url)

# Read the input stream from the link, and write the audio to audio.mp3 file.
ffmpeg_process = (
    ffmpeg
    .input(stream_url)
    .audio
    .output('audio.mp3')
    .overwrite_output()
    .run_async() # In case FFmpeg in not in executable path, add cmd=fullpath like: .run_async(cmd=r'c:\FFmpeg\bin\ffmpeg.exe')
)


print('Press Esc to end recording')


# Wait for Esc key in a thread (non-blocking wait).
wait_for_esc_key_thread = Thread(target=wait_for_esc_key)
wait_for_esc_key_thread.start()


# Wait for Escape key pressed or FFmpeg finished.
while (not esc_key_pressed) and (not ffmpeg_process.poll()):
    time.sleep(0.01)


if esc_key_pressed:
    # Close FFmpeg gracefully
    # https://stackoverflow.com/questions/67276793/output-always-corrupt-from-ffmpeg-using-selenium-python
    ffmpeg_process.send_signal(signal.CTRL_C_EVENT)  # Is signal.CTRL_C_EVENT Windows only?
Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Thanks so much for detailed response! I can't post the links I use, because the live links of tthe youtube channel I'm downloading go private right after live stream. I did run your code in a py script and it did record a livestreams audio for me. I am confused what cv2 does? It does nothing on my system. Also 'Esc' doesn't do anything. I should've mentioned i'm running linux. Thats probably why Esc isn't working, it's mapped to a different key maybe. Since I'm only going mp3(no vid d/l), would it be better run streamlink as 'worst' quality? I also don't care much of mp3 quality – samas Oct 31 '21 at 02:25
  • ALso, when running the py script, it does give me this message. I'm not sure if its important or not? `[https @ 0x55e2a6e1ba72] Cannot reuse HTTP connection for different host: rr6---sn-q4fl6n7y.googlevideo.com:-1 != rr1---sn-q4flrnel.googlevideo.com:-1` – samas Oct 31 '21 at 02:31
  • Just as an update, I changed the Esc key map to number 9 (obtained with xmodmap) in the script, and when I run the script Esc still doesn't work. For some reason I'm also getting a segmentation fault/core dump at the start of the script start. The audio still gets downloaded, so i'm not sure which part is faulting – samas Oct 31 '21 at 04:17
  • The OpenCV is just used for getting the pressed key. The black image window must be in focus for the key to be read. According to the following [post](https://stackoverflow.com/questions/24072790/how-to-detect-key-presses) getting a key in Python is not so trivial... The "Windows only?" regards passing `signal.CTRL_C_EVENT` to FFmpeg sub-process. What version of FFmpeg are you using? Try updating FFmpeg (the segmentation fault may be related). – Rotem Oct 31 '21 at 08:11
  • I updated the example to use **pynput** instead of OpenCV, for checking if Esc key is pressed. It's more elegant, but I still haven't test my code in Linux. Note: twitch.tv has `quality='audio_only'` option. I don't know if it's a common option or not. – Rotem Oct 31 '21 at 16:08