Creating a Simulated HLS Streaming Server with FFmpeg...HOW??
Hello all: I'm new to this list, and I sure hope someone can help me. I have been working on this for MONTHS and have not been able to get it to work. What I'm trying to do seems like it should be fairly simple or common with FFmpeg. I'm trying to set up a simulated HLS live video streaming server using FFmpeg. What I'm doing is sort of like creating a TV broadcast system where you provide the software a list of MP4 files and the software streams each MP4 one-by-one, back-to-back. To create this software I've written a Python script that takes a list of MP4 video files to play and then it iterates through the list, attempting to call FFmpeg over and over, once for each MP4 file. In other words, call FFmpeg to stream the first video. Let's call it video1.mp4. When it's done, call FFmpeg again to stream video2.mp4, and so on. I'm using HLS and I've signaled FFMpeg to append to the playlist, which it appears to be doing correctly. But for some reason, the media player (either VLC or MPV) will not play the whole stream. It just plays the first file and then stops playing, even though FFmpeg is dutifully generating new .TS segment files and updating the M3U8 playlist accordingly. I cannot for the life of me figure out why the media players stop playing. What is signaling them to stop? I can't see any indication in the M3U8 that would tell them to stop. Does anyone have any ideas? Has anyone done this before? Any assistance would be tremendously appreciated! Thank you so much! Norm
People will probably ask you to post your code as a gist. On Sat, Jul 29, 2023 at 12:31 AM Norm Kaiser <norm_kaiser@hotmail.com> wrote:
Hello all:
I'm new to this list, and I sure hope someone can help me. I have been working on this for MONTHS and have not been able to get it to work.
What I'm trying to do seems like it should be fairly simple or common with FFmpeg. I'm trying to set up a simulated HLS live video streaming server using FFmpeg. What I'm doing is sort of like creating a TV broadcast system where you provide the software a list of MP4 files and the software streams each MP4 one-by-one, back-to-back. To create this software I've written a Python script that takes a list of MP4 video files to play and then it iterates through the list, attempting to call FFmpeg over and over, once for each MP4 file.
In other words, call FFmpeg to stream the first video. Let's call it video1.mp4. When it's done, call FFmpeg again to stream video2.mp4, and so on.
I'm using HLS and I've signaled FFMpeg to append to the playlist, which it appears to be doing correctly. But for some reason, the media player (either VLC or MPV) will not play the whole stream. It just plays the first file and then stops playing, even though FFmpeg is dutifully generating new .TS segment files and updating the M3U8 playlist accordingly.
I cannot for the life of me figure out why the media players stop playing. What is signaling them to stop? I can't see any indication in the M3U8 that would tell them to stop.
Does anyone have any ideas? Has anyone done this before?
Any assistance would be tremendously appreciated!
Thank you so much! Norm _______________________________________________ ffmpeg-user mailing list ffmpeg-user@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-user
To unsubscribe, visit link above, or email ffmpeg-user-request@ffmpeg.org with subject "unsubscribe".
People will probably ask you to post your code as a gist.
Indeed, or at least just post here the ffmpeg commands you are currently using. KR, Vincent
Good point, gentlemen. Here is ffmpeg command I'm using: hls_command = ( f'ffmpeg -re {start_time_option} -i "{input_file}" ' '-c:v libx264 -c:a aac ' f'-f hls -hls_time {segment_duration} -hls_list_size {playlist_size} ' '-hls_flags delete_segments -hls_segment_type fmp4 -hls_flags omit_endlist -segment_list_flags +live ' '-hls_flags delete_segments+omit_endlist+append_list -hls_segment_type fmp4 ' f'-hls_delete_threshold {delete_threshold} -hls_playlist_type event ' f'-hls_segment_filename "{segment_filename}" "{output_playlist}"' ) And here is the current state of the Python. Bear with me, as I've done a lot of try this, try that to the code: import datetime import subprocess import psutil import os import shlex import random import string from pynput import keyboard import time #*************** START VARIABLE DECLARATION SECTION ************* ThePlaylistThatIsPlaying = "" TheVideoThatIsPlaying = "" TheTimeToStart = 0 FFMpegProc = None #**************** END VARIABLE DECLARATION SECTION ************** def show_message_box(): # Create a basic tkinter window (you can hide it if you want) root = tk.Tk() root.withdraw() #************************************************************************* #************************** START FUNCTION SECTION *********************** #************************************************************************* def generate_random_letters(): letters = string.ascii_letters return ''.join(random.choice(letters) for _ in range(8)) #This function reads the playlist from a text file into memory. def ReadInThePlaylist(PlaylistToBeReadIn): PlaylistToBeReadIn = "/home/norm/" + PlaylistToBeReadIn lines = [] with open(PlaylistToBeReadIn, 'r', encoding='utf-8-sig') as file: for line in file: line = line.strip() # Remove leading/trailing whitespace and newline characters words = line.split() # Split the line into a list of words lines.append(words) # Append the list of words to the lines list return lines #The function below is what actually plays the stream. def StartVideoStream(input_file, output_directory, output_playlist, start_time=0, segment_duration=2, playlist_size=6, delete_threshold=24): os.makedirs(output_directory, exist_ok=True) segmentRandomName = "segment" + "%03d.ts" segment_filename = os.path.join(output_directory, segmentRandomName) output_playlist = os.path.join(output_directory, output_playlist) start_time_option = f"-ss {start_time}" if start_time > 0 else "" #-f concat hls_command = ( f'ffmpeg -re {start_time_option} -i "{input_file}" ' '-c:v libx264 -c:a aac -preset ultrafast ' f'-f hls -hls_time {segment_duration} -hls_list_size {playlist_size} ' '-hls_flags delete_segments -hls_segment_type fmp4 -hls_flags omit_endlist -segment_list_flags +live ' '-hls_flags delete_segments+omit_endlist+append_list -hls_segment_type fmp4 ' f'-hls_delete_threshold {delete_threshold} -hls_playlist_type event ' f'-hls_segment_filename "{segment_filename}" "{output_playlist}"' ) try: # Run the ffmpeg command to start HLS streaming proc = subprocess.run(hls_command, shell=True) print("HLS streaming started successfully.") except subprocess.CalledProcessError as e: print(f"Error while running ffmpeg: {e}") return proc #************************************************************************* #************************** END FUNCTION SECTION ************************* #************************************************************************* #Let's create a custom class to hold the playlist entries. class VideoAsset: def __init__(self, InPoint, OutPoint, Path, AssetType, Bug, Weekday, StartTimeAsText): self.InPoint = InPoint self.OutPoint = OutPoint self.Path = Path self.AssetType = AssetType self.Bug = Bug self.Weekday = Weekday self.StartTimeAsText = StartTimeAsText #**************** DETERMINE WHAT MOVIE SHOULD BE PLAYING ********** # In this section we have to first determine which playlist is today's. # Then we read it in to memory by calling the ReadInThePlaylist function. d = datetime.datetime.now() Today = datetime.date.today() Year = Today.strftime("%Y") Month = d.strftime("%m") Day = d.strftime("%d") CurrentHour = d.hour CurrentMinutes = d.minute CurrentSeconds = d.second ThePlaylistThatShouldBePlaying = Year + "-" + Month + "-" + Day + ".txt" if ThePlaylistThatIsPlaying != ThePlaylistThatShouldBePlaying: ThePlaylistThatIsPlaying = ThePlaylistThatShouldBePlaying PlaylistFile = ReadInThePlaylist(ThePlaylistThatIsPlaying) Playlist = [] for i in range(0, len(PlaylistFile) - 1): #OK, playlist file is in memory...now we load it into an array of our custom VIDEOASSET object so it resembles a database. Playlist.append(VideoAsset(int(PlaylistFile[i][0]), int(PlaylistFile[i][1]), PlaylistFile[i][2], PlaylistFile[i][3], "", "", "")) #OK, so now we have the playlist in memory in a custom object. Now we have to do the time calculation to figure out what time of day it is #and play the right movie at the right start time. TimeOfDayInSeconds = (CurrentHour * 3600) + (CurrentMinutes * 60) + CurrentSeconds Found = False TheCounter = 0 while not Found: if TimeOfDayInSeconds >= Playlist[TheCounter].InPoint and TimeOfDayInSeconds < Playlist[TheCounter].OutPoint: TheVideoThatShouldBePlaying = Playlist[TheCounter].Path TheTimeToStart = (TimeOfDayInSeconds - Playlist[TheCounter].InPoint) Found = True #In here write the asset type checking, if you want to TheCounter = TheCounter + 1 print(TheTimeToStart) if TheVideoThatShouldBePlaying != TheVideoThatIsPlaying: TheVideoThatIsPlaying = TheVideoThatShouldBePlaying #StartVideoStream("/home/norm/test.mrp4", "/var/www/nginx-default", "playlist.m3u8", 120) def press_callback(key): global FFMpegProc print('{}'.format(key)) if '{}'.format(key) == "'r'": FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/test.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) print('I should start') if '{}'.format(key) == "'o'": FFMpegProc = StartVideoStream("/home/norm/Logo2.mp4", "/var/www/nginx-default", "playlist.m3u8", 0) l = keyboard.Listener(on_press=press_callback) l.start() l.join() Thank you so much! Norm ________________________________ From: ffmpeg-user <ffmpeg-user-bounces@ffmpeg.org> on behalf of Vincent Deconinck <vdeconinck@gmail.com> Sent: Saturday, July 29, 2023 12:28 PM To: FFmpeg user questions <ffmpeg-user@ffmpeg.org> Subject: Re: [FFmpeg-user] Creating a Simulated HLS Streaming Server with FFmpeg...HOW??
People will probably ask you to post your code as a gist.
Indeed, or at least just post here the ffmpeg commands you are currently using. KR, Vincent _______________________________________________ ffmpeg-user mailing list ffmpeg-user@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-user To unsubscribe, visit link above, or email ffmpeg-user-request@ffmpeg.org with subject "unsubscribe".
On 7/29/2023 11:08 AM, Norm Kaiser wrote:
Good point, gentlemen. Here is ffmpeg command I'm using:
It's always helpful to post the complete command output to see what's going on, please run one of the full commands from a shell and post that. Also, please do not top-post on this mailing list. However.... On 7/28/2023 9:31 PM, Norm Kaiser wrote:
In other words, call FFmpeg to stream the first video. Let's call it video1.mp4. When it's done, call FFmpeg again to stream video2.mp4, and so on. Done encoding or done playing/streaming?
I'm using HLS and I've signaled FFMpeg to append to the playlist, which it appears to be doing correctly. But for some reason, the media player (either VLC or MPV) will not play the whole stream. It just plays the first file and then stops playing, even though FFmpeg is dutifully generating new .TS segment files and updating the M3U8 playlist accordingly.
I see two possibilities right off- First is that the second video is added to the playlist -after- the first has completed (and the player stops because it's hit the end of the list). Second is that the player only reads the playlist at start and never sees that it's been appended to. You could easily test this manually by pre-encoding the videos and hand-editing the playlist at different points in the timeline. What's the timeline of when entries are made on the play list, when videos start and stop, and when they're encoded? Later, z!
On July 29, 2023 10:32 PM, Carl Z said: It's always helpful to post the complete command output to see what's going on, please run one of the full commands from a shell and post that. Also, please do not top-post on this mailing list. How much of the output should I post, considering both instances of ffmpeg do stream and don't produce any error. Should I just let them both run for, say, 30 seconds and then stop them manually and then paste all of the output? It would be quite a bit! Also, please do not top-post on this mailing list. You are definitely right. My mistake. Done encoding or done playing/streaming? Both, I suppose? I am using the -re flag, so ffmpeg is encoding at the same rate that the media plays, correct? I see two possibilities right off- First is that the second video is added to the playlist -after- the first has completed (and the player stops because it's hit the end of the list). My suspicion is that your first suggestion is what's happening -- the first instance of ffmpeg stops writing to the playlist file and before the second instance can get up and cranking, the players (both MPV and VLC) sense that the stream is finished and then just stop. But then why do they just stop, considering I am using omit_endlist? I would expect the player to just go black for a few seconds until the next segment is written to the playlist and then the player would wake up and "Ah! Here's more. I'll continue playing..." But they don't do this. They just stop. Worse, MPV just exits. If the theory that the second instance of ffmpeg isn't starting up fast enough, how could I possibly correct this? It would seem to present an extremely delicate timing issue. Second is that the player only reads the playlist at start and never sees that it's been appended to. So I'm declaring that the stream is an EVENT, not VOD, so shouldn't the player by definition continue attempting to read the M3U8 file until reads an explicit EXT-X-ENDLIST? You could easily test this manually by pre-encoding the videos and hand-editing the playlist at different points in the timeline. Well, I am able to do this using a static M3U8 file. For example, I can just encode several videos really quick by omitting the -re flag. That'll result in a folder full of properly numbered .TS segments. I can then create a really long M3U8 file by hand and the players will play it. The complication is in dynamic M3U8 files, where the old segments that have already played are popped off the top of the list and new ones are added to the bottom. That's what won't work. It's like what you suggested: The second instance of ffmpeg isn't encoding and updating the playlist fast enough. Thoughts? Thank you so very much! Norm ________________________________ From: ffmpeg-user <ffmpeg-user-bounces@ffmpeg.org> on behalf of Carl Zwanzig <cpz@tuunq.com> Sent: Saturday, July 29, 2023 10:32 PM To: ffmpeg-user@ffmpeg.org <ffmpeg-user@ffmpeg.org> Subject: Re: [FFmpeg-user] Creating a Simulated HLS Streaming Server with FFmpeg...HOW?? On 7/29/2023 11:08 AM, Norm Kaiser wrote:
Good point, gentlemen. Here is ffmpeg command I'm using:
It's always helpful to post the complete command output to see what's going on, please run one of the full commands from a shell and post that. Also, please do not top-post on this mailing list. However.... On 7/28/2023 9:31 PM, Norm Kaiser wrote:
In other words, call FFmpeg to stream the first video. Let's call it video1.mp4. When it's done, call FFmpeg again to stream video2.mp4, and so on. Done encoding or done playing/streaming?
I'm using HLS and I've signaled FFMpeg to append to the playlist, which it appears to be doing correctly. But for some reason, the media player (either VLC or MPV) will not play the whole stream. It just plays the first file and then stops playing, even though FFmpeg is dutifully generating new .TS segment files and updating the M3U8 playlist accordingly.
I see two possibilities right off- First is that the second video is added to the playlist -after- the first has completed (and the player stops because it's hit the end of the list). Second is that the player only reads the playlist at start and never sees that it's been appended to. You could easily test this manually by pre-encoding the videos and hand-editing the playlist at different points in the timeline. What's the timeline of when entries are made on the play list, when videos start and stop, and when they're encoded? Later, z! _______________________________________________ ffmpeg-user mailing list ffmpeg-user@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-user To unsubscribe, visit link above, or email ffmpeg-user-request@ffmpeg.org with subject "unsubscribe".
participants (4)
-
Carl Zwanzig -
David Bernat -
Norm Kaiser -
Vincent Deconinck