Spanicin commited on
Commit
1f6e841
·
verified ·
1 Parent(s): 4c64658

Update stream_server.py

Browse files
Files changed (1) hide show
  1. stream_server.py +34 -97
stream_server.py CHANGED
@@ -29,114 +29,51 @@ def is_valid_path(video_name):
29
  video_path = (BASE_DIR / video_name).resolve()
30
  return str(video_path).startswith(str(BASE_DIR))
31
 
32
- def convert_to_hls(input_path, output_dir, video_name, start_number):
33
  """
34
- Converts an MP4 video to HLS format with 3-second segments starting from `start_number`.
35
 
36
- Args:
37
- input_path (str): Path to the input MP4 video.
38
- output_dir (str): Directory where HLS files will be stored.
39
- video_name (str): Name of the video (used for playlist naming).
40
- start_number (int): The starting number for HLS segments.
41
-
42
- Returns:
43
- int: Number of segments generated.
44
  """
45
- # FFmpeg command to convert MP4 to HLS with 3-second segments
46
- cmd = [
 
 
 
 
 
47
  'ffmpeg',
48
- '-y', # Overwrite output files without asking
49
- '-i', input_path, # Input file
50
- #'-codec', 'copy', # Audio codec-vf scale=1280:720 -r 30 -c:v libx264 -b:v 1500k -c:a aac -b:a 128k
51
- '-vf','scale=1280:720',
52
- '-r','30',
53
- '-c:v','libx264',
54
- '-b:v','1500k',
55
- '-c:a','aac',
56
- '-b:a','128k',
57
- '-hls_time', '3', # Segment duration in seconds
58
- '-hls_playlist_type', 'vod', # Video on Demand playlist type
59
- '-start_number', str(start_number), # Starting segment number
60
- '-hls_segment_filename', os.path.join(output_dir, 'video_stream%d.ts'), # Segment file naming
61
- os.path.join(output_dir, f'{video_name}.m3u8') # Output playlist
62
  ]
63
 
64
  try:
65
- # Execute the FFmpeg command
66
- subprocess.run(cmd, check=True, stderr=subprocess.PIPE)
 
67
  except subprocess.CalledProcessError as e:
68
- # Log FFmpeg errors and raise an HTTP exception
69
- error_message = e.stderr.decode()
70
- print(f"FFmpeg error: {error_message}")
71
- raise HTTPException(status_code=500, detail="Video conversion failed")
72
-
73
-
74
-
75
- async def add_video(video_name, output_path, audio_duration):
76
- """
77
- Endpoint to add a video to the streaming queue.
78
-
79
- Args:
80
- video_name (str): Name of the video file to add.
81
- request (Request): FastAPI request object to extract base URL.
82
 
83
- Returns:
84
- dict: Success message.
85
- """
86
- global total_processed_duration
87
- print((HLS_DIR / f'{video_name}.m3u8').exists())
88
- if not (HLS_DIR / f'{video_name}.m3u8').exists():
89
- async with segment_lock:
90
- global segment_counter
91
- current_start_number = segment_counter
92
- num_segments_before = len([f for f in os.listdir(HLS_DIR) if f.startswith('video_stream') and f.endswith('.ts')])
93
- await asyncio.get_event_loop().run_in_executor(
94
- None, convert_to_hls, str(video_name), str(HLS_DIR), video_name, current_start_number
95
- )
96
- num_segments_after = len([f for f in os.listdir(HLS_DIR) if f.startswith('video_stream') and f.endswith('.ts')])
97
- num_new_segments = num_segments_after - num_segments_before
98
- # Update the global segment counter
99
- segment_counter += num_new_segments
100
-
101
- video_names.append(video_name)
102
- segment_duration = 3
103
- full_segments = int(audio_duration // segment_duration)
104
- remaining_duration = audio_duration % segment_duration
105
-
106
- existing_segments = set()
107
- if output_path:
108
- with open(output_path, 'r') as m3u8_file:
109
- for line in m3u8_file:
110
- if line.startswith('/live_stream/video_stream'):
111
- existing_segments.add(line.strip())
112
-
113
- new_segments = []
114
- # Generate new segment paths
115
- for i in range(1, full_segments + 1):
116
- new_segment_path = f"/live_stream/video_stream{i}.ts"
117
- if new_segment_path not in existing_segments:
118
- new_segments.append(new_segment_path)
119
- total_processed_duration += segment_duration
120
 
121
-
122
- with open(output_path, 'a') as m3u8_file:
123
- for segment in new_segments:
124
- m3u8_file.write(f"#EXTINF:{segment_duration:.3f},\n")
125
- m3u8_file.write(f"{segment}\n")
126
-
127
- if remaining_duration > 0:
128
- remaining_segment_path = f"/live_stream/video_stream{full_segments + 1}.ts"
129
- if remaining_segment_path not in existing_segments:
130
- m3u8_file.write(f"#EXTINF:{remaining_duration:.3f},\n")
131
- m3u8_file.write(f"{remaining_segment_path}\n")
132
- total_processed_duration += remaining_duration
133
-
134
- # Add #EXT-X-ENDLIST only once after all segments have been added
135
- if total_processed_duration == audio_duration:
136
- with open(output_path, 'a') as m3u8_file:
137
- m3u8_file.write("#EXT-X-ENDLIST\n")
138
- total_processed_duration = 0
139
 
 
 
140
  return {"message": f'"{video_name}" added to the streaming queue.'}
141
 
142
  async def concatenate_playlists(video_names, base_dir):
 
29
  video_path = (BASE_DIR / video_name).resolve()
30
  return str(video_path).startswith(str(BASE_DIR))
31
 
32
+ def convert_to_hls(input_file, output_playlist, segment_prefix='segment', segment_duration=10):
33
  """
34
+ Converts an MP4 file to HLS .ts segments, maintaining continuity characteristics across segments.
35
 
36
+ :param input_file: Path to the input MP4 file.
37
+ :param output_playlist: Path to the output .m3u8 playlist file.
38
+ :param segment_prefix: Prefix for naming the .ts segments. Default is 'segment'.
39
+ :param segment_duration: Duration of each segment in seconds. Default is 10 seconds.
 
 
 
 
40
  """
41
+ if not os.path.exists(input_file):
42
+ raise FileNotFoundError(f"Input file '{input_file}' does not exist.")
43
+
44
+ # FFmpeg command to convert MP4 to HLS segments
45
+ os.chmod(input_file, 0o644) # Change permission to read/write for the user
46
+
47
+ command = [
48
  'ffmpeg',
49
+ '-i', input_file, # Input MP4 file
50
+ '-c:v', 'libx264', # Video codec, consistent across all segments
51
+ '-c:a', 'aac', # Audio codec, consistent across all segments
52
+ '-strict', '-2', # Strict flag for AAC codec
53
+ '-flags', '-global_header', # Set global header flag for consistency
54
+ '-hls_time', str(segment_duration), # Segment duration in seconds
55
+ '-hls_list_size', '0', # Keep all segments in the playlist
56
+ '-hls_flags', 'append_list+omit_endlist+program_date_time', # Flags to maintain continuity
57
+ '-hls_segment_type', 'mpegts', # Ensure the segment type is TS
58
+ '-hls_segment_filename', f'{segment_prefix}%d.ts', # Naming pattern for the .ts segments
59
+ '-force_key_frames', f'expr:gte(t,n_forced*{segment_duration})', # Force keyframes for segment duration consistency
60
+ '-avoid_negative_ts', 'make_zero', # Avoid negative timestamps
61
+ output_playlist # Output .m3u8 playlist file
 
62
  ]
63
 
64
  try:
65
+ # Run the FFmpeg command
66
+ subprocess.run(command, check=True)
67
+ print(f"Successfully converted '{input_file}' to HLS segments with playlist '{output_playlist}'.")
68
  except subprocess.CalledProcessError as e:
69
+ print(f"Error during conversion: {e}")
70
+ except Exception as e:
71
+ print(f"An unexpected error occurred: {e}")
 
 
 
 
 
 
 
 
 
 
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ def add_video(video_name, output_path, audio_duration):
76
+ convert_to_hls(video_name, output_path, segment_duration=audio_duration)
77
  return {"message": f'"{video_name}" added to the streaming queue.'}
78
 
79
  async def concatenate_playlists(video_names, base_dir):