如何在本地设置直播服务器

目前我知道的有以下三种方式,nginx-rtmp-module 编译复杂,srs 也比较麻烦,Node-Media-Server 是最方便的。

nginx-rtmp-module

在 Windows10 上面 nginx-rtmp-module 太复杂,找了很多文档,都没有解决,后来找到一个已经嵌入 rtmp 模块的 nginx 版本 nginx-rtmp-win32,设置 conf 之后,启动 nginx 即可使用。

nginx.conf 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
worker_processes  2;

#pid logs/nginx.pid;

events {
worker_connections 8192;
}

rtmp {
server {
listen 1935;
chunk_size 4000;
application live {
live on;
}
application hls{
live on;
hls on;
hls_path E:/nginx-rtmp-win32-1.2.1/temp/hls;
hls_fragment 5s;
}
}
}

http {
#include /nginx/conf/naxsi_core.rules;
include mime.types;
default_type application/octet-stream;


sendfile off;
#tcp_nopush on;

server_names_hash_bucket_size 128;

## Start: Timeouts ##
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 30;
send_timeout 10;
keepalive_requests 10;
## End: Timeouts ##

#gzip on;

server {
listen 8388;
server_name localhost;


location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root nginx-rtmp-module/;
}
location /control {
rtmp_control all;
}

location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias E:/nginx-rtmp-win32-1.2.1/temp/hls;
expires -1;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *;
}

location / {
root html;
index index.html index.htm;
}
}
}

使用 ffmpeg 推流

1
2
3
4
5
6
7
8
9
10
11
# rtmp 推流地址 `rtmp://127.0.0.1:1935/live/home`
# hls 推流地址 `rtmp://127.0.0.1:1935/hls/home`

# rtmp 拉流地址 `rtmp://127.0.0.1:1935/live/home`
# hls 拉流地址 `http://localhost:8388/hls/home.m3u8`

# rtmp 推流, -stream_loop -1 代表循环推流
ffmpeg.exe -stream_loop -1 -re -i .\ik.mp4 -c copy -f flv rtmp://127.0.0.1:1935/live/home

# hls 推流
ffmpeg.exe -stream_loop -1 -re -i .\ik.mp4 -c copy -f flv rtmp://127.0.0.1:1935/hls/home

hls 推流与本地分片

使用 hls 推流,我们可以在本地目录看到不断更新的 m3u8 文件,不断生成的 ts 文件,这些文件会被浏览器不断请求,推流结束之后这些本地文件就会被删除。

Node-Media-Server

Node-Media-Servernginx-rtmp-win32 统一作者的作品,基于 Node 实现的直播服务器。

安装 node-media-server 之后,执行以下代码,就启动了直播服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const NodeMediaServer = require('node-media-server');

const config = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 30,
ping_timeout: 60
},
http: {
port: 8000,
mediaroot: './media',
allow_origin: '*'
},
trans: {
ffmpeg: 'E:/Program Files/FFMpeg/bin/ffmpeg.exe',
tasks: [
{
app: 'live',
hls: true,
hlsFlags: '[hls_time=2:hls_list_size=3:hls_flags=delete_segments]',
dash: true,
dashFlags: '[f=dash:window_size=3:extra_window_size=5]'
}
]
}
};

var nms = new NodeMediaServer(config)
nms.run();

根据以上代码的配置,推流之后,可以同时支持四种拉流地址。此时,就可以在本地同时看到 ts 文件和 m4s 文件分片。

1
2
3
4
5
6
7
8
9
10
11
12
13
# ffmpeg.exe -stream_loop -1 -re -i .\ik.mp4 -c copy -f flv rtmp://localhost/live/STREAM_NAME
# rtmp://localhost/live/STREAM_NAME
# http://localhost:8000/live/STREAM_NAME.flv
# http://localhost:8000/live/STREAM_NAME/index.m3u8
# http://localhost:8000/live/STREAM_NAME/index.mpd

ffmpeg.exe -stream_loop -1 -re -i .\ik.mp4 -c copy -f flv rtmp://localhost/live/home

# 拉流地址,home 即 STREAM_NAME
# rtmp://localhost/live/home
# http://localhost:8000/live/home.flv
# http://localhost:8000/live/home/index.m3u8
# http://localhost:8000/live/home/index.mpd

拉流地址可以通过 VLC 或西瓜播放器验证。

微信开发者工具异常

最近下载最新的微信开发者工具稳定版,编译小程序,发现预发看到预览效果,查看 Console 发现很多此类错误:

ReferenceError: define is not defined

这是因为调试基础库下载失败,可能因为公司内网网络原因,无法下载调试基础库。

可以切换到其他网络,比如 4G,下载成功后将在AppData\Local\微信开发者工具\User Data\1a695ca2de1a85735f93a43fb366c83f\WeappVendor 目录下可以找到如下这些文件

这些文件可以复制到其他电脑中,也可以自己保存这些文件备用。

fabric.js 实例

绘制视频并播放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var canvas = new fabric.Canvas("canvas", { backgroundColor: "black" });

var video1El = document.createElement("video");
video1El.src = "http://a.com/demo.mp4";
video1El.width = 1280;
video1El.height = 720;
video1El.loop = true;

var video1 = new fabric.Image(video1El);

canvas.add(video1);

fabric.util.requestAnimFrame(function render() {
canvas.renderAll();
fabric.util.requestAnimFrame(render);
});

function play() {
video1El.play();
}

跨域加载图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 第一种方法
fabric.Image.fromURL(
"https://a.com/demo.png",
function (image) {
image.set({
left: 50,
top: 140,
opacity: 0.8,
}).scale(0.5);// scale 是个方法
canvas.add(image);
}, { crossOrigin: 'Anonymous' } // 这样设置也可以
);


// 第二种方法
const image = new Image();
// 设定跨域支持,必须在设置 src 之前
image.setAttribute("crossOrigin", "anonymous");
image.src ="https://a.com/demo.png";

// 图片加载成功后才添加到 canvas 中
image.onload = function () {
const fabImage = new fabric.Image(image, { left: 20, top: 20 });
canvas.add(fabImage);
};


// 验证方式,将Canvas复制到剪贴板
function copy() {
const canvas = document.getElementById("canvas1");
canvas.toBlob(function (blob) {
const item = new ClipboardItem({ "image/png": blob });
navigator.clipboard.write([item]);
alert("Copied! paste it on paint");
});
}

fabric.js 若干经验

  • 使用 fabric.StaticCanvas 就不会覆盖一层操作区,不可选中、编辑

fabric.js 代码分析

fabric.js 打包根据条件可能会用到 YUI-Compressor,一个 jar 包类型的文件压缩工具。不过默认还是用 uglifyjs 来压缩。

fabric.js 既可以在浏览器端允许,也可以在 Node 端运行。如果在浏览器端运行,不需要依赖任何包,如果在 Node 端运行,需要依赖 canvasjsdom,用于模拟浏览器环境。

除了 uglify-js,fabric.js 打包没有依赖任何打包工具,例如 gulp 或 webpack 等,完全是将多个文件按一定的顺序合并成一个文件,然后再压缩。

运行 yarn build 就可以打包 fabric.js,还能生成 fabric.min.js.gz 文件。

fabric.js 通过 onchange 包监控文件修改,对应 yarn build:watch 命令。fabric.js 打包默认不支持 gestures,如果需要支持,可以使用 yarn build_with_gestures 命令。不同的打包命令,都是通过参数来区分,在 build.js 文件中根据参加执行不同的打包条件。

git push gitee dev:master 将 dev 分支提交到 gitee 上的 master 分支

文件结构

fabric 的主要代码都在 src 目录,仅在支持 gestures 的情况下,会将 lib/event.js 打包进去,lib 目录下的其他文件不会用到。

根据打包文件的顺序,我们来依次解析各文件。

HEADER.js

创建 fabric 对象,定义的版本信息,定义 AMD 导出方式,判断运行环境是否包含 document,window 等,如果在 node 环境中,则会 jsdomcanvas,创建 DOMParser、Canvas 等对象。

定义 DPI,定义常用各种正则表达式,定义缓存大小,定义分辨率,定义后台开启 WebGL 滤镜的方法。

src/globalFabric.js

将上面定义的 fabric 对象,放到 window 全局中。

lib/event.js

支持 gestures 的事件,单击、双击、tap 等

src/mixins/observable.mixin.js

定义了发布订阅对象,提供 on,off,fire 方法

1
2
3
4
5
fabric.Observable = {
fire: fire,
on: on,
off: off,
};

src/mixins/collection.mixin.js

定义了集合类型 Collection,封装数组,提供了简易操作方法。

src/mixins/shared_methods.mixin.js

一些操作 Object 的公共方法

src/util/misc.js

定义了一些 fabric.util 的方法,比如数学计算的 sin cos,角度与弧度的互转,旋转点,旋转矩阵、计算边框、逆变变换,单位计算,获取 SVG 属性,获取类型的命名空间,加载图片,合并 SVG 对象,绘制虚线,创建 canvas 对象,创建 img 对象,上传 dataURL,矩阵变换,计算旋转矩阵,重置或保存对象 transform 等。

src/util/named_accessors.mixin.js

提供定义类型访问器的方法, getXXX 和 setXXX

src/util/path.js

提供路径 Path 相关的方法,线段转贝塞尔,计算向量角度,获取弧线边框,计算路径长度

src/util/lang_array.js

给数组添加一些扩展方法

src/util/lang_class.js

提供 fabric.util.createClass 方法

src/util/lang_object.js

提供 fabric.util.object.extend(对象复制方法) 和 fabric.util.object.clone 方法,并且将 fabric.Observable 的字段完全复制到 fabric.util

src/util/lang_string.js

提供一些 string 相关的方法,比如大小写、XML 转义等

src/util/dom_event.js

定义触摸事件

src/util/dom_style.js

提供 fabric.util.setStyle 方法,为元素添加样式

src/util/dom_misc.js

提供一些 DOM 操作的方法,比如查询元素,添加类名,获取元素 offset,设置元素是否可以被点击等

src/util/dom_request.js

封装 XMLHttpRequest 请求

src/util/log.js

默认使用 console.log,fabric.log = console.log;

src/util/animate.js

提供动画相关方法

src/util/animate_color.js

颜色动画

src/util/anim_ease.js

各种缓动方法

src/parser.js

SVG 的解析,加载等操作

src/elements_parser.js

仍然是与 SVG 有关的元素操作

src/point.class.js

定义 fabric.Point 类型

src/intersection.class.js

定义 fabric.Intersection 类型,判断线段与多边形的相交关系

src/color.class.js

定义 fabric.Color 类型,支持从 Hex、Rgb、Hsl 等多种方式转为颜色,并能输出各种格式的颜色,内置多种带名称的颜色 fabric.Color.colorNameMap,支持各种颜色格式互转

选择框相关

src/controls.actions.js

定义选择框的各种操作

src/controls.render.js

绘制选择框上的操作圆点、方块

src/controls.class.js

定义选择框按钮,即 fabric.Control 类型,提供了多种 handler,包含鼠标操作,鼠标样式等。

在 src/mixins/object_interactivity.mixin.js 中的 drawControls 方法将真正绘制选择框,8 个方向的控制点。

src/gradient.class.js

定义渐变色,即 fabric.Gradient 类型,支持 SVG 中的渐变色

src/pattern.class.js

定义纹理,即 fabric.Pattern 类型,在 http://fabricjs.com/patterns 可以看到效果。

src/shadow.class.js

定义阴影,即 fabric.Shadow 类型,在 http://fabricjs.com/shadows 可以看到效果。

src/static_canvas.class.js

定义 fabric.StaticCanvas 类型,在 http://fabricjs.com/static_canvas 可以看到效果。
可以将 Canvas 数据转为 SVG、JSON,是画板的基础部分,只包含元素的呈现,不包含选择框和鼠标操作等,Canvas 类型依赖于 Static_Canvas。

src/brushes/base_brush.class.js

画刷基类

src/brushes/pencil_brush.class.js

笔画刷,画线条

src/brushes/circle_brush.class.js

点画刷

src/brushes/spray_brush.class.js

src/brushes/pattern_brush.class.js

纹理画刷

src/canvas.class.js

画板类,定义了选择框的颜色、是否虚线框等样式,定义了各种状态下的鼠标样式。定义了层级操作、旋转中心、鼠标拖动操作,定义了缓存 Canvas,定义清除对象的操作。

drawControls 绘制选择框

src/mixins/canvas_events.mixin.js

在 Canvas 上定义了各种鼠标事件,单击、双击、滚动、拖拽、缩放、右键、鼠标移入移出等

src/mixins/canvas_grouping.mixin.js

处理画板 Canvas 上的元素组合

src/mixins/canvas_dataurl_exporter.mixin.js

在 Static_Canvas 上定义了 toDataURL 和 toCanvasElement 方法

src/mixins/canvas_serialization.mixin.js

在 StaticCanvas 上定义了序列化,loadFromJSON 从 JSON 转为画板,还支持复制画板内容。

src/mixins/canvas_gestures.mixin.js

定义了触摸操作,点击、长按、旋转等。

src/shapes/object.class.js

定义了形状类型的基类,定义了多种基础属性,定义了基础鼠标操作响应,实现 toObject ,toJSON,toCanvasElement 方法。

src/mixins/object_origin.mixin.js

根据指定点的一些位移操作

src/mixins/object_geometry.mixin.js

计算对象矩阵、重合区域等

src/mixins/object_stacking.mixin.js

层级操作,向上向下置顶置底

src/mixins/object.svg_export.js

定义 toSVG 方法,实现了许多 SVG 的解析方法

src/mixins/stateful.mixin.js

对象状态的保存与状态变更

src/mixins/object_interactivity.mixin.js

绘制选择区域、选择框、控制点

src/mixins/animation.mixin.js

给 Object 扩展了 animate 方法

src/mixins/object_straightening.mixin.js

摆正对象位置,比如将其从当前角度旋转到0、90、180、270等之一,取决于哪个更接近

形状类型

src/shapes/line.class.js

线段

src/shapes/circle.class.js

圆形

src/shapes/triangle.class.js

三角形

src/shapes/ellipse.class.js

椭圆形

src/shapes/rect.class.js

矩形

src/shapes/polyline.class.js

折线

src/shapes/polygon.class.js

多边形

src/shapes/path.class.js

路径,包含路径标记语法的解析

src/shapes/group.class.js

组合元素,包含组合元素转对象、转 SVG 的各种定义

src/shapes/active_selection.class.js

元素组合选中

src/shapes/image.class.js

图片类型,包含应用滤镜

图片滤镜

通过 WebGL 应用图片滤镜效果

GLSL 语法

http://fabricjs.com/image-filters

文本类型

src/shapes/text.class.js

文本,包含将文本分行,计算文字宽高

src/mixins/text_style.mixin.js

文本样式相关,包含文本选中区域的样式

src/shapes/itext.class.js

IText,继承自 Text,支持各种快捷键操作,上下左右、复制粘贴

src/mixins/itext_behavior.mixin.js

文本的选择,文字输入与删除,编辑状态

src/mixins/itext_click_behavior.mixin.js

文本相应双击、三击操作

src/mixins/itext_key_behavior.mixin.js

键盘快捷键,光标控制、输入、复制粘贴

src/mixins/itext.svg_export.js

导出为 SVG

src/shapes/textbox.class.js

文本框,继承自 IText,高度自适应,不可调整

src/mixins/default_controls.js

定义了选择框上的默认点,以及 Text 类型的特殊处理

fabric 文档

react-router-dom 和 react-router 不能混用

最近在项目中又遇到问题,线上环境出现这样的 Bug,但是本地却没有错误。而且之前的版本也没错。

其实本地没错是因为有一些包的缓存,删除 node_modules 之后重新安装,也会有问题。

出现这个错误的原因是 react-router-dom 和 react-router 发生了混用。这个版本出错是因为使用了 react-router-dom 里面的 NavLink 组件,如下:

1
import { NavLink } from 'react-router-dom';

但是 withRouter 和 Router 却来自 react-router,这是一个大坑!

1
2
3
4
5
// index.js
import { Router } from 'react-router';

// index.tsx
import { Switch, Route, withRouter } from 'react-router';

改成统一使用 react-router-dom 就行了。

1
2
3
4
5
// index.js
import { Router } from 'react-router-dom';

// index.tsx
import { Switch, Route, withRouter } from 'react-router-dom';

快速删除 node_modules 的方法

全局安装rimraf:

1
npm install rimraf -g

在项目根目录执行:

1
rimraf node_modules

录屏软件推荐(OBS)

Open Broadcaster Software | OBS

生成的视频很小,支持视频剪辑和音频编辑。

  • 1、可以添加视频来源,录屏就是显示器,若有多个显示器也可以切换
  • 2、开始录制,结束录制后会自动生成到电脑视频目录,默认 MKV 格式,能录制电脑声音和麦克风声音
  • 3、可以将 MKV 格式转为 MP4 等其他格式
  • 4、可以设置快捷键,比如 F7 开始录制,F8 停止录制
  • 5、可以录制过程中切换场景和来源,视频会自动修改

ReactNative入门

参考搭建开发环境,其中有些步骤可能需要改动。

Windows 下搭建 ReactNative 开发环境:

安装 Node

不需要安装 Python 2

安装 react-native-cli 命令行工具

1
npm install -g yarn react-native-cli

安装 Android Studio

需要安装 Android Studio 3.4 以上的版本

配置 Android 环境变量

添加 ANDROID_HOME,值为 C:\Users\fuma\AppData\Local\Android\Sdk,修改 PATH,添加两个路径:

  • C:\Users\fuma\AppData\Local\Android\Sdk\platform-tools
  • C:\Users\fuma\AppData\Local\Android\Sdk\tools

初始化项目

1
react-native init rnDemo

启动模拟器

点击右上角的模拟器图标,

打开的列表中应该会有一个默认的模拟器,双击启动即可。

启动项目

进入项目中,允许命令

1
npm run android

注意会启动这样一个窗口,绿色进度条到 100%,就代表加载成功。

然后就可以看到 React Native 应用在模拟器中的效果。

油猴脚本开发

Tampermonkey

油猴脚本模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ==UserScript==
// @name New Userscript
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match http://*/*
// @grant none
// ==/UserScript==

(function() {
'use strict';

// Your code here...
})();

GM_xxx 方法在使用前,需要先在顶部标明,例如 // @grant GM_openInTab ,这样就可以使用 GM_openInTab 方法来打开新页面

支持的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@name
@namespace
@version
@author
@description
@homepage, @homepageURL, @website and @source
@icon, @iconURL and @defaulticon
@icon64 and @icon64URL
@updateURL
@downloadURL
@supportURL
@include
@match
@exclude
@require
@resource
@connect
@run-at
@grant
@noframes
@unwrap
@nocompat

其他接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
unsafeWindow
Subresource Integrity
GM_addStyle(css)
GM_deleteValue(name)
GM_listValues()
GM_addValueChangeListener(name, function(name, old_value, new_value, remote) {})
GM_removeValueChangeListener(listener_id)
GM_setValue(name, value)
GM_getValue(name, defaultValue)
GM_log(message)
GM_getResourceText(name)
GM_getResourceURL(name)
GM_registerMenuCommand(name, fn, accessKey)
GM_unregisterMenuCommand(menuCmdId)
GM_openInTab(url, options), GM_openInTab(url, loadInBackground)
GM_xmlhttpRequest(details)
GM_download(details), GM_download(url, name)
GM_getTab(callback)
GM_saveTab(tab)
GM_getTabs(callback)
GM_notification(details, ondone), GM_notification(text, title, image, onclick)
GM_setClipboard(data, info)
GM_info
<><![CDATA[your_text_here]]></>

其他注意点

  • 油猴 GM_download 方法无法下载 blob 资源转化的 URL

VSCode插件

起点

现在最常用的开发工具就是 VSCode 了,插件可以丰富 VSCode 的功能,提升写代码写文章的效率。

你或许不需要自己开发一个插件,因为插件市场有足够多的插件,查看插件市场

不过,别人写的插件,可能并不能刚好满足你的要求,要么多了、要么少了。自己写插件,可以尝试融合多个插件为一体,可以将大插件中的某个小功能抽取出来。

首先,学习 VSCode 插件开发可以先看官方文档,然后看看 Github 上的开源项目。
在 VSCode 的插件介绍页面中,基本是每个 VSCode 都有一个“存储库”,点击即可前往 Github 仓库。

开发

VSCode 插件可以用 TypeScript 编写,使用官方脚手架 https://github.com/Microsoft/vscode-generator-code 可以生成模板。

VSCode 能识别插件项目,按 F5 就会启动一个新的 VSCode 窗口用于调试。

代码示例

1
2
// 默认方式打开本地文件
vscode.env.openExternal(vscode.Uri.file('F:\\极乐净土.mp4'));

打开 Webview 开发者工具

ctrl+shift+p,输入 Webview 开发,即可看到。

打包、安装

每次更新,都应该提升 version,同一个 version,VSCode 好像会保留缓存。

VSCode 插件可以通过 VSIX 安装,代码可以打包成 VSIX。然后打开插件标签,通过 VSIX 安装插件。

插件推荐

  • Settings Sync
  • GitLens
  • Markdown All in One
  • favorites
  • vscode-icons
  • REST Client
  • Import Cost
  • Prettier
  • Color Info

参考资料

Electron入门

主进程和渲染器进程

https://electronjs.org/docs/tutorial/application-architecture

只有一个主进程,每个页面都是单独的渲染进程。

主进程和渲染进程的通信方式如下:

  • ipcMainipcRenderer 是传统方式
  • remote 提供了一种简单方法,可以在渲染进程调用主线程的全局变量

如何在 React 中引入 ipcRenderer?

需要在 webpack 中设置 target 为 electron-renderer,参考https://stackoverflow.com/questions/39332546/using-electrons-ipcrender-from-inside-a-react-component。

1
target: 'electron-renderer',

ipcMain 与 ipcRenderer

1
2
3
4
5
6
7
8
9
10
11
// react 页面中
import { ipcRenderer } from 'electron';

ipcRenderer.send('send-somemessagee');

// 主线程接收
import { app, BrowserWindow, ipcMain } from 'electron';

ipcMain.on('send-somemessagee', (event, arg) => {
console.log('===send somemessagee====', arg)
})

在 React 组件中使用 remote

1
2
3
4
5
import electron, { ipcRenderer } from 'electron';
const { BrowserWindow } = electron.remote;

let win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('https://github.com')

VSCode 中调试主线程

针对 https://github.com/electron-react-boilerplate/electron-react-boilerplate。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "调试主线程",
"cwd": "${workspaceRoot}",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"env": {"NODE_ENV":"development"},
"args" : ["-r", "@babel/register", "./app/main.dev.js"]
}
]
}

打包源码为 asar

https://electronjs.org/docs/tutorial/application-packaging