一起用代码吸猫!本文正在参与【喵星人征文活动】。
看到掘金官方推出了一个“吸猫”征文活动,本人作为伪铲屎官心血来潮试试吧,毕竟奖励是程序员心中的最佳键盘HHKB呢,虽然是一只咸鱼,但是梦想还是要有的。
简简单单的用CSS画一只猫?感觉又没有啥深度,而且这样子的行为更像是在水文章,并没有什么竞争力。既然不写前端页面,自己又不会深度学习相关的知识,那么剩下的也就只有声网Agora的直播SDK了。本人几年前有用过Agora的AndroidSDK用来封装过直播间,现在再用它来简单的跑个Demo应该没啥大问题吧?但是话说回来,用手机直播手机肯定是顶不住的,先不说手机发烫严重的问题,单单是手机电池的电量问题就够吃一壶了。所以今天换一个方式来直播,用电脑直播,别说24小时,就算连着直播一个月都没问题。说到电脑直播大家肯定会想到OBS,毕竟各大直播平台的主播都在用这个软件来直播的,因为obs-studio项目是开源免费的,而且支持各种插件自定义自己的直播间,实属香。但是如果我用OBS来直播,那么又没涉及到代码方面的知识,这不行啊,这不符合掘金的要求啊,那咋办,只能从底层出发。我们都知道音视频的底层是C,而且音视频最好用的就是ffmpeg,而且因为是24h不间断的直播,那么在nas上面,或者是服务器上面推流,更加节能省电,所以今天我们就先用ffmpeg来实现一个简单的推流功能。
作为0基础没搞过ffmpeg的混子,查看了官网的wiki以及还有大佬的blog之后,发现我们想要推流视频到流媒体服务器,那么我们就要先搭建一个流媒体服务器,网上最多的是用nginx来做。
mac搭建nginx+rtmp服务器
原想用mac中自带的Apache搭建,但是naginx是轻量级的,同样起web 服务,也比apache 占用更少的内存及资源,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能,用它来做hls或者rtmp流媒体服务器是非常不错的选择.
一、使用Homebrow安装 nginx
(1)执行克隆命令,github的项目(github.com/denji/homeb…)
brew tap denji/nginx
!!!注意brew tap homebrew/nginx报下面的错误,homebrew/nginx已经弃用. 报错:Error: homebrew/nginx was deprecated. This tap is now empty as all its formulae were migrated.
(2)执行安装命令:
brew install nginx-full --with-rtmp-module
至此nginx和rtmp模块就安装好了,下面开始来配置nginx的rtmp模块 接下来看一下nginx安装在什么地方
brew info nginx-full
nginx安装所在位置:
/usr/local/opt/nginx-full/bin/nginx
nginx配置文件所在位置:
/usr/local/etc/nginx/nginx.conf
(3)启动nginx,执行命令:
sudo nginx
二、测试:
在浏览器中打开如下地址:http://localhost:8080
出现以上界面,说明安装成功.
如果终端上提示
nginx: [emerg] bind() to 0.0.0.0:8080 failed (48: Address already in use)
则表示8080端口被占用了, 查看端口PID
lsof -i tcp:8080
kill掉占用8080端口的PID
kill 9603(这里替换成占用8080端口的PID)
三.重新加载nginx的配置文件
(1)修改nginx.conf这个配置文件,配置rtmp 复制nginx配置文件所在位置:
vi /usr/local/etc/nginx/nginx.conf
(2)执行上面命令会直接编辑,或者直接前往当前文件用记事本打开. 滚动到最后面(最后一个}后面即可,不能在{}里面),添加一下代码,进行配置,最后记得保存。
rtmp {
server {
listen 1935;
#直播流配置
application rtmplive {
live on;
#为 rtmp 引擎设置最大连接数。默认为 off
max_connections 1024;
}
application hls{
live on;
hls on;
hls_path /usr/local/var/www/hls;
hls_fragment 1s;
}
}
}
(3)编辑完成之后,执行一下重新加载配置文件命令:
sudo nginx -s reload
需要输入开机密码 sudo不加的话会报错: nginx: [alert] could not open error log file: open() "/usr/local/var/log/nginx/error.log" failed (13: Permission denied)
该命令执行后会出来一个弹框询问是否允许 nginx 加入到网络中,选择允许即可。
(4)重启nginx:
sudo /usr/local/opt/nginx-full/bin/nginx -s reload
PS:如果你之前不是按照我上面的方法按照的 nginx,在执行 sudo nginx -s reload 时报了如下错,建议你卸载 nginx后按照我上面的步骤重新安装nginx。
nginx: [emerg] unknown directive "rtmp" in /usr/local/etc/nginx/nginx.conf:119
nginx常用方法:
重新加载配置文件: nginx -s reload
重新加载日志: nginx -s reopen
停止 nginx: nginx -s stop
有序退出 nginx: nginx -s quit
出现权限不足的错误提示时,命令前加上 sudo
四、安装ffmepg工具
brew install ffmpeg
上图表示ffmepg安装完成
五、本地推流
(1)、搭建本地视频直播,比如电脑上面有很多电影,我们可以通过推流的形式实现实时直播:
A:在电脑上播放推流内容 安装一个支持rtmp协议的视频播放器,Mac下可以用VLC VLC或者使用开源项目IINA 本地下载一个视频文件路径为 /Users/iOS002/Desktop/loginmovie.mp4 执行以下命令
ffmpeg -re -i /Users/iOS002/Desktop/loginmovie.mp4 -vcodec libx264 -acodec aac -strict -2 -f flv rtmp://localhost:1935/rtmplive/room
用vlc 然后打开 VLC 中 的 file -- Open Network, 直接输入代码中的 url:
rtmp://localhost:1935/rtmplive/room
即可以通过VLC来播放终端中实时推过来的 RTMP流。
效果如图
B:通过手机观看电脑的推流 通过集成 ijkplayer 把地址换成推流的地址即可观看: 播放端用的针对RTMP优化过的ijkplayer,ijkplayer是基于FFmpeg的跨平台播放器,这个开源项目已经被多个 App 使用,其中映客、美拍和斗鱼使用了 ijkplayer。
(2)、桌面录制或者分享
ffmpeg -f avfoundation -i "1" -vcodec libx264 -preset ultrafast -acodec libfaac -f flv rtmp://localhost:1935/rtmplive/room
(3)、桌面+麦克风
ffmpeg -f avfoundation -i "1:0" -vcodec libx264 -preset ultrafast -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost:1935/rtmplive/room
(4)、桌面+麦克风,并且还要摄像头拍摄到自己
ffmpeg -f avfoundation -framerate 30 -i "1:0" \-f avfoundation -framerate 30 -video_size 640x480 -i "0" \-c:v libx264 -preset ultrafast \-filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10' -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://localhost:2016/rtmplive/room
六、手机推流
可以用 LFLiveKit 集成到工程进行推流,LFLiveKit已经帮我们实现了视频采集、后台录制、美颜功能、支持h264、AAC编码,动态改变速率,RTMP传输等,我们开发的时候就很简单了只需把localhost:8080换成自己电脑的ip地址即可:
rtmp://10.0.0.17:1935/rtmplive/room
注意通过网络查看电脑的局域网 IP替换掉 localhost 即可。
A:通过VLC观看手机的推流 打开手机直播后,然后在电脑上打开VLC(同上),就能实现手机推流,在电脑上拉流播放了!!(注:手机需要和电脑连接同一网络!)
B:通过手机观看手机的推流(这也就是市面上的那些直播App的最终实现形式了) 通过集成 ijkplayer 把地址换成推流的地址即可观看。
PS:一个很隐蔽的报错:
如果你发现你的推流地址和拉流地址在电脑上都是好好的,但是通过手机实现的时候就是报错,那么估计就是因为Mac防火墙的问题。
ERROR: PILI_RTMP_Connect0, failed to connect socket. 60 (Operation timed out)
ERROR: WriteN, PILI_RTMP send error 9, Bad file descriptor, (140 bytes)
ERROR: PILI_RTMP_Connect0, failed to connect socket. 60 (Operation timed out)
ERROR: WriteN, PILI_RTMP send error 9, Bad file descriptor, (140 bytes)
关闭 Mac 的防火墙即可解决问题。
更简单的流媒体服务器
mac-local-rtmp-server
安装之后直接打开就可以生成本地推流直播的地址了,我们直接复制粘贴地址就好了,根本不需要我们去做多余的配置
我们直接在终端里面输入ffmpeg -f avfoundation -framerate 10 -pixel_format yuyv422 -i "0" -f copy -f flv rtmp://127.0.0.1/live/Skkv8MTIF
这样子就可以看到
推流成功了,我们再使用
IINA来打开rtmp://127.0.0.1/live/Skkv8MTIF就可以插到摄像头捕获的画面了(由于自己没有养猫,所以简单拿一直猫咪的素材来演示了)
你以为到这里就完了?不不不,这里只是局域网,撸猫看猫肯定是要24小时不分时间和地点的呀,既然是这样我就想着把视频流推流到Bilibili上面去吧,由于每次开播只要是短时间内断开链接,那么直播平台的推流地址还有推流码都是和之前一样,所以我这里简单的写了一个shell脚本来推流,这样子我们只需要每次启动./bilibili.sh的时候,就能够保持自动推流了。
###
# @Description:
# @Author: itgoyo
# @E-mail: itgoyo@gmail.com
# @Github: itgoyo
###
while true
do
ffmpeg -f avfoundation -framerate 10 -pixel_format yuyv422 -i "0" -f copy -f flv "rtmp://live-push.bilivideo.com/live-bvc/?streamname=live_12767066_6265400&key=0c32218670c24b8ea4bb89xxxxxxx4b6c2&schedule=rtmp&pflag=9"
done
但是有时候我发现直播断开连接,while循环还是没能推流成功,可能是由于断开太久或者是别的原因导致推流服务器或者是推流码变动了,这样子我这边就不能及时发现直播已经断开。所以我就在网上找关于B站直播的接口相关数据,最后真的让我找到了。于是简单的使用vue来实现了一个断线的判断,然后他会调用浏览器的弹窗,在显示器的右上角显示。这就引申出别的问题了,如果使用浏览器来监听是否断线的话,要保证浏览器是活着的状态,也就是说浏览器要打开,达不到我理想的预期。用之前的Server酱 次数现在又有限制,而且效果也不明显。所以网上查找资料,看看这么调用MacOS系统自带的弹窗,然后在StackOverflow上面看到一个简单的demo于是,根据接口需要,还有为了防止封号的危险,把接口调用改成每分钟再调用一次,最重代码如下。
# coding=utf-8
# 需要安装 [sudo]gem install terminal-notifier
# The notifier function
'''
Description:
Author: itgoyo
E-mail: itgoyo@gmail.com
Github: itgoyo
'''
import json
import requests
import os
from datetime import datetime
from threading import Timer
def notify(title, subtitle, message,appIcon):
t = '-title {!r}'.format(title)
s = '-subtitle {!r}'.format(subtitle)
m = '-message {!r}'.format(message)
i = '-contentImage {!r}'.format(appIcon)
os.system('terminal-notifier {}'.format(' '.join([m, t, s,i])))
# 打印时间函数
def printTime(inc):
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
res = requests.get("https://api.bilibili.com/x/space/acc/info?mid=12767066").json()
# print(res)
# print(res["data"])
# print(res["data"]["live_room"])
# print(res["data"]["live_room"]["liveStatus"])
if (res["data"]["live_room"]["liveStatus"])!=1: #1是在直播的状态,不是1就是断线
# Calling the function
notify(title = 'Bilibili直播间',
subtitle = res["data"]["live_room"]["title"],
message = '⚡️⚡️直播间断开推流⚡️⚡️',
appIcon = 'https://i2.hdslb.com/bfs/face/863b88d596eed9dbef28c67a4796bec08e478d79.jpg')
t = Timer(inc, printTime,(inc,))
t.start()
printTime(1*60)
一般提醒都是一闪而过,这样子我们可能会没注意到,我们在系统提醒弹窗里面设置成常驻,只要你不点击关闭的按钮,它就会一直在右上角显示。起码现在这样子已经满足了我一天24h云撸猫的需求,如果各位小伙伴有更好的方案,欢迎评论区一起讨论,有疑问的小伙伴也可以一起讨论。
文章参考: