You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FFmpeg生成HLS音频流切换轨道时播放器停止的问题求助

解决HLS音频轨道无缝切换问题

你的猜测没错——每次启动新FFmpeg进程时,默认会覆盖重建playlist.m3u8,导致播放器拿到的列表只包含当前轨道的分段,播放完后没有后续内容,自然停止。要实现无缝切换,核心是让新进程的分段信息追加到现有playlist,而非重建,同时保证分段编号连续。

具体解决步骤

  1. 跟踪并传递分段起始编号
    每次启动新轨道的FFmpeg进程前,读取当前playlist中最后一个分段的编号,让新进程从下一个编号开始生成分段,避免重复覆盖,同时保证播放器能识别连续的分段序列。

  2. 强制FFmpeg追加到现有playlist
    你已经加了append_list flag,但需要配合-hls_start_number参数,确保FFmpeg不会从头重建playlist,而是在现有基础上追加新分段。

  3. 保留轨道切换标记
    继续使用discont_start flag,它会在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_segments flag会自动删除超出hls_list_size的旧分段,无需手动处理,避免磁盘占用过大。

内容的提问来源于stack exchange,提问作者demerf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 16:20:59