├── README.md
├── app.js
├── app.json
├── app.wxss
├── pages
├── index
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
├── pull
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
├── pullQRCode
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
├── push
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
└── video
│ ├── index.js
│ ├── index.json
│ ├── index.wxml
│ └── index.wxss
├── project.config.json
├── res
└── img
│ ├── adapt.png
│ ├── anyway.png
│ ├── beauty.png
│ ├── bg.png
│ ├── camera.png
│ ├── df_hd.png
│ ├── df_sd.png
│ ├── df_ssd.png
│ ├── err_icon.png
│ ├── file.png
│ ├── future_icon.png
│ ├── logo.png
│ ├── mute.png
│ ├── pause.png
│ ├── play.png
│ ├── pull_icon.png
│ ├── push_icon.png
│ ├── qrcode.png
│ ├── scan.png
│ ├── share.png
│ ├── turnout.png
│ ├── vod_icon.png
│ └── white.png
└── utils
└── weapp.qrcode.esm.js
/README.md:
--------------------------------------------------------------------------------
1 | # 介绍
2 | 网易云信视频直播DEMO微信小程序版本(以下简称视频小程序DEMO)是一套结合网易云信直播点播能力,基于微信小程序平台开发的视频直播点播小程序。此DEMO用于为开发者演示直播推流、直播播放、点播播放等能力。由于微信小程序为第三方平台,使用过程中可能存在某些兼容性问题,建议您综合评估风险。我们推荐您使用云信原生移动端SDK进行开发。
3 |
4 | 视频小程序DEMO可扫描以下二维码体验:
5 |
6 | 
7 |
8 |
9 | 视频小程序DEMO主要功能列表如下:
10 |
11 | |总览||
12 | | :------- | :----- |
13 | |功能 |描述|
14 | |功能入口 |直播推流,直播播放,点播播放,更多功能|
15 | |权限要求 |摄像头、录音、用户信息|
16 |
17 |
18 | |直播推流 ||
19 | | :------- | :----- |
20 | |功能 |描述|
21 | |获取并填充推流地址 |点击添加,获取一个推流地址进行推流|
22 | |输入推流地址 |支持文本框粘贴输入推流URL|
23 | |二维码扫描获取推流地址 |支持二维码扫描获取推流地址|
24 | |开始/停止推流 ||
25 | |美颜 ||
26 | |清晰度选择 ||
27 | |摄像头切换 ||
28 | |摄像头开启/关闭 ||
29 | |声音开启/关闭 ||
30 | |扫码获取拉流地址 ||
31 |
32 |
33 | |直播播放 ||
34 | | :------- | :----- |
35 | |功能 |描述|
36 | |粘贴拉流地址 |支持rtmp flv协议拉流,提供文本框供粘贴|
37 | |扫码填充拉流地址 |支持通过扫描二维码填充拉流地址|
38 | |开始/停止播放 ||
39 | |支持系统音量调整 |支持通过改变系统音量调节播放声音|
40 | |横竖屏切换 ||
41 |
42 |
43 | |点播播放 ||
44 | | :------- | :----- |
45 | |粘贴播放地址 |支持mp4,flv,HLS格式播放,提供文本框供粘贴URL|
46 | |填充一个默认播放地址 |点击添加,填充一个默认播放地址,播放准备好的测试视频|
47 | |扫码填充播放地址 |支持通过扫描二维码填充播放地址|
48 | |开始/暂停播放 ||
49 | |点播拖拽进度条 |手势拖拽seek到指定位置|
50 | |支持系统音量调整|支持通过改变系统音量调节播放声音|
51 | |横竖屏切换 ||
52 |
53 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | App({
2 | onLaunch: function () {},
3 | globalData: {}
4 | })
5 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages":[
3 | "pages/index/index",
4 | "pages/push/index",
5 | "pages/pullQRCode/index",
6 | "pages/pull/index",
7 | "pages/video/index"
8 | ],
9 | "window":{
10 | "navigationBarTitleText": "云信视频DEMO",
11 | "navigationBarBackgroundColor": "#000",
12 | "navigationBarTextStyle": "white",
13 | "backgroundTextStyle": "light",
14 | "backgroundColor": "#f4f4f4",
15 | "enablePullDownRefresh": false,
16 | "onReachBottomDistance": 0
17 | },
18 | "networkTimeout": {
19 | "request": 10000,
20 | "connectSocket": 10000,
21 | "uploadFile": 10000,
22 | "downloadFile": 10000
23 | },
24 | "debug": false
25 | }
26 |
--------------------------------------------------------------------------------
/app.wxss:
--------------------------------------------------------------------------------
1 | body, page {
2 | height: 100%;
3 | }
4 | .f-o-v {
5 | overflow: visible;
6 | }
7 | .f-db {
8 | display: block;
9 | }
10 | .f-disable {
11 | opacity: 0.4;
12 | }
13 | .f-c-red {
14 | color: red;
15 | }
16 | .coverImg {
17 | position: absolute;
18 | }
19 | .page-box {
20 | width: 100%;
21 | height: 100%;
22 | background: #000;
23 | color: #fff;
24 | }
25 | .ipt-box {
26 | position: relative;
27 | width: 100%;
28 | height: 150rpx;
29 | background: rgba(0, 0, 0, 0.4);
30 | }
31 | .ipt {
32 | display: inline-block;
33 | margin: 18rpx 0 18rpx 20rpx;
34 | width: 558rpx;
35 | height: 64rpx;
36 | color: #ffffff;
37 | font-size: 28rpx;
38 | background-color: rgba(0, 0, 0, 0.3);
39 | border-radius: 8rpx;
40 | opacity: 0.6;
41 | }
42 | .placeholder {
43 | opacity: 0.6;
44 | font-size:28rpx;
45 | color:#fff;
46 | }
47 | .btn {
48 | position: absolute;
49 | top: 18rpx;
50 | display: inline-block;
51 | width: 56rpx;
52 | height: 56rpx;
53 | }
54 | .btn-1 {
55 | right: 95rpx;
56 | }
57 | .btn-2 {
58 | right: 20rpx;
59 | }
60 | .desc {
61 | display: block;
62 | padding: 0 0 0 20rpx;
63 | font-size: 24rpx;
64 | opacity: 0.6;
65 | line-height: 24rpx;
66 | }
67 | .video-box {
68 | width: 100%;
69 | }
70 | .video {
71 | width: 100%;
72 | height: 100%;
73 | }
74 | .action {
75 | position: absolute;
76 | right: 0;
77 | bottom: 0;
78 | left: 0;
79 | width: 100%;
80 | height: 300rpx;
81 | background: rgba(0, 0, 0, 0.4);
82 | text-align: center;
83 | font-size: 22rpx;
84 | }
85 |
--------------------------------------------------------------------------------
/pages/index/index.js:
--------------------------------------------------------------------------------
1 | Page({
2 | onShareAppMessage: function () {
3 | return {
4 | title: '云信视频DEMO',
5 | path: 'pages/index/index',
6 | imageUrl: '/res/img/share.png'
7 | }
8 | }
9 | })
10 |
11 |
--------------------------------------------------------------------------------
/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "云信视频DEMO",
3 | "disableScroll": true
4 | }
--------------------------------------------------------------------------------
/pages/index/index.wxml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .flex-wrp {
2 | display: flex;
3 | width: 100%;
4 | height: 680rpx;
5 | flex-flow: row wrap;
6 | }
7 | .flex-item {
8 | width: 50%;
9 | height: 50%;
10 | }
11 | .btn-item {
12 | margin-top: 40rpx;
13 | margin-bottom: 40rpx;
14 | padding-bottom: 30rpx;
15 | background: #fff;
16 | border: 0 solid #e3e3e3;
17 | box-shadow: 0 2px 8px 0 rgba(218, 218, 218, 0.5);
18 | border-radius: 8rpx;
19 | text-align: center;
20 | overflow: hidden;
21 | }
22 | .btn-item-1, .btn-item-3 {
23 | margin-right: 20rpx;
24 | margin-left: 40rpx;
25 | }
26 | .btn-item-2, .btn-item-4 {
27 | margin-right: 40rpx;
28 | margin-left: 20rpx;
29 | }
30 | .item-img {
31 | display: block;
32 | margin: 40rpx auto 32rpx;
33 | width: 120rpx;
34 | height: 120rpx;
35 | }
36 | .item-txt {
37 | font-size: 24rpx;
38 | line-height: 34rpx;
39 | color: #333;
40 | }
41 | .item-txt-des {
42 | color: #666;
43 | }
44 | .intro {
45 | position: absolute;
46 | right: 0;
47 | bottom: 0;
48 | left: 0;
49 | height: 136rpx;
50 | text-align: center;
51 | }
52 | .intro-logo {
53 | width: 250rpx;
54 | height: 42rpx;
55 | }
56 | .intro-desc {
57 | font-size: 24rpx;
58 | color: #999;
59 | }
--------------------------------------------------------------------------------
/pages/pull/index.js:
--------------------------------------------------------------------------------
1 | const app = getApp()
2 | Page({
3 | data: {
4 | pulling: false,
5 | livePlayerContext: {},
6 | src: '', // 音视频地址。目前仅支持 flv, rtmp 格式
7 | mode: 'live', // live(直播),RTC(实时通话)
8 | autoplay: false,
9 | muted: false,
10 | orientation: 'vertical', // 画面方向,可选值有 vertical,horizontal
11 | objectFit: 'contain', // 填充模式,可选值有 contain,fillCrop
12 | },
13 | // 生命周期函数--监听页面初次渲染完成
14 | onReady: function () {
15 | this.createContext();
16 | wx.setKeepScreenOn({
17 | keepScreenOn: true,
18 | })
19 | },
20 | // 生命周期函数--监听页面显示
21 | onShow: function () {
22 | wx.setKeepScreenOn({
23 | keepScreenOn: true
24 | })
25 | },
26 | // 生命周期函数--监听页面卸载
27 | onUnload: function () {
28 | this.stop()
29 | wx.setKeepScreenOn({
30 | keepScreenOn: false,
31 | })
32 | },
33 | onShareAppMessage: function () {
34 | return {
35 | title: '直播播放',
36 | path: 'pages/pull/index',
37 | imageUrl: '/res/img/share.png'
38 | }
39 | },
40 | createContext: function () {
41 | this.setData({
42 | livePlayerContext: wx.createLivePlayerContext('livePlayer')
43 | })
44 | },
45 | inputSrc: function (e) {
46 | this.checkUrl(e.detail.value)
47 | this.setData({
48 | src: e.detail.value
49 | })
50 | this.createContext();
51 | },
52 | scanQRCode: function () {
53 | var that = this
54 | that.data.livePlayerContext.stop()
55 | wx.scanCode({
56 | success: function(res) {
57 | console.log(res)
58 | if (res.scanType === 'QR_CODE' && res.errMsg === 'scanCode:ok') {
59 | that.createContext();
60 | that.setData({
61 | src: res.result
62 | })
63 | } else {
64 | wx.showToast({
65 | image: '/res/img/err_icon.png',
66 | title: '获取地址失败'
67 | })
68 | }
69 | }
70 | })
71 | },
72 | checkUrl: function (str) {
73 | if (/^(rtmp|RTMP):\/\/[\w\/\.?&_=-]+$/.test(str) || /^(http|HTTP|https|HTTPS):\/\/[\w\/\.&_=-]+\.flv[\w\/\.=?&_-]*$/.test(str)) {
74 | return true
75 | }
76 | wx.showToast({
77 | image: '/res/img/err_icon.png',
78 | title: '不可用的地址'
79 | })
80 | return false
81 | },
82 | play: function () {
83 | if (!this.checkUrl(this.data.src)) {
84 | return
85 | }
86 | var that = this
87 | this.data.livePlayerContext.play({
88 | success: function (res) {
89 | that.setData({
90 | pulling: true
91 | })
92 | },
93 | fail: function (res) {
94 | wx.showToast({
95 | image: '/res/img/err_icon.png',
96 | title: '播放失败'
97 | })
98 | }
99 | })
100 | that.setData({
101 | pulling: true
102 | })
103 | },
104 | stop: function () {
105 | var that = this
106 | this.data.livePlayerContext.stop({
107 | success: function (res) {
108 | that.setData({
109 | pulling: false
110 | })
111 | },
112 | fail: function (res) {
113 | wx.showToast({
114 | image: '/res/img/err_icon.png',
115 | title: '操作失败'
116 | })
117 | }
118 | })
119 | },
120 | pause: function () {
121 | var that = this
122 | this.data.livePlayerContext.pause({
123 | success: function (res) {
124 | that.setData({
125 | pulling: false
126 | })
127 | },
128 | fail: function (res) {
129 | wx.showToast({
130 | image: '/res/img/err_icon.png',
131 | title: '操作失败'
132 | })
133 | }
134 | })
135 | },
136 | resume: function () {
137 | var that = this
138 | this.data.livePlayerContext.resume({
139 | success: function (res) {
140 | that.setData({
141 | pulling: true
142 | })
143 | },
144 | fail: function (res) {
145 | wx.showToast({
146 | image: '/res/img/err_icon.png',
147 | title: '操作失败'
148 | })
149 | }
150 | })
151 | },
152 | switchMuted() {
153 | this.setData({
154 | muted: !this.data.muted
155 | })
156 | },
157 | switchOrientation() {
158 | this.setData({
159 | orientation: this.data.orientation === 'vertical' ? 'horizontal' : 'vertical'
160 | })
161 | },
162 | swicthObjectFit() {
163 | this.setData({
164 | objectFit: this.data.objectFit === 'contain' ? 'fillCrop' : 'contain'
165 | })
166 | },
167 | statechange(e) {
168 | if ([2006, 3005].indexOf(+e.detail.code) !== -1) {
169 | this.stop()
170 | setTimeout(function () {
171 | wx.showToast({
172 | image: '/res/img/err_icon.png',
173 | title: '主播停止推流' + e.detail.code
174 | })
175 | }, 800)
176 | }
177 | if ([-2301, 3001, 3002, 3003].indexOf(+e.detail.code) !== -1) {
178 | this.stop()
179 | setTimeout(function () {
180 | wx.showToast({
181 | image: '/res/img/err_icon.png',
182 | title: '播放失败' + e.detail.code
183 | })
184 | }, 800)
185 | }
186 | },
187 | fullscreenchange(e) {
188 | console.log('fullscreenchange: ', e.detail)
189 | },
190 | netstatus(e) {
191 | console.log('live-player info:', e.detail.info)
192 | },
193 | error(e) {
194 | console.error('live-player error:', e.detail.errMsg)
195 | wx.showToast({
196 | image: '/res/img/err_icon.png',
197 | title: '播放失败'
198 | })
199 | }
200 | })
201 |
--------------------------------------------------------------------------------
/pages/pull/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "直播播放",
3 | "disableScroll": true
4 | }
5 |
--------------------------------------------------------------------------------
/pages/pull/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 | 支持RTMP、Http-flv协议的直播播放。
13 |
14 |
15 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 静音
48 |
49 |
50 |
51 | {{objectFit === 'contain' ? '适应' : '填充'}}
52 |
53 |
54 |
55 | {{orientation === 'vertical' ? '竖屏' : '横屏'}}
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/pages/pull/index.wxss:
--------------------------------------------------------------------------------
1 | .ipt {
2 | width: 640rpx;
3 | }
4 | .action {
5 | height: 174rpx;
6 | }
7 | .action-play {
8 | position: absolute;
9 | left: 0;
10 | display: inline-block;
11 | width: 294rpx;
12 | }
13 | .action-play-btn {
14 | margin: 20rpx 0 0 44rpx;
15 | width: 134rpx;
16 | }
17 | .action-play-img {
18 | width: 134rpx;
19 | height: 134rpx;
20 | }
21 | .action-param {
22 | position: absolute;
23 | top: 0;
24 | right: 0;
25 | display: inline-block;
26 | padding: 0;
27 | width: 450rpx;
28 | }
29 | .action-param-item {
30 | display: inline-block;
31 | margin: 35rpx 40rpx 0 0;
32 | padding: 0;
33 | width: 112rpx;
34 | height: 110rpx;
35 | background: rgba(0, 0, 0, 0.3);
36 | border-radius: 8rpx;
37 | }
38 | .action-param-img {
39 | display: block;
40 | margin: 20rpx auto 12rpx;
41 | width: 44rpx;
42 | height: 44rpx;
43 | }
--------------------------------------------------------------------------------
/pages/pullQRCode/index.js:
--------------------------------------------------------------------------------
1 | import drawQrcode from '../../utils/weapp.qrcode.esm.js'
2 |
3 | Page({
4 | data: {
5 | hlsUrl: '',
6 | rtmpUrl: '',
7 | },
8 | onLoad: function (option) {
9 | this.getUrl(option)
10 | },
11 | onShareAppMessage: function () {
12 | return {
13 | title: '直播播放',
14 | path: 'pages/pullQRCode/index?hls=' + encodeURIComponent(this.data.hlsUrl) + '&rtmp=' + encodeURIComponent(this.data.rtmpUrl),
15 | imageUrl: '/res/img/share.png'
16 | }
17 | },
18 | getUrl: function (option) {
19 | var that = this
20 | if (!option) {
21 | wx.showToast({
22 | image: '/res/img/err_icon.png',
23 | title: '获取地址失败'
24 | })
25 | return
26 | }
27 | if (option.rtmp) {
28 | that.setData({
29 | rtmpUrl: decodeURIComponent(option.rtmp)
30 | })
31 | drawQrcode({
32 | width: 160,
33 | height: 160,
34 | canvasId: 'rtmpUrl',
35 | text: this.data.rtmpUrl
36 | })
37 | } else {
38 | wx.showToast({
39 | image: '/res/img/err_icon.png',
40 | title: '获取地址失败'
41 | })
42 | }
43 | if (option.hls) {
44 | that.setData({
45 | hlsUrl: decodeURIComponent(option.hls)
46 | })
47 | drawQrcode({
48 | width: 160,
49 | height: 160,
50 | canvasId: 'hlsUrl',
51 | text: this.data.hlsUrl
52 | })
53 | } else {
54 | wx.showToast({
55 | image: '/res/img/err_icon.png',
56 | title: '获取地址失败'
57 | })
58 | }
59 | },
60 | })
61 |
--------------------------------------------------------------------------------
/pages/pullQRCode/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "扫码播放",
3 | "disableScroll": true
4 | }
--------------------------------------------------------------------------------
/pages/pullQRCode/index.wxml:
--------------------------------------------------------------------------------
1 |
2 | RTMP播放地址
3 | 推荐小程序扫码播放
4 |
5 | HLS播放地址
6 | 推荐微信扫一扫播放
7 |
8 |
9 |
--------------------------------------------------------------------------------
/pages/pullQRCode/index.wxss:
--------------------------------------------------------------------------------
1 | .page {
2 | padding-top: 20rpx;
3 | font-size: 24rpx;
4 | color: #333;
5 | text-align: center;
6 | line-height: 40rpx;
7 | }
8 | .tt {
9 | margin-top: 60rpx;
10 | }
11 | .des {
12 | margin-bottom: 20rpx;
13 | }
14 | .qrcode {
15 | margin: 0 auto;
16 | width: 160px;
17 | height: 160px;
18 | }
--------------------------------------------------------------------------------
/pages/push/index.js:
--------------------------------------------------------------------------------
1 | const app = getApp()
2 | Page({
3 | data: {
4 | pushing: false,
5 | isServerAutoUrl: false, // 是否是来自服务器的链接
6 | showModeMenu: false,
7 | rearCamera: false, // 后置摄像头
8 | livePusherContext: {},
9 | url: '', // 目前仅支持 flv, rtmp 格式
10 | enableCamera: true, // 摄像头开关
11 | beauty: 0,
12 | whiteness: 0,
13 | mode: 'HD', //清晰度 SD(标清), HD(高清), FHD(超清), RTC(实时通话)
14 | orientation: 'vertical', // 竖屏 vertical,horizontal
15 | muted: false,
16 | hlsUrl: '',
17 | rtmpUrl: ''
18 | },
19 | // 生命周期函数--监听页面初次渲染完成
20 | onReady: function () {
21 | this.getUserLimits()
22 | this.createContext();
23 | wx.setKeepScreenOn({
24 | keepScreenOn: true,
25 | })
26 | },
27 | // 生命周期函数--监听页面显示
28 | onShow: function () {
29 | var that = this
30 | if (that.data.pushing) {
31 | that.data.livePusherContext.stop()
32 | setTimeout(function () {
33 | that.data.livePusherContext.start()
34 | }, 100)
35 | }
36 | wx.setKeepScreenOn({
37 | keepScreenOn: true
38 | })
39 | },
40 | // 生命周期函数--监听页面卸载
41 | onUnload: function () {
42 | this.stop()
43 | wx.setKeepScreenOn({
44 | keepScreenOn: false,
45 | })
46 | },
47 | onShareAppMessage: function () {
48 | return {
49 | title: '直播推流',
50 | path: 'pages/push/index',
51 | imageUrl: '/res/img/share.png'
52 | }
53 | },
54 | createContext: function () {
55 | this.setData({
56 | livePusherContext: wx.createLivePusherContext('videoPush')
57 | })
58 | },
59 | // 获取用户授权
60 | getUserLimits: function () {
61 | wx.getSetting({
62 | success: function (res) {
63 | if (!res.authSetting['scope.camera']) {
64 | wx.authorize({
65 | scope: 'scope.camera',
66 | success: function () {},
67 | fail: function () {
68 | wx.showToast({
69 | image: '/res/img/err_icon.png',
70 | title: '相机授权失败'
71 | })
72 | }
73 | })
74 | }
75 | if (!res.authSetting['scope.record']) {
76 | wx.authorize({
77 | scope: 'scope.record',
78 | success: function () {},
79 | fail: function () {
80 | wx.showToast({
81 | image: '/res/img/err_icon.png',
82 | title: '录音授权失败'
83 | })
84 | }
85 | })
86 | }
87 | },
88 | fail: function () {
89 | wx.showToast({
90 | image: '/res/img/err_icon.png',
91 | title: '授权失败'
92 | })
93 | }
94 | })
95 | },
96 | pullQRCode: function () {
97 | if (!this.data.isServerAutoUrl) {
98 | wx.showToast({
99 | image: '/res/img/err_icon.png',
100 | title: '不支持扫码'
101 | })
102 | return
103 | }
104 | var that = this
105 | if (!that.recordJumpToQRCode) {
106 | that.recordJumpToQRCode = setTimeout(function () {
107 | that.recordJumpToQRCode = ''
108 | }, 1000)
109 | wx.navigateTo({
110 | url: '/pages/pullQRCode/index?hls=' + encodeURIComponent(that.data.hlsUrl) + '&rtmp=' + encodeURIComponent(that.data.rtmpUrl)
111 | })
112 | }
113 | },
114 | inputPushUrl: function (e) {
115 | this.checkUrl(e.detail.value)
116 | if (e.detail.value !== this.data.url) {
117 | this.setData({
118 | url: e.detail.value,
119 | isServerAutoUrl: false
120 | })
121 | }
122 | },
123 | getPushUrl: function () {
124 | var that = this
125 | wx.showLoading({ title: '请求中', mask: true })
126 | wx.request({
127 | method: 'POST',
128 | url: 'https://app.netease.im/appdemo/weApp/popLive',
129 | success: function (res) {
130 | wx.hideLoading()
131 | if (res.data && res.data.code === 200) {
132 | that.setData({
133 | url: res.data.data.pushUrl,
134 | hlsUrl: res.data.data.hlsPullUrl,
135 | rtmpUrl: res.data.data.rtmpPullUrl,
136 | isServerAutoUrl: true
137 | })
138 | wx.showToast({
139 | title: '获取地址成功'
140 | })
141 | } else {
142 | wx.showToast({
143 | image: '/res/img/err_icon.png',
144 | title: '无可用的地址'
145 | })
146 | }
147 | },
148 | fail: function () {
149 | wx.hideLoading()
150 | wx.showToast({
151 | image: '/res/img/err_icon.png',
152 | title: '获取地址失败'
153 | })
154 | }
155 | })
156 | },
157 | // 扫描完成填充地址栏
158 | scanQRCode: function () {
159 | var that = this
160 | that.stop()
161 | wx.scanCode({
162 | success: function (res) {
163 | console.log(res)
164 | if (res.scanType === 'QR_CODE' && res.errMsg === 'scanCode:ok') {
165 | that.setData({
166 | url: res.result,
167 | isServerAutoUrl: false
168 | })
169 | } else {
170 | wx.showToast({
171 | image: '/res/img/err_icon.png',
172 | title: '获取地址失败'
173 | })
174 | }
175 | }
176 | })
177 | },
178 | checkUrl: function (str) {
179 | if (/^(rtmp|RTMP):\/\/[\w\/\.=?&_=-]+$/.test(str)) {
180 | return true
181 | }
182 | wx.showToast({
183 | image: '/res/img/err_icon.png',
184 | title: '地址不可用'
185 | })
186 | return false
187 | },
188 | // 播放推流
189 | play: function () {
190 | if (!this.checkUrl(this.data.url)) {
191 | return
192 | }
193 | var that = this
194 | this.data.livePusherContext.start({
195 | success: function (res) {
196 | that.setData({
197 | pushing: true
198 | })
199 | },
200 | fail: function (res) {
201 | wx.showToast({
202 | image: '/res/img/err_icon.png',
203 | title: '推流失败'
204 | })
205 | }
206 | })
207 | },
208 | // 停止推流
209 | stop: function () {
210 | var that = this
211 | that.setData({
212 | pushing: false,
213 | url: '',
214 | isServerAutoUrl: false,
215 | enableCamera: true
216 | })
217 | that.data.livePusherContext.stop()
218 | },
219 | // 暂停推流
220 | pause: function () {
221 | var that = this
222 | this.data.livePusherContext.pause({
223 | success: function (res) {
224 | that.setData({
225 | pushing: false
226 | })
227 | },
228 | fail: function (res) {
229 | wx.showToast({
230 | image: '/res/img/err_icon.png',
231 | title: '操作失败'
232 | })
233 | }
234 | })
235 | },
236 | // 恢复推流
237 | resume: function () {
238 | var that = this
239 | this.data.livePusherContext.resume({
240 | success: function (res) {
241 | that.setData({
242 | pushing: true
243 | })
244 | },
245 | fail: function (res) {
246 | wx.showToast({
247 | image: '/res/img/err_icon.png',
248 | title: '操作失败'
249 | })
250 | }
251 | })
252 | },
253 | // 切换摄像头
254 | handoverCamera: function () {
255 | var that = this
256 | this.data.livePusherContext.switchCamera({
257 | success: function (res) {
258 | that.setData({
259 | rearCamera: !that.data.rearCamera
260 | })
261 | },
262 | fail: function (res) {
263 | wx.showToast({
264 | image: '/res/img/err_icon.png',
265 | title: '操作失败'
266 | })
267 | }
268 | })
269 | },
270 | switchBeauty: function () {
271 | this.setData({
272 | beauty: this.data.beauty ? 0 : 10
273 | })
274 | },
275 | switchWhiteness: function () {
276 | this.setData({
277 | whiteness: this.data.whiteness ? 0 : 10
278 | })
279 | },
280 | showHideMode: function () {
281 | this.setData({
282 | showModeMenu: !this.data.showModeMenu
283 | })
284 | },
285 | switchMode: function (data) {
286 | this.setData({
287 | mode: data.target.dataset.type,
288 | showModeMenu: false
289 | })
290 | },
291 | switchOrientation: function () {
292 | this.setData({
293 | orientation: this.data.orientation === 'vertical' ? 'horizontal' : 'vertical'
294 | })
295 | },
296 | switchMuted: function () {
297 | this.setData({
298 | muted: !this.data.muted
299 | })
300 | },
301 | switchCamera: function () {
302 | var that = this
303 | that.setData({
304 | enableCamera: !that.data.enableCamera
305 | })
306 | if (that.data.pushing) {
307 | that.data.livePusherContext.stop()
308 | setTimeout(function () {
309 | that.data.livePusherContext.start()
310 | }, 500)
311 | }
312 | },
313 | statechange: function (e) {
314 | if ([-1307, -1308, -1309, -1310, 3001, 3002, 3003, 3004, 3005].indexOf(+e.detail.code) !== -1) {
315 | this.stop()
316 | setTimeout(function () {
317 | wx.showToast({
318 | image: '/res/img/err_icon.png',
319 | title: '推流失败' + e.detail.code
320 | })
321 | }, 500)
322 | }
323 | },
324 | error: function (e) {
325 | console.log('live-pusher errMsg:', e.detail.errMsg, ', errCode:', e.errCode)
326 | this.stop()
327 | setTimeout(function () {
328 | wx.showToast({
329 | image: '/res/img/err_icon.png',
330 | title: '推流失败'
331 | })
332 | }, 800)
333 | }
334 | })
335 |
--------------------------------------------------------------------------------
/pages/push/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "直播推流",
3 | "disableScroll": true
4 | }
5 |
--------------------------------------------------------------------------------
/pages/push/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
15 |
28 |
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 | {{orientation === 'vertical' ? '竖屏' : '横屏'}}
78 |
79 |
80 |
81 | 扫码播放
82 |
83 |
84 |
85 |
86 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/pages/push/index.wxss:
--------------------------------------------------------------------------------
1 | .ipt-box {
2 | height: 100rpx;
3 | }
4 | .action {
5 | height: 280rpx;
6 | }
7 | .action-play {
8 | position: absolute;
9 | top: 0;
10 | bottom: 0;
11 | left: 0;
12 | width: 220rpx;
13 | }
14 | .action-play-img {
15 | display: inline-block;
16 | margin: 52rpx auto 20rpx;
17 | width: 134rpx;
18 | height: 134rpx;
19 | }
20 | .action-param {
21 | position: absolute;
22 | top: 0;
23 | right: 0;
24 | bottom: 0;
25 | left: 220rpx;
26 | }
27 | .action-param-line {
28 | height: 50%;
29 | }
30 | .action-param-line-1 .action-param-item {
31 | margin-top: 20rpx;
32 | }
33 | .action-param-line-2 .action-param-item {
34 | margin-top: 5rpx;
35 | }
36 | .action-param-item {
37 | position: relative;
38 | display: inline-block;
39 | margin: 0 20rpx 0 0;
40 | width: 112rpx;
41 | height: 110rpx;
42 | background: rgba(0, 0, 0, 0.3);
43 | border-radius: 8rpx;
44 | }
45 | .action-param-img {
46 | display: inline-block;
47 | margin: 18rpx auto 12rpx;
48 | width: 44rpx;
49 | height: 44rpx;
50 | }
51 | .mode-menu {
52 | position: absolute;
53 | bottom: 262rpx;
54 | right: 155rpx;
55 | width: 112rpx;
56 | height: 194rpx;
57 | background: rgba(0,0,0,0.4);
58 | border-radius: 8rpx;
59 | text-align: center;
60 | }
61 | .mode-menu-item {
62 | height: 33.2%;
63 | }
64 | .mode-menu-img {
65 | display: inline-block;
66 | margin-top: 10rpx;
67 | width: 44rpx;
68 | height: 44rpx;
69 | }
--------------------------------------------------------------------------------
/pages/video/index.js:
--------------------------------------------------------------------------------
1 | const app = getApp()
2 | Page({
3 | data: {
4 | playing: false,
5 | fullScreen: false,
6 | videoContext: {},
7 | src: '',
8 | playSrc: ''
9 | },
10 | // 生命周期函数--监听页面初次渲染完成
11 | onReady: function () {
12 | wx.setKeepScreenOn({
13 | keepScreenOn: true,
14 | })
15 | },
16 | // 生命周期函数--监听页面显示
17 | onShow: function () {
18 | wx.setKeepScreenOn({
19 | keepScreenOn: true
20 | })
21 | },
22 | // 生命周期函数--监听页面卸载
23 | onUnload: function () {
24 | this.stop()
25 | wx.setKeepScreenOn({
26 | keepScreenOn: false,
27 | })
28 | },
29 | onShareAppMessage: function () {
30 | return {
31 | title: '点播播放',
32 | path: 'pages/video/index',
33 | imageUrl: '/res/img/share.png'
34 | }
35 | },
36 | createContext: function() {
37 | this.setData({
38 | videoContext: wx.createVideoContext('video')
39 | })
40 | },
41 | inputSrc: function (e) {
42 | if (this.checkUrl(e.detail.value)) {
43 | this.setData({
44 | playSrc: e.detail.value
45 | })
46 | this.createContext();
47 | } else {
48 | this.setData({
49 | playSrc: ''
50 | })
51 | }
52 | this.setData({
53 | src: e.detail.value
54 | })
55 | },
56 | tip: function () {
57 | if (this.data.src === '') {
58 | wx.showToast({
59 | icon: 'none',
60 | title: '请获取地址'
61 | })
62 | } else {
63 | wx.showToast({
64 | icon: 'none',
65 | title: '请确认地址'
66 | })
67 | }
68 | },
69 | getSrc: function () {
70 | var that = this
71 | that.stop()
72 | wx.showLoading({ title: '请求中', mask: true })
73 | wx.request({
74 | method: 'POST',
75 | url: 'https://app.netease.im/appdemo/weApp/vodUrl',
76 | success: function (res) {
77 | wx.hideLoading()
78 | if (res.data && res.data.code === 200) {
79 | that.createContext();
80 | that.setData({
81 | playSrc: res.data.data,
82 | src: res.data.data
83 | })
84 | wx.showToast({
85 | title: '获取地址成功'
86 | })
87 | } else {
88 | wx.showToast({
89 | image: '/res/img/err_icon.png',
90 | title: '获取地址失败'
91 | })
92 | }
93 | },
94 | fail: function () {
95 | wx.hideLoading()
96 | wx.showToast({
97 | image: '/res/img/err_icon.png',
98 | title: '获取地址出错'
99 | })
100 | }
101 | })
102 | },
103 | scanQRCode: function () {
104 | var that = this
105 | this.stop()
106 | wx.scanCode({
107 | success: function (res) {
108 | if (res.scanType === 'QR_CODE' && res.errMsg === 'scanCode:ok') {
109 | that.createContext();
110 | that.setData({
111 | src: res.result
112 | })
113 | if (that.checkUrl(res.result)) {
114 | that.setData({
115 | playSrc: res.result
116 | })
117 | } else {
118 | that.setData({
119 | playSrc: ''
120 | })
121 | }
122 | } else {
123 | wx.showToast({
124 | image: '/res/img/err_icon.png',
125 | title: '获取地址失败'
126 | })
127 | }
128 | }
129 | })
130 | },
131 | checkUrl: function (str) {
132 | if (/^(http|HTTP|https|HTTPS):\/\/[\w\/\.=?&_=-]+(\.mp4)[\w\/\.=?&_=-]*$/.test(str)) {
133 | return true
134 | }
135 | wx.showToast({
136 | image: '/res/img/err_icon.png',
137 | title: '不支持的地址'
138 | })
139 | return false
140 | },
141 | play: function () {
142 | if (this.checkUrl(this.data.playSrc)) {
143 | this.setData({
144 | playing: true
145 | })
146 | } else {
147 | this.data.videoContext.stop()
148 | }
149 | },
150 | pause: function () {
151 | this.setData({
152 | playing: false
153 | })
154 | },
155 | ended: function () {
156 | this.setData({
157 | playing: false
158 | })
159 | var that = this
160 | setTimeout(function () {
161 | that.data.videoContext.exitFullScreen()
162 | }, 500)
163 | },
164 | stop: function() {
165 | if (this.data.videoContext && this.data.videoContext.stop) {
166 | this.setData({
167 | playing: false,
168 | fullScreen: 'vertical',
169 | })
170 | this.data.videoContext.stop();
171 | }
172 | },
173 | fullscreenchange: function (e) {
174 | this.setData({
175 | fullScreen: e.detail.fullScreen
176 | })
177 | wx.showToast({
178 | icon: 'none',
179 | title: this.data.fullScreen ? '全屏' : '退出全屏',
180 | })
181 | },
182 | error: function (e) {
183 | console.error('error: ', e)
184 | this.stop()
185 | wx.showToast({
186 | image: '/res/img/err_icon.png',
187 | title: '播放失败'
188 | })
189 | },
190 | })
191 |
--------------------------------------------------------------------------------
/pages/video/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "点播播放",
3 | "disableScroll": true
4 | }
--------------------------------------------------------------------------------
/pages/video/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 | 支持mp4格式视频播放
15 |
16 |
17 |
23 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/pages/video/index.wxss:
--------------------------------------------------------------------------------
1 | .page-box {
2 | background: #000;
3 | }
4 | .bgImg {
5 | width: 100%;
6 | height: 100%;
7 | }
--------------------------------------------------------------------------------
/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件。",
3 | "setting": {
4 | "urlCheck": false,
5 | "es6": true,
6 | "postcss": true,
7 | "minified": true,
8 | "newFeature": true
9 | },
10 | "compileType": "miniprogram",
11 | "libVersion": "1.9.91",
12 | "appid": "",
13 | "projectname": "weapp-netcall",
14 | "condition": {
15 | "search": {
16 | "current": -1,
17 | "list": []
18 | },
19 | "conversation": {
20 | "current": -1,
21 | "list": []
22 | },
23 | "miniprogram": {
24 | "current": -1,
25 | "list": []
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/res/img/adapt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/adapt.png
--------------------------------------------------------------------------------
/res/img/anyway.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/anyway.png
--------------------------------------------------------------------------------
/res/img/beauty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/beauty.png
--------------------------------------------------------------------------------
/res/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/bg.png
--------------------------------------------------------------------------------
/res/img/camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/camera.png
--------------------------------------------------------------------------------
/res/img/df_hd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/df_hd.png
--------------------------------------------------------------------------------
/res/img/df_sd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/df_sd.png
--------------------------------------------------------------------------------
/res/img/df_ssd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/df_ssd.png
--------------------------------------------------------------------------------
/res/img/err_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/err_icon.png
--------------------------------------------------------------------------------
/res/img/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/file.png
--------------------------------------------------------------------------------
/res/img/future_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/future_icon.png
--------------------------------------------------------------------------------
/res/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/logo.png
--------------------------------------------------------------------------------
/res/img/mute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/mute.png
--------------------------------------------------------------------------------
/res/img/pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/pause.png
--------------------------------------------------------------------------------
/res/img/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/play.png
--------------------------------------------------------------------------------
/res/img/pull_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/pull_icon.png
--------------------------------------------------------------------------------
/res/img/push_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/push_icon.png
--------------------------------------------------------------------------------
/res/img/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/qrcode.png
--------------------------------------------------------------------------------
/res/img/scan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/scan.png
--------------------------------------------------------------------------------
/res/img/share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/share.png
--------------------------------------------------------------------------------
/res/img/turnout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/turnout.png
--------------------------------------------------------------------------------
/res/img/vod_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/vod_icon.png
--------------------------------------------------------------------------------
/res/img/white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/netease-im/weapp-netcall/581fd537b6af54b653ed5cb7bb211e0d8a699aab/res/img/white.png
--------------------------------------------------------------------------------
/utils/weapp.qrcode.esm.js:
--------------------------------------------------------------------------------
1 | /**
2 | * weapp.qrcode.js v0.6.0 (https://github.com/yingye/weapp-qrcode#readme)
3 | */
4 |
5 | function QR8bitByte(t) { this.mode = QRMode.MODE_8BIT_BYTE, this.data = t } function QRCode(t, e) { this.typeNumber = t, this.errorCorrectLevel = e, this.modules = null, this.moduleCount = 0, this.dataCache = null, this.dataList = new Array } QR8bitByte.prototype = { getLength: function (t) { return this.data.length }, write: function (t) { for (var e = 0; e < this.data.length; e++)t.put(this.data.charCodeAt(e), 8) } }, QRCode.prototype = { addData: function (t) { var e = new QR8bitByte(t); this.dataList.push(e), this.dataCache = null }, isDark: function (t, e) { if (t < 0 || this.moduleCount <= t || e < 0 || this.moduleCount <= e) throw new Error(t + "," + e); return this.modules[t][e] }, getModuleCount: function () { return this.moduleCount }, make: function () { if (this.typeNumber < 1) { var t = 1; for (t = 1; t < 40; t++) { for (var e = QRRSBlock.getRSBlocks(t, this.errorCorrectLevel), r = new QRBitBuffer, o = 0, n = 0; n < e.length; n++)o += e[n].dataCount; for (n = 0; n < this.dataList.length; n++) { var i = this.dataList[n]; r.put(i.mode, 4), r.put(i.getLength(), QRUtil.getLengthInBits(i.mode, t)), i.write(r) } if (r.getLengthInBits() <= 8 * o) break } this.typeNumber = t } this.makeImpl(!1, this.getBestMaskPattern()) }, makeImpl: function (t, e) { this.moduleCount = 4 * this.typeNumber + 17, this.modules = new Array(this.moduleCount); for (var r = 0; r < this.moduleCount; r++) { this.modules[r] = new Array(this.moduleCount); for (var o = 0; o < this.moduleCount; o++)this.modules[r][o] = null } this.setupPositionProbePattern(0, 0), this.setupPositionProbePattern(this.moduleCount - 7, 0), this.setupPositionProbePattern(0, this.moduleCount - 7), this.setupPositionAdjustPattern(), this.setupTimingPattern(), this.setupTypeInfo(t, e), this.typeNumber >= 7 && this.setupTypeNumber(t), null == this.dataCache && (this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList)), this.mapData(this.dataCache, e) }, setupPositionProbePattern: function (t, e) { for (var r = -1; r <= 7; r++)if (!(t + r <= -1 || this.moduleCount <= t + r)) for (var o = -1; o <= 7; o++)e + o <= -1 || this.moduleCount <= e + o || (this.modules[t + r][e + o] = 0 <= r && r <= 6 && (0 == o || 6 == o) || 0 <= o && o <= 6 && (0 == r || 6 == r) || 2 <= r && r <= 4 && 2 <= o && o <= 4) }, getBestMaskPattern: function () { for (var t = 0, e = 0, r = 0; r < 8; r++) { this.makeImpl(!0, r); var o = QRUtil.getLostPoint(this); (0 == r || t > o) && (t = o, e = r) } return e }, createMovieClip: function (t, e, r) { var o = t.createEmptyMovieClip(e, r); this.make(); for (var n = 0; n < this.modules.length; n++)for (var i = 1 * n, a = 0; a < this.modules[n].length; a++) { var s = 1 * a; this.modules[n][a] && (o.beginFill(0, 100), o.moveTo(s, i), o.lineTo(s + 1, i), o.lineTo(s + 1, i + 1), o.lineTo(s, i + 1), o.endFill()) } return o }, setupTimingPattern: function () { for (var t = 8; t < this.moduleCount - 8; t++)null == this.modules[t][6] && (this.modules[t][6] = t % 2 == 0); for (var e = 8; e < this.moduleCount - 8; e++)null == this.modules[6][e] && (this.modules[6][e] = e % 2 == 0) }, setupPositionAdjustPattern: function () { for (var t = QRUtil.getPatternPosition(this.typeNumber), e = 0; e < t.length; e++)for (var r = 0; r < t.length; r++) { var o = t[e], n = t[r]; if (null == this.modules[o][n]) for (var i = -2; i <= 2; i++)for (var a = -2; a <= 2; a++)this.modules[o + i][n + a] = -2 == i || 2 == i || -2 == a || 2 == a || 0 == i && 0 == a } }, setupTypeNumber: function (t) { for (var e = QRUtil.getBCHTypeNumber(this.typeNumber), r = 0; r < 18; r++) { var o = !t && 1 == (e >> r & 1); this.modules[Math.floor(r / 3)][r % 3 + this.moduleCount - 8 - 3] = o } for (r = 0; r < 18; r++) { o = !t && 1 == (e >> r & 1); this.modules[r % 3 + this.moduleCount - 8 - 3][Math.floor(r / 3)] = o } }, setupTypeInfo: function (t, e) { for (var r = this.errorCorrectLevel << 3 | e, o = QRUtil.getBCHTypeInfo(r), n = 0; n < 15; n++) { var i = !t && 1 == (o >> n & 1); n < 6 ? this.modules[n][8] = i : n < 8 ? this.modules[n + 1][8] = i : this.modules[this.moduleCount - 15 + n][8] = i } for (n = 0; n < 15; n++) { i = !t && 1 == (o >> n & 1); n < 8 ? this.modules[8][this.moduleCount - n - 1] = i : n < 9 ? this.modules[8][15 - n - 1 + 1] = i : this.modules[8][15 - n - 1] = i } this.modules[this.moduleCount - 8][8] = !t }, mapData: function (t, e) { for (var r = -1, o = this.moduleCount - 1, n = 7, i = 0, a = this.moduleCount - 1; a > 0; a -= 2)for (6 == a && a--; ;) { for (var s = 0; s < 2; s++)if (null == this.modules[o][a - s]) { var u = !1; i < t.length && (u = 1 == (t[i] >>> n & 1)), QRUtil.getMask(e, o, a - s) && (u = !u), this.modules[o][a - s] = u, -1 == --n && (i++ , n = 7) } if ((o += r) < 0 || this.moduleCount <= o) { o -= r, r = -r; break } } } }, QRCode.PAD0 = 236, QRCode.PAD1 = 17, QRCode.createData = function (t, e, r) { for (var o = QRRSBlock.getRSBlocks(t, e), n = new QRBitBuffer, i = 0; i < r.length; i++) { var a = r[i]; n.put(a.mode, 4), n.put(a.getLength(), QRUtil.getLengthInBits(a.mode, t)), a.write(n) } var s = 0; for (i = 0; i < o.length; i++)s += o[i].dataCount; if (n.getLengthInBits() > 8 * s) throw new Error("code length overflow. (" + n.getLengthInBits() + ">" + 8 * s + ")"); for (n.getLengthInBits() + 4 <= 8 * s && n.put(0, 4); n.getLengthInBits() % 8 != 0;)n.putBit(!1); for (; !(n.getLengthInBits() >= 8 * s || (n.put(QRCode.PAD0, 8), n.getLengthInBits() >= 8 * s));)n.put(QRCode.PAD1, 8); return QRCode.createBytes(n, o) }, QRCode.createBytes = function (t, e) { for (var r = 0, o = 0, n = 0, i = new Array(e.length), a = new Array(e.length), s = 0; s < e.length; s++) { var u = e[s].dataCount, h = e[s].totalCount - u; o = Math.max(o, u), n = Math.max(n, h), i[s] = new Array(u); for (var l = 0; l < i[s].length; l++)i[s][l] = 255 & t.buffer[l + r]; r += u; var f = QRUtil.getErrorCorrectPolynomial(h), g = new QRPolynomial(i[s], f.getLength() - 1).mod(f); a[s] = new Array(f.getLength() - 1); for (l = 0; l < a[s].length; l++) { var R = l + g.getLength() - a[s].length; a[s][l] = R >= 0 ? g.get(R) : 0 } } var d = 0; for (l = 0; l < e.length; l++)d += e[l].totalCount; var c = new Array(d), Q = 0; for (l = 0; l < o; l++)for (s = 0; s < e.length; s++)l < i[s].length && (c[Q++] = i[s][l]); for (l = 0; l < n; l++)for (s = 0; s < e.length; s++)l < a[s].length && (c[Q++] = a[s][l]); return c }; for (var QRMode = { MODE_NUMBER: 1, MODE_ALPHA_NUM: 2, MODE_8BIT_BYTE: 4, MODE_KANJI: 8 }, QRErrorCorrectLevel = { L: 1, M: 0, Q: 3, H: 2 }, QRMaskPattern = { PATTERN000: 0, PATTERN001: 1, PATTERN010: 2, PATTERN011: 3, PATTERN100: 4, PATTERN101: 5, PATTERN110: 6, PATTERN111: 7 }, QRUtil = { PATTERN_POSITION_TABLE: [[], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170]], G15: 1335, G18: 7973, G15_MASK: 21522, getBCHTypeInfo: function (t) { for (var e = t << 10; QRUtil.getBCHDigit(e) - QRUtil.getBCHDigit(QRUtil.G15) >= 0;)e ^= QRUtil.G15 << QRUtil.getBCHDigit(e) - QRUtil.getBCHDigit(QRUtil.G15); return (t << 10 | e) ^ QRUtil.G15_MASK }, getBCHTypeNumber: function (t) { for (var e = t << 12; QRUtil.getBCHDigit(e) - QRUtil.getBCHDigit(QRUtil.G18) >= 0;)e ^= QRUtil.G18 << QRUtil.getBCHDigit(e) - QRUtil.getBCHDigit(QRUtil.G18); return t << 12 | e }, getBCHDigit: function (t) { for (var e = 0; 0 != t;)e++ , t >>>= 1; return e }, getPatternPosition: function (t) { return QRUtil.PATTERN_POSITION_TABLE[t - 1] }, getMask: function (t, e, r) { switch (t) { case QRMaskPattern.PATTERN000: return (e + r) % 2 == 0; case QRMaskPattern.PATTERN001: return e % 2 == 0; case QRMaskPattern.PATTERN010: return r % 3 == 0; case QRMaskPattern.PATTERN011: return (e + r) % 3 == 0; case QRMaskPattern.PATTERN100: return (Math.floor(e / 2) + Math.floor(r / 3)) % 2 == 0; case QRMaskPattern.PATTERN101: return e * r % 2 + e * r % 3 == 0; case QRMaskPattern.PATTERN110: return (e * r % 2 + e * r % 3) % 2 == 0; case QRMaskPattern.PATTERN111: return (e * r % 3 + (e + r) % 2) % 2 == 0; default: throw new Error("bad maskPattern:" + t) } }, getErrorCorrectPolynomial: function (t) { for (var e = new QRPolynomial([1], 0), r = 0; r < t; r++)e = e.multiply(new QRPolynomial([1, QRMath.gexp(r)], 0)); return e }, getLengthInBits: function (t, e) { if (1 <= e && e < 10) switch (t) { case QRMode.MODE_NUMBER: return 10; case QRMode.MODE_ALPHA_NUM: return 9; case QRMode.MODE_8BIT_BYTE: case QRMode.MODE_KANJI: return 8; default: throw new Error("mode:" + t) } else if (e < 27) switch (t) { case QRMode.MODE_NUMBER: return 12; case QRMode.MODE_ALPHA_NUM: return 11; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 10; default: throw new Error("mode:" + t) } else { if (!(e < 41)) throw new Error("type:" + e); switch (t) { case QRMode.MODE_NUMBER: return 14; case QRMode.MODE_ALPHA_NUM: return 13; case QRMode.MODE_8BIT_BYTE: return 16; case QRMode.MODE_KANJI: return 12; default: throw new Error("mode:" + t) } } }, getLostPoint: function (t) { for (var e = t.getModuleCount(), r = 0, o = 0; o < e; o++)for (var n = 0; n < e; n++) { for (var i = 0, a = t.isDark(o, n), s = -1; s <= 1; s++)if (!(o + s < 0 || e <= o + s)) for (var u = -1; u <= 1; u++)n + u < 0 || e <= n + u || 0 == s && 0 == u || a == t.isDark(o + s, n + u) && i++; i > 5 && (r += 3 + i - 5) } for (o = 0; o < e - 1; o++)for (n = 0; n < e - 1; n++) { var h = 0; t.isDark(o, n) && h++ , t.isDark(o + 1, n) && h++ , t.isDark(o, n + 1) && h++ , t.isDark(o + 1, n + 1) && h++ , 0 != h && 4 != h || (r += 3) } for (o = 0; o < e; o++)for (n = 0; n < e - 6; n++)t.isDark(o, n) && !t.isDark(o, n + 1) && t.isDark(o, n + 2) && t.isDark(o, n + 3) && t.isDark(o, n + 4) && !t.isDark(o, n + 5) && t.isDark(o, n + 6) && (r += 40); for (n = 0; n < e; n++)for (o = 0; o < e - 6; o++)t.isDark(o, n) && !t.isDark(o + 1, n) && t.isDark(o + 2, n) && t.isDark(o + 3, n) && t.isDark(o + 4, n) && !t.isDark(o + 5, n) && t.isDark(o + 6, n) && (r += 40); var l = 0; for (n = 0; n < e; n++)for (o = 0; o < e; o++)t.isDark(o, n) && l++; return r += 10 * (Math.abs(100 * l / e / e - 50) / 5) } }, QRMath = { glog: function (t) { if (t < 1) throw new Error("glog(" + t + ")"); return QRMath.LOG_TABLE[t] }, gexp: function (t) { for (; t < 0;)t += 255; for (; t >= 256;)t -= 255; return QRMath.EXP_TABLE[t] }, EXP_TABLE: new Array(256), LOG_TABLE: new Array(256) }, i = 0; i < 8; i++)QRMath.EXP_TABLE[i] = 1 << i; for (i = 8; i < 256; i++)QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8]; for (i = 0; i < 255; i++)QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i; function QRPolynomial(t, e) { if (void 0 == t.length) throw new Error(t.length + "/" + e); for (var r = 0; r < t.length && 0 == t[r];)r++; this.num = new Array(t.length - r + e); for (var o = 0; o < t.length - r; o++)this.num[o] = t[o + r] } function QRRSBlock(t, e) { this.totalCount = t, this.dataCount = e } function QRBitBuffer() { this.buffer = new Array, this.length = 0 } function drawQrcode(t) { t = t || {}, (t = Object.assign({ width: 256, height: 256, typeNumber: -1, correctLevel: QRErrorCorrectLevel.H, background: "#ffffff", foreground: "#000000" }, t)).canvasId ? function () { var e = new QRCode(t.typeNumber, t.correctLevel); e.addData(t.text), e.make(); for (var r = wx.createCanvasContext && wx.createCanvasContext(t.canvasId), o = t.width / e.getModuleCount(), n = t.height / e.getModuleCount(), i = 0; i < e.getModuleCount(); i++)for (var a = 0; a < e.getModuleCount(); a++) { var s = e.isDark(i, a) ? t.foreground : t.background; r.setFillStyle(s); var u = Math.ceil((a + 1) * o) - Math.floor(a * o), h = Math.ceil((i + 1) * o) - Math.floor(i * o); r.fillRect(Math.round(a * o), Math.round(i * n), u, h) } r.draw() }() : console.warn("please you set canvasId!") } QRPolynomial.prototype = { get: function (t) { return this.num[t] }, getLength: function () { return this.num.length }, multiply: function (t) { for (var e = new Array(this.getLength() + t.getLength() - 1), r = 0; r < this.getLength(); r++)for (var o = 0; o < t.getLength(); o++)e[r + o] ^= QRMath.gexp(QRMath.glog(this.get(r)) + QRMath.glog(t.get(o))); return new QRPolynomial(e, 0) }, mod: function (t) { if (this.getLength() - t.getLength() < 0) return this; for (var e = QRMath.glog(this.get(0)) - QRMath.glog(t.get(0)), r = new Array(this.getLength()), o = 0; o < this.getLength(); o++)r[o] = this.get(o); for (o = 0; o < t.getLength(); o++)r[o] ^= QRMath.gexp(QRMath.glog(t.get(o)) + e); return new QRPolynomial(r, 0).mod(t) } }, QRRSBlock.RS_BLOCK_TABLE = [[1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12], [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16]], QRRSBlock.getRSBlocks = function (t, e) { var r = QRRSBlock.getRsBlockTable(t, e); if (void 0 == r) throw new Error("bad rs block @ typeNumber:" + t + "/errorCorrectLevel:" + e); for (var o = r.length / 3, n = new Array, i = 0; i < o; i++)for (var a = r[3 * i + 0], s = r[3 * i + 1], u = r[3 * i + 2], h = 0; h < a; h++)n.push(new QRRSBlock(s, u)); return n }, QRRSBlock.getRsBlockTable = function (t, e) { switch (e) { case QRErrorCorrectLevel.L: return QRRSBlock.RS_BLOCK_TABLE[4 * (t - 1) + 0]; case QRErrorCorrectLevel.M: return QRRSBlock.RS_BLOCK_TABLE[4 * (t - 1) + 1]; case QRErrorCorrectLevel.Q: return QRRSBlock.RS_BLOCK_TABLE[4 * (t - 1) + 2]; case QRErrorCorrectLevel.H: return QRRSBlock.RS_BLOCK_TABLE[4 * (t - 1) + 3]; default: return } }, QRBitBuffer.prototype = { get: function (t) { var e = Math.floor(t / 8); return 1 == (this.buffer[e] >>> 7 - t % 8 & 1) }, put: function (t, e) { for (var r = 0; r < e; r++)this.putBit(1 == (t >>> e - r - 1 & 1)) }, getLengthInBits: function () { return this.length }, putBit: function (t) { var e = Math.floor(this.length / 8); this.buffer.length <= e && this.buffer.push(0), t && (this.buffer[e] |= 128 >>> this.length % 8), this.length++ } }; export default drawQrcode;
6 |
--------------------------------------------------------------------------------