FFmpeg生成HLS音频流切换轨道时播放器停止的问题求助
解决HLS音频轨道无缝切换问题
你的猜测没错——每次启动新FFmpeg进程时,默认会覆盖重建playlist.m3u8,导致播放器拿到的列表只包含当前轨道的分段,播放完后没有后续内容,自然停止。要实现无缝切换,核心是让新进程的分段信息追加到现有playlist,而非重建,同时保证分段编号连续。
具体解决步骤
跟踪并传递分段起始编号
每次启动新轨道的FFmpeg进程前,读取当前playlist中最后一个分段的编号,让新进程从下一个编号开始生成分段,避免重复覆盖,同时保证播放器能识别连续的分段序列。强制FFmpeg追加到现有playlist
你已经加了append_listflag,但需要配合-hls_start_number参数,确保FFmpeg不会从头重建playlist,而是在现有基础上追加新分段。保留轨道切换标记
继续使用discont_startflag,它会在playlist中插入#EXT-X-DISCONTINUITY标记,告诉播放器这是不同轨道的切换,避免因编码参数差异导致播放错误。
修改后的代码示例
首先添加一个辅助函数,用于获取当前最后一个分段的编号:
def get_last_segment_number(self): playlist_path = os.path.join(self.output_path, "playlist.m3u8") if not os.path.exists(playlist_path): return 0 with open(playlist_path, 'r') as f: lines = f.readlines() # 反向遍历,找到最后一个.ts分段的行 for line in reversed(lines): line = line.strip() if line.endswith('.ts'): # 从分段文件名中提取编号(比如segment_008.ts → 8) segment_base = os.path.basename(line) num_part = segment_base.split('_')[1].split('.')[0] return int(num_part) return 0
然后修改start_worker函数,加入分段起始编号的传递:
def start_worker(self, track: Track): track_hash = track.hash input_path = os.path.join(self.source_path, track.filepath.name) segment_path = os.path.join(self.output_path, "segment_%03d.ts") output_path = os.path.join(self.output_path, "playlist.m3u8") # 获取上一个进程的最后分段编号,新进程从下一个编号开始 last_segment_num = self.get_last_segment_number() start_number = last_segment_num + 1 command = ["ffmpeg", "-re", "-i", input_path, "-map", "a", "-c:a", "aac", "-b:a", "128k", "-f", "hls", "-hls_time", "8", "-threads", "2", "-hls_list_size", "6", "-hls_start_number", str(start_number), # 新增:指定分段起始编号 "-hls_flags", "delete_segments+append_list+discont_start+program_date_time", "-hls_segment_filename", segment_path, output_path] logging.info(f"Started Worker for {self.name}, playing {input_path}") self.current_worker = subprocess.Popen(command) self.last_played = track return self.current_worker
额外注意事项
- 进程顺序控制:确保前一个轨道的FFmpeg进程完全结束后,再启动下一个进程,避免分段生成混乱。可以在代码中加入
self.current_worker.wait()来等待上一个进程结束。 - 播放器端设置:确保HLS播放器开启了自动刷新playlist的功能(大多数主流播放器默认会定期请求.m3u8文件,间隔通常等于
hls_time),这样才能及时获取到追加的新分段。 - 分段清理:
delete_segmentsflag会自动删除超出hls_list_size的旧分段,无需手动处理,避免磁盘占用过大。
内容的提问来源于stack exchange,提问作者demerf
相关产品推荐
相关产品推荐

