write using import streamlink module OR by runnong subprocess or os.system command?

768 Views Asked by At

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?

1

There are 1 best solutions below

5
Rotem On

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?