Mediamtx+Python读取webrtc流

一、功能思路:

1、我采用ffmpeg -re -stream_loop -1 -i xcc.mp4 -c:v libx264 -profile:v baseline -x264opts "bframes=0:repeat_headers=1" -b:v 1500k -preset fast -f flv rtmp://127.0.0.1:1835/stream/111推流到mediamtx的rtmp上
2、通过mediamtx自带的转流,我将可以直接把推过来的流以webrtc方式访问http://127.0.0.1:8889/stream/111
3、由于我确认http://127.0.0.1:8889/stream/111能够播放出视频,所以我现在想采用python的方式读取http://127.0.0.1:8889/stream/111/whep的流,来实现保存流的每帧图像

涉及到的工具有:

  • 开源视频流服务器—>mediamtx
  • 本地推流rtmp工具—>ffmpeg+lal
  • 编写代码读取webrtc流工具—>PyCharm
    在这里插入图片描述

二、代码编写实现:

import asyncio
import json
from aiortc import RTCPeerConnection, RTCSessionDescription, VideoStreamTrack
import aiohttp
import cv2
import numpy as np

class DummyVideoTrack(VideoStreamTrack):
    """创建一个虚拟的视频轨道,以防止 WebRTC 报错"""
    def __init__(self):
        super().__init__()

    async def recv(self):
        # 生成一个黑色帧,防止 WebRTC 报错
        width, height = 640, 480
        frame = np.zeros((height, width, 3), dtype=np.uint8)
        return frame

class VideoReceiver:
    def __init__(self):
        self.frame = None

    async def consume_track(self, track):
        while True:
            try:
                frame = await track.recv()
                img = frame.to_ndarray(format="bgr24")
                cv2.imshow("Video", img)
                cv2.waitKey(1)
            except Exception as e:
                print("处理帧错误:", e)
                break

async def send_ice_candidate(candidate, ice_url):
    async with aiohttp.ClientSession() as session:
        async with session.post(
            ice_url,
            data=json.dumps({"candidate": candidate}),
            headers={"Content-Type": "application/json"}
        ) as response:
            if response.status != 200:
                print(f"发送 ICE candidate 错误: {response.status}")

async def run():
    pc = RTCPeerConnection()
    receiver = VideoReceiver()

    # 添加一个虚拟的视频轨道,防止 WebRTC 报错
    pc.addTrack(DummyVideoTrack())


    # 处理接收到的媒体轨道
    def on_track(track):
        print(f"接收到 {track.kind} 轨道")
        if track.kind == "video":
            asyncio.create_task(receiver.consume_track(track))

    pc.add_listener("track", on_track)

    # 监听 ICE 连接状态变化
    def on_ice_connection_state_change():
        print(f"ICE 连接状态: {pc.iceConnectionState}")

    pc.on("iceconnectionstatechange", on_ice_connection_state_change)

    # 监听 ICE Candidate
    def on_ice_candidate(candidate):
        if candidate:
            print(f"新 ICE candidate: {candidate}")
            asyncio.create_task(send_ice_candidate(candidate, ice_url))

    pc.on("icecandidate", on_ice_candidate)

    # 创建 SDP Offer
    offer = await pc.createOffer()
    await pc.setLocalDescription(offer)

    # 发送 Offer 到 WHEP 端点
    whep_url = "http://127.0.0.1:8889/stream/111/whep"
    headers = {"Content-Type": "application/sdp"}

    async with aiohttp.ClientSession() as session:
        async with session.post(
                whep_url,
                data=pc.localDescription.sdp,
                headers=headers
        ) as response:
            if response.status != 201:
                raise Exception(f"服务器返回错误: {response.status}")

            answer_sdp = await response.text()
            answer = RTCSessionDescription(sdp=answer_sdp, type="answer")
            await pc.setRemoteDescription(answer)

            if "Location" in response.headers:
                ice_url = "http://127.0.0.1:8889" + response.headers["Location"]
                print("ICE 协商 URL:", ice_url)

        try:
            while True:
                await asyncio.sleep(1)
        except KeyboardInterrupt:
            pass
        finally:
            await pc.close()

if __name__ == "__main__":
    asyncio.run(run())

三、效果展示:

  • 浏览器可以直接播放webrtc的扩展地址,然后最上层就是代码cv2读取每帧显示出来的画面
    在这里插入图片描述
<think>嗯,用户遇到了一个bash脚本的问题。他们输入备份名称和路径后,脚本无法运行。需要仔细看看哪里出错了。首先,我应该检查用户的脚本代码,看看有没有语法错误或者逻辑问题。 用户提供的脚本中,有几个地方看起来有问题。比如,创建备份目录的那一行,变量赋值好像被截断了,显示了很多奇怪的字符,可能是复制粘贴时出错了。原代码中的`full_backup_dir=“$backup_path/{backup_name}_$(date +%Y%m%d%H%M%S)”`,这里可能用了错误的引号,中文引号“”而不是英文的"",这会导致变量无法正确展开。 然后,rsync命令中的--link-dest参数,路径是否正确?用户是不是在变量引用时用了错误的引号?还有rsync命令的源目录和目标目录是否正确?用户的脚本中源目录是/home/,而目标目录是$full_backup_dir,但要注意路径后面是否有斜杠,这会影响rsync的行为。 另外,rsync命令后面的错误重定向部分,用户用了`2>&1 | tee ...`,是否正确捕获了错误输出?还有,在trap命令中,单引号是否被正确使用?用户脚本中的trap用了反引号,这可能是个问题,应该用单引号包裹命令。 还有,检查是否有权限问题,备份路径是否可写?用户输入的路径是否存在,虽然脚本里检查了目录是否存在,但如果路径中有空格或者特殊字符,是否需要加引号? 最后,查看备份日志文件,是否有具体的错误信息提示?用户提到无法运行,可能有语法错误导致脚本直接退出。比如变量赋值时的错误引号,或者命令参数格式不正确。 可能需要逐步检查每一行,特别是变量替换、引号使用、命令参数是否正确。另外,脚本中的ParseError提示,可能是用户在粘贴代码时出现了格式问题,比如KaTeX解析错误,可能是在某些地方存在未转义的字符或者符号。</think>好的,让我们逐步排查这个增量备份脚本的问题: 一、观察到的直接问题 1. 变量赋值错误 原代码存在乱码: ```bash full_backup_dir=“$backup_path/{backup_name}_$(date +%Y%m%d%H%M%S)” ``` 应修正为: ```bash full_backup_dir="$backup_path/${backup_name}_$(date +%Y%m%d%H%M%S)" ``` * 使用英文双引号 * 变量名用${}明确范围 2. rsync命令格式错误 原命令存在符号错乱: ```bash rsync -a --progress --link-dest=“$backup_path/latest” --exclude=“.tmp" --exclude=".cache/” /home/ ... ``` 应修正为: ```bash rsync -a --progress --link-dest="$backup_path/latest" \ --exclude=".tmp" --exclude=".cache/" \ /home/ "$full_backup_dir" 2>&1 | tee "${full_backup_dir}/backup.log" ``` * 统一使用英文引号 * 使用反斜杠\换行保持可读性 * 确保源目录(/home/)和目标目录变量正确分隔 二、常见问题排查步骤 1. 添加调试模式 在脚本开头添加: ```bash set -x # 开启命令回显 # 或使用分段调试 # trap 'echo "正在执行: $BASH_COMMAND"' DEBUG ``` 2. 检查路径权限 ```bash # 在创建目录前添加权限验证 if [ ! -w "$backup_path" ]; then echo "错误:无写入权限!" exit 1 fi ``` 3. 验证rsync可用性 ```bash # 单独执行rsync命令测试 dry_run="rsync -avn --progress --link-dest=$backup_path/latest /home/ $full_backup_dir" echo "测试命令:$dry_run" eval $dry_run ``` 三、完整修正脚本 ```bash #!/bin/bash trap 'echo -e "\n操作已中断!已备份文件保持完整。"; exit 1' INT TERM read -p "请输入备份名称: " backup_name read -p "请输入备份存储路径: " backup_path # 路径标准化处理(处理尾部斜杠) backup_path="${backup_path%/}" if [ ! -d "$backup_path" ]; then echo "错误:备份路径不存在!" exit 1 fi full_backup_dir="${backup_path}/${backup_name}_$(date +%Y%m%d%H%M%S)" mkdir -p "$full_backup_dir" || exit 1 echo "正在执行增量备份..." rsync -a --progress --link-dest="${backup_path}/latest" \ --exclude=".tmp" --exclude=".cache/" \ /home/ "$full_backup_dir" 2>&1 | tee "${full_backup_dir}/backup.log" ln -sfn "$full_backup_dir" "${backup_path}/latest" echo "增量备份已完成!存储位置:$full_backup_dir" ``` 四、关键改进点 1. 路径处理 * `${backup_path%/}` 消除路径尾部斜杠 * 使用`|| exit 1`确保目录创建失败时立即退出 2. 错误处理增强 * 标准化变量引用方式 `${var}` * 使用明确的退出状态码 3. 日志记录优化 * 使用`tee`同时输出到控制台和日志文件 * 保留完整的标准输出和错误输出 五、典型测试案例 ```bash # 测试命令 ./backup_script.sh 请输入备份名称: test_backup 请输入备份存储路径: /mnt/backup # 验证步骤 ls -l /mnt/backup/test_backup_* # 应看到带时间戳的目录 ls -l /mnt/backup/latest # 应显示正确符号链接 cat /mnt/backup/test_backup_*/backup.log # 检查日志输出 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值