因
前两天老大给我发了个任务,说让我做一个flask 服务器接口,接收请求,在请求参数中获取 直播拉流地址和 推流地址,然后调用 ffmpeg 到拉流地址上进行拉流 将拉到的流媒体数据推到另一个直播服务器上,
简单来说就是直播的二次转发
过程
什么鬼, 拉流是什么东西?推流又是什么?ffmpeg 又是个什么鬼?obs又是什么?啥是推流码?啊这
但是看他说这很简单的,我也就十分配合的一边点头,一边复读:是是是, 好的,没问题
回去后我百度了下obs ,哦,原来是一个流媒体采集软件啊,简单来说就是采集当前的屏幕和采录到的音频整合成流媒体数据进行 上传到 直播服务器上,推流就是上传流媒体的意思,将流媒体数据推送到服务器上, 那拉流就是从直播服务器上获取数据咯 (花里胡哨的)
那ffmpeg 又是什么东东呢? 老大跟我说是一个 可以从直播服务器上进行拉流的进行转发推流的一个软件,emm… ,经过搜索了一番,哦,知道了,这是一个可以处理视频的一个软件,可以将一个视频抓图,加水印,也可以将视频转换成流媒体数据进行推流
正在我百度学到这些知识的时候,老大给我发了几个推流和拉流的url
这服务器的url 怎么跟 普通的 http 这么像?除了前面的 http 换成了 rtmp 了,这可能就是直播的规则吧,不管了,先完成再研究,这密钥又是什么鬼?呃,按照普通后端服务器的开发角度来看,应该是类似于cookie 值吧,通过这个密钥值来进行区分 你要推流给哪个房间,并且密钥值中还携带了身份验证值,这样就能确保是主播本人在推流了, 应该是这样的,
不管了,直接上马推流开播,珍不戳,直播的感觉珍不戳,没想到我也有当主播的一天,芜湖
哎,不骚了,来看看实际效果
对了,在直播前我已经安装了 ffmpeg 了,ffmpeg 中还有个功能,就是可以直接拉流进行播放
拉流播放命令: ffplay 拉流地址
当然,也可以使用 vlc
播放器来进行拉流 播放直播
其实在现实中遇到好几个问题才到这一步的,首先,老大发的推流地址 前两个是没有推流码的,推流和拉流的地址都几乎是一个地址,只不过推流携带了token值
这很简单就完成的手动的直播推流,ffplay 拉流看效果了,
这种的话我直接就完成了推流到live552 服务器,再用ffmpeg 进行拉流转推倒 live553服务器上了
哦,对了, ffmpeg 拉流转发的命令是:
ffmpeg -i 拉流地址 -vcodec copy -acodec copy -f fmt flv -y 推流地址
-vcodec codec 强制使用codec编解码方式。如果用copy表示原始编解码数据必须被拷贝。
应该是代表拉流 处理流数据中的视频的编码格式吧,可能类似于代码中的 utf-8
, gbk
这些编码格式吧
-acodec codec 使用codec编解码
应该是代表拉流 处理流数据中的 音频编码格式吧, copy代表复制,不更改编码格式
-f fmt 强迫采用格式fmt
这个猜不到
-y
应该是将流数据输出到位置
后来给我发了一个带 推流码的直播地址,
啊这, 拉流地址呢?老大说让我把这个推流地址和推流码整合起来让 ffmpeg 可以进行推流,当时我也不太懂,还以为是从这个推流地址和一些参数也可以进行拉流的, 这个带推流码的直播地址推流很简单,就是将推流码拼接到推流地址后面就可以了,当时我先是用obs 推流到552 服务器上,再ffmpeg 从552中进行拉流推倒 这个有推流码的服务器上,但是到这个有推流码的地址去拉流看效果的时候就卡住了,
当时我还以为是直接从这个推流地址上进行拉流的,淦,弄了好久,不管这个推流地址和推流码怎么配合拼接都无法进行拉流,也不是无法拉流, 有时候 fflpay 这个直播地址和推流码也是可以拉到的,按道理来说应该不行的,不知道怎么回事,但它确实可以拉到数据
后来折腾了好久,我忽然想到,这个服务器应该不是我们公司的服务器,直播推流域名明细不同,于是我直接百度 搜索,没想到真的是其他公司的啊,啊这,那这个拉流地址就有可能不是这个地址了,于是我就到这个网站上注册,然后进行直播测试,然后通过手机抓包抓出了这个拉流地址,嗯,,真不是同一个地址,这时候通过拉流地址配合推流参数就正常的获取到直播数据了
程序
接下来就是写程序了,由于这个 ffmpeg 是以软件 程序的方式存在服务器上的, 我当时第一想法就是用python 中的 os 模块中 的 system 直接调用 ffmpeg ,
但是老大的需求要 监听调用起来的进程,当进程结束后,比如 直播跳转推流了,这时候 ffmpeg 拉流就拉不到新数据,这时候 这个进程就会结束,或者 传递过来的 推流或拉流地址有问题的时候 ffmpeg 也会调不起来,这些情况要给回调url 发送调用状态,如果用 os.system()
好像做不到监听吧(可能我太菜了,不知道怎么做)
于是又双叒叕 百度了一下,诶,这个 python有对应的 库,这就舒服了啊(带佬已经封装好了ffmpy
)
带佬 np
舒服,直接完成任务
import logging, requests
from concurrent.futures.thread import ThreadPoolExecutor
from ffmpy import FFmpeg
from flask import Flask, request, jsonify
app = Flask(__name__)
thread = ThreadPoolExecutor(max_workers=5) # 创建一个最大容纳数量为5的线程池
logging.basicConfig(filename='logger.log', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S',
format="%(asctime)s %(message)s")
chlr = logging.StreamHandler() # 输出到控制台的handler
logging.getLogger().addHandler(chlr)
# input_path 拉流地址, output_path 推流地址
def start_transpond(input_url, output_url, callback_url):
# 流媒体数据转发基本参数
params = "-vcodec copy -acodec copy -f flv -y "
ff = FFmpeg(
inputs={input_url: None},
outputs={output_url: params})
logging.info("正在调用 ffmpeg 进行推流转发, 拉流地址是: {} , 推流地址是:{}\n".format(input_url, output_url))
logging.info("ffmpeg 命令是: {}\n".format(ff.cmd))
try:
ffmpeg_ret = ff.run()
if ffmpeg_ret[0] is None and ffmpeg_ret[1] is None:
requests.post(callback_url, data={"code": "0", "msg": "ffmpeg 调用结束,程序正常退出, 退出信息为 {} 拉流地址是: {} , 推流地址是:{}".format(ffmpeg_ret, input_url, output_url)})
logging.info("ffmpeg 调用结束,程序正常退出, 退出信息为 {} \n 拉流地址是: {} , 推流地址是:{}\n".format(ffmpeg_ret, input_url, output_url))
else:
requests.post(callback_url, data={"code": "201", "msg": "ffmpeg 调用结束,程序可能非正常退出,退出信息为 {} 拉流地址是: {} , 推流地址是:{}".format(ffmpeg_ret, input_url, output_url)})
logging.info("ffmpeg 调用结束,程序可能非正常退出,退出信息为 {} \n 拉流地址是: {} , 推流地址是:{}\n".format(ffmpeg_ret, input_url, output_url))
except Exception as bs:
# print(bs)
requests.post(callback_url, data={"code": "500",
"msg": "调用ffmpeg错误,错误信息为: {}".format(bs)})
logging.info("调用ffmpeg错误,错误信息为: {}".format(bs))
@app.route("/", methods=["POST"])
def index():
input_url = request.form.get("input_url")
output_url = request.form.get("output_url")
callback_url = request.form.get("callback_url")
logging.info("接收到任务 参数有: {}".format(request.form))
if not all([input_url, output_url, callback_url]):
return jsonify({"code": 400, "msg": "缺少必要的参数"})
logging.info("正在往线程池添加任务")
thread.submit(start_transpond, input_url, output_url, callback_url)
logging.info("往线程池添加任务结束")
return jsonify({"code": 0, "msg": "任务已创建"})
app.run(host="0.0.0.0", port="7788", debug=False)
以上是我使用 ffmpeg 的记录,知识不算深入,仅当做学习总结记录