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

如何用FFmpeg将HLS片段管道输出至numpy数组并解决显示异常

问题

从HLS流提取片段并通过管道输出到numpy数组时,生成的视频出现颜色异常、画面边缘重复的问题。直接用FFmpeg输出为MP4文件时视频显示正常,但通过Python的subprocess调用FFmpeg并将输出存入numpy数组,再用cv2.VideoWriter生成MP4时就会出问题。相关代码如下:

clip_command = f"ffmpeg -y -live_start_index 0 -i master.m3u8 -ss {start} -t {duration} -pix_fmt rgb24 -f rawvideo pipe:1"
generate_clip = Popen(clip_command, shell=True, stdout=PIPE, stderr=STDOUT, bufsize=10**8)

nb_img = 300*300*3
out = cv2.VideoWriter('output.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 15, (300, 300))
while True:
    buffer = generate_clip.stdout.read(nb_img)
    if len(buffer) != nb_img:
        # last frame
        break
    img = np.frombuffer(buffer, dtype='uint8').reshape(300, 300, 3)
    out.write(img)
out.release()

尝试修改-pix_fmt参数无效果,不确定问题根源及该方案是否可行。

解决方案

出现颜色异常和画面边缘重复的问题,核心是像素格式不匹配和帧数据对齐(Stride)错误导致的,以下是具体修复步骤:

1. 修正像素格式匹配问题

OpenCV的VideoWriter默认接收BGR格式的图像,而你用FFmpeg输出的是rgb24格式,直接写入会导致颜色通道反转,这就是颜色异常的直接原因。
修改FFmpeg命令的像素格式为bgr24:

clip_command = f"ffmpeg -y -live_start_index 0 -i master.m3u8 -ss {start} -t {duration} -pix_fmt bgr24 -f rawvideo pipe:1"

2. 处理帧数据对齐问题

FFmpeg输出rawvideo时,会对帧的宽度做字节对齐(通常按16字节对齐),如果你的视频宽度300不是对齐值的倍数,实际每帧的字节数会比300*300*3多,导致后续帧的数据错位,出现画面边缘重复的情况。
解决方法有两种:

  • 方法一:强制FFmpeg输出对齐的宽高,比如把300改成304(16的倍数),但会改变视频尺寸;
  • 方法二:先通过FFprobe获取视频的实际参数(宽、高、每帧字节数),再按实际值读取:
    先执行FFprobe命令获取视频信息:
    ffprobe -v error -select_streams v:0 -show_entries stream=width,height,pix_fmt -of csv=s=x:p=0 master.m3u8
    
    解析结果后得到准确的宽高,计算每帧的字节数(需考虑FFmpeg的对齐规则),或者在FFmpeg命令中添加-vf "scale=trunc(iw/2)*2:trunc(ih/2)*2"确保宽高为偶数(部分编码器要求)。

3. 优化子进程调用

避免使用shell=True(存在安全风险),改用列表形式传递命令参数;同时不要将stderr重定向到stdout,否则FFmpeg的日志会混入视频数据,导致帧损坏:

from subprocess import Popen, PIPE
import numpy as np
import cv2

# 改用列表参数,避免shell=True
clip_command = [
    "ffmpeg", "-y", "-live_start_index", "0", "-i", "master.m3u8",
    "-ss", str(start), "-t", str(duration),
    "-pix_fmt", "bgr24", "-f", "rawvideo", "pipe:1"
]
generate_clip = Popen(clip_command, stdout=PIPE, stderr=PIPE, bufsize=10**8)

width, height = 300, 300
nb_img = width * height * 3

out = cv2.VideoWriter('output.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 15, (width, height))
while True:
    buffer = generate_clip.stdout.read(nb_img)
    if not buffer:
        break
    if len(buffer) != nb_img:
        # 跳过不完整的帧
        continue
    img = np.frombuffer(buffer, dtype='uint8').reshape(height, width, 3)
    out.write(img)
out.release()

# 读取FFmpeg错误信息排查问题
stderr = generate_clip.stderr.read().decode()
if stderr:
    print("FFmpeg错误日志:", stderr)

4. 方案可行性验证

该方案完全可行,只要解决像素格式和帧数据对齐的核心问题,就能正确从管道读取FFmpeg输出的rawvideo并转为numpy数组,再生成正常的MP4视频。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 11:20:28