vue 实现 扫二维码 功能

如何通过 vue 调用 相机 实现 扫一扫的功能,但是查看文档发现,需要获取 getUserMedia 的属性值,但存在兼容性问题。

退而求其次,通过 h5plus 来实现。

QrScanner.vue

<!-- 扫描二维码-->

<template>

<div>

<!-- 内容部分 -->

<video id="qr-vedio"class="v"autoplay=""></video>

<canvas id="qr-canvas"width="800"height="600"style="width: 800px; height: 600px;display:none;"></canvas><p v-show="result != ''">{{result}}</p>

<p v-show="errorMes != ''">{{errorMes}}</p></div></template><script>

exportdefault{

props: {//},

data () {

return{

vedio: '',

canvas: '',

context: '',

stopScan: null,

errorMes: '',

result: ''

}

},

mounted(){

console.log(1);

var_that = this;

window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

this.vedio = document.getElementById('qr-vedio');

this.canvas = document.getElementById('qr-canvas');

this.context = this.canvas.getContext('2d');

console.log(2);// Call the getUserMedia method with our callback functionsif(navigator.getUserMedia) {

console.log(3);

varvideoSource = [];navigator.mediaDevices.enumerateDevices().then((function(sourceInfos) {

var i;

for(i = 0; i != sourceInfos.length; ++i) {

var sourceInfo = sourceInfos[i];

if(sourceInfo.kind === 'videoinput'&& sourceInfo.label.indexOf('back') != -1) {

videoSource.push(sourceInfo.deviceId);

}

}

var successCallback = function(stream) {_

that.vedio.src = (window.URL && window.URL.createObjectURL(stream)) || stream;window.localMediaStream = stream;_that.vedio.addEventListener("loadstart", function() {_that.vedio.play();}, false);

_that.stopScan = setInterval(_that.scan, 500);}

navigator.getUserMedia({

video: {

optional: [{ sourceId: videoSource[0] }]

}}, successCallback, function(e) {console.log(e);});}));

} else{

this.errorMes = 'Native web camera streaming (getUserMedia) not supported in this browser.';

}

qrcode.callback = function(data) {_

that.result = data;

console.log(data)

if(window.localMediaStream && window.localMediaStream.stop) {

window.localMediaStream.stop();

}

if(_that.stopScan) {

clearInterval(_that.stopScan);}};

},methods: {

scan() {

if(window.localMediaStream) {

this.context.drawImage(this.vedio, 0, 0, 100, 100);

}try{

qrcode.decode();

} catch(e) {

console.log('decode has error');

}}}}

</script>

<style lang="less"scoped>

.v {width: 320px;height: 240px;}

#qr-canvas {width: 800px;height: 800px;}

</style>

记录自己写的一个自定义vide标签自定义控制按钮事件播放器,video标签自定义按钮

由于UI还没出样式,于是就先写写video自定义按钮事件,附在线mp4视频测试地址
版本:vue,没样式,只有video标签自定义控制按钮事件
在线mp4测试地址:GO

<template>
    <div class="player">
        <video ref="video" :controls="false" controlslist="nodownload">
            <!-- <source src="./../../assets/source/sintel.mp4" type="video/mp4"/> -->
            <source
                src="http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4"
                type="video/mp4"
            />
        </video>
        <div class="btns">
            <el-button ref="isPlay" class="stop">停止/播放</el-button>
            <el-button ref="enableMute">关闭声音</el-button>
            <el-button ref="disableMute">打开声音</el-button>
            <input type="range" ref="ran" :value="ranVal"/>
            <div ref="current"></div>
            <!-- 当前进度 -->
            <div ref="buffered"></div>
            <!-- 下载进度 秒 -->
            <div ref="duration"></div>
            <!-- 总时长 -->
            <el-button ref="fullScreen">全屏</el-button>
            <!-- 全屏按钮 -->
            <div ref="progress" style="height:10px;background:#f00;">
                <!-- 进度条 -->
                <div ref="bar" style="height:5px;background:#0f0;"></div>
                <!-- 播放进度 -->
                <div ref="buffer" style="height:5px;background:#00f;"></div>
                <!-- 缓存进度 -->
            </div>
        </div>
    </div>
</template>

<script>
import { hasClass } from "@/commons/func";
export default {
    data() {
        return {
            bufferTimer: null,
            timer: null,
            video: null,

            enableMute: null,
            disableMute: null,
            ran: null,
            ranVal:0,
            current: null,
            buffered: null,
            duration: null,
            fullScreen: null,
            progress: null,
            bar: null,
            buffer: null
        };
    },
    methods: {
        init() {
            this.video = this.$refs.video; //获取video对象
            this.isPlay = this.$refs.isPlay.$el; //获取播放/暂停按钮对象,element-ui库需要'.$el'获取
            this.enableMute = this.$refs.enableMute.$el; //获取关闭声音按钮对象
            this.disableMute = this.$refs.disableMute.$el; //获取开启声音按钮对象
            this.ran = this.$refs.ran; //获取滑块对象,方便调整音量大小
            this.ranVal = this.video.volume*100;
            this.ran.style.backgroundSize = this.ranVal+'% 100%';
            this.current = this.$refs.current; //获取显示当前播放时间进度的对象
            this.buffered = this.$refs.buffered; //获取显示下载进度的对象,下载使用,暂时无用
            this.duration = this.$refs.duration; //
            this.fullScreen = this.$refs.fullScreen.$el;
            this.progress = this.$refs.progress;
            this.bar = this.$refs.bar;
            this.buffer = this.$refs.buffer;
            this.addEvent(this.isPlay, "click", this.playPause);
            this.addEvent(this.video, "timeupdate", this.timeupdate);
            this.addEvent(this.progress, "click", this.changeProgress);
            this.addEvent(this.fullScreen, "click", this.launchFullScreen);
            this.addEvent(this.enableMute, "click", this.closeVolume);
            this.addEvent(this.disableMute, "click", this.openVolume);
            this.rangeSlider(this.ran,{min:0,max:100,step:5,callback:this.setVolume})
        },
        // 补零
        zeroFill(num) {
            if (num < 10) {
                num = "0" + num;
            }
            return num;
        },
        // 处理秒数为时分秒 h:m:s
        getTime(num) {
            let m = this.zeroFill(Math.floor(num / 60) % 60),
                s = this.zeroFill(Math.floor(num % 60)),
                h = this.zeroFill(Math.floor(Math.floor(num / 60) / 60)),
                time = "" + h + ":" + m + ":" + s + "";
            return time;
        },
        //全屏方法
        launchFullScreen() {
            if (this.video.requestFullscreen) {
                this.video.requestFullscreen();
            } else if (this.video.mozRequestFullScreen) {
                this.video.mozRequestFullScreen();
            } else if (this.video.webkitRequestFullscreen) {
                this.video.webkitRequestFullscreen();
            } else if (this.video.msRequestFullscreen) {
                this.video.msRequestFullscreen();
            }
        },
        //播放和暂停
        playPause() {
            let classStr = this.isPlay.className;
            if (hasClass(this.isPlay, "stop")) {
                this.video.play();
                this.bufferTimer = setInterval(() => {
                    this.buffer.style.width =
                        (this.video.buffered.end(0) / this.video.duration) *
                            100 +
                        "%";
                }, 1000 / 30);
                if (this.video.buffered.end(0) == this.video.duration) {
                    this.buffer.style.width = "100%";
                    clearInterval(this.bufferTimer);
                }
                this.timer = setInterval(() => {
                    this.bar.style.width =
                        (this.video.currentTime / this.video.duration) * 100 +
                        "%";
                }, 1000 / 30);
                this.isPlay.className = classStr.replace("stop", "play");
            } else if (hasClass(this.isPlay, "play")) {
                this.video.pause();
                clearInterval(this.timer);
                this.isPlay.className = classStr.replace("play", "stop");
            }
        },
        //视频播放进度改变触发
        timeupdate() {
            this.current.innerHTML = this.getTime(this.video.currentTime);
            this.duration.innerHTML = this.getTime(this.video.duration);
            this.buffered.innerHTML = this.video.buffered.end(0);
            if (this.video.currentTime == this.video.duration) {
                this.isPlay.className = this.isPlay.className.replace(
                    "play",
                    "stop"
                );
            }
        },
        //点击进度条改变播放进度
        changeProgress(e) {
            let barLength = e.pageX - this.progress.offsetLeft;
            this.video.currentTime =
                (barLength / this.progress.clientWidth) * this.video.duration;
            this.bar.style.width =
                (barLength / this.progress.clientWidth) * 100 + "%";
        },
        //关闭声音
        closeVolume() {
            this.video.muted = true;
        },
        //开启声音
        openVolume() {
            this.video.muted = false;
        },
        //设置音量
        setVolume() {
            this.video.volume = this.ran.value / 100;
            this.video.muted = false;
        },
        rangeSlider(rangeElem, { min, max, step, callback }) {
            min = !isNaN(parseFloat(min)) ? Number(min) : null;
            max = !isNaN(parseFloat(max)) ? Number(max) : null;
            step = !isNaN(parseFloat(step)) ? Number(step) : 1;
            callback = callback ? callback : null;

            rangeElem.setAttribute("min", min);
            rangeElem.setAttribute("max", max);
            rangeElem.setAttribute("step", step);

            rangeElem.addEventListener("input", function(e) {
                var that = e.target;
                that.style.backgroundSize = this.value + "% 100%";
                if (typeof callback == "function") {
                    callback(that);
                }
            });
        }
    },
    mounted() {
        this.init();
    }
};
</script>

<style lang="less" scoped>
input[type="range"] {
    -webkit-appearance: none;
    width: 200px;
    height: 5px;
    border-radius: 5px;
    background: -webkit-linear-gradient(#fa03e4, #a5f601) no-repeat;
    background-size: 0% 100%;
}
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    height: 15px;
    width: 5px;
    margin-top: -5px; /*使滑块超出轨道部分的偏移量相等*/
    background: #f5f5f5;
    border-radius: 2px; /*外观设置为圆形*/
    border: solid 1px #a5a5a5; /*设置边框*/
    box-shadow: 0 0px 1px #666666; /*添加底部阴影*/
}
input[type="range"]::-webkit-slider-runnable-track {
    height: 5px;
    border-radius: 2px; /*将轨道设为圆角的*/
    box-shadow: 0 0px 1px #0f00ff, inset 0 0px 2px #00ffff; /* 轨道内置阴影效果 */
}
input[type="range"]:focus {
    outline: none;
}
</style>

有个通用函数是写在外面引入的,现写在这儿,防止用到的兄dei不知道怎么弄

hasClass(elem,classm){
    return elem.className.indexOf(classm) > -1;
}

有几个绑定事件的函数估计会找不到,this.addEvent 找不到的【看这里】,已经封装好了的,可以直接拿去用

获取用户当前位置信息的两种方法——H5、微信

在之前的 调用百度地图API的总结 中获取当前位置信息我用的是 H5 ,其实微信也提供了获取用户地理位置的方法,现将这两种方法都贴出来,看情况选择使用。

一、H5 获取当前地理位置得到经纬度

复制代码
   // H5 获取当前位置经纬度
    var location_lon = '',location_lat = ''; // 经度,纬度
    if (navigator.geolocation){
        navigator.geolocation.getCurrentPosition(function (position) {
            location_lon = position.coords.longitude;
            location_lat = position.coords.latitude;
           // alert('h5经度:'+location_lon);alert('h5纬度:'+location_lat);
        });
    }else {
        alert("您的设备不支持定位功能");
    }
复制代码

二、微信公众平台(地理位置)

微信有获取用户地理位置的接口,在 wx.config 中配置就可以用了:

复制代码
wx.config({
    debug: true, 
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名
    jsApiList: ['getLocation'] // 必填,需要使用的JS接口列表
});
复制代码

获得权限后就可以直接使用 wx.getLocation() 方法获得用户的地理位置了:

复制代码
wx.getLocation({
   type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
   success: function (res) {
      var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
      var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
      var speed = res.speed; // 速度,以米/每秒计
      var accuracy = res.accuracy; // 位置精度
   }
});
复制代码

三、地理位置配合地图展示

获得的经纬度可以使用百度地图或者高德地图展示,微信内置地图也可以查看位置:

复制代码
wx.openLocation({
   latitude: 0, // 纬度,浮点数,范围为90 ~ -90
   longitude: 0, // 经度,浮点数,范围为180 ~ -180。
   name: '', // 位置名
   address: '', // 地址详情说明
   scale: 1, // 地图缩放级别,整形值,范围从1~28。默认为最大
   infoUrl: '' // 在查看位置界面底部显示的超链接,可点击跳转
});
复制代码

vue h5 video 多视频无缝切换

思路

  1. 页面中创建两个 video 标签
  2. 在组件加载时同时加载两个视频资源(autoplay),暂时不播放的视频在 canplay 事件中调用暂停方法,使之实现预加载
  3. 使用绝对定位将预加载的视频移出可视窗口
  4. 第一个视频结束后,通过 ended 事件播放之前预加载的视频,同时将当前 src 切换为下一个需要播放的视频地址,完成缓存

核心代码

    <video ref="video" :class="{'video-hide':!playVideoTag}" autoplay @ended="onEnded('video')"></video>
    <video ref="video1" :class="{'video-hide':playVideoTag}" autoplay @ended="onEnded('video1')"></video>
  data() {
    return {
      srcList: [],
      curIndex: 0,
      playVideoTag: true, // 当前播放的 ref 是否为 video
      canPlayNext: true   // 是否存在下一个播放地址
    }
  },
    onEnded(e) {
      if (this.canPlayNext) {
        const video = e === 'video' ? this.$refs.video1 : this.$refs.video
        video.play()
        this.playVideoTag = !this.playVideoTag
        this.loadNext()
      }
      console.log('onended my video')
      this.$emit('ended')
    },
    playVideo(src) {  // 可以接受单个 url 和 url 列表
      this.srcList = typeof src === 'string' ? [src] : src
      this.$refs.video.src = src[0]
      this.srcList = src
      this.loadNext()
    },
    loadNext() {
      const video = this.playVideoTag ? this.$refs.video1 : this.$refs.video
      if (this.srcList.length - 1 === this.curIndex) { // 当前索引是列表的最后一个
        console.log('没有新视频')
        this.canPlayNext = false
        return
      }
      video.src = this.srcList[++this.curIndex]
	  video.addEventListener('canplay', e => e.target.pause(), { once: true })
    }
	.video-hide{
	  position: absolute;
	  top: -10000px;
	  left: -10000px;
	}

uni-app 横屏设置

打开APP就是横屏需要在app.vue中进行设置。

<script>    export default {        onLaunch: function() {            console.log('App Launch');			plus.screen.lockOrientation('landscape-primary'); //锁定        },        onShow: function() {            console.log('App Show');        },        onHide: function() {            console.log('App Hide');        }    }</script>

主要代码:plus.screen.lockOrientation(‘landscape-primary’); 

API解释如下:

锁定屏幕方向

void plus.screen.lockOrientation( String orientation );

说明:

锁定屏幕方向后屏幕只能按锁定的屏幕方向显示,关闭当前页面后仍然有效。 可再次调用此方法修改屏幕锁定方向或调用unlockOrientation()方法恢复到应用的默认值。

参数:

  • orientation: ( String ) 必选 要锁定的屏幕方向值
    锁定屏幕方向可取以下值: “portrait-primary”: 竖屏正方向; “portrait-secondary”: 竖屏反方向,屏幕正方向按顺时针旋转180°; “landscape-primary”: 横屏正方向,屏幕正方向按顺时针旋转90°; “landscape-secondary”: 横屏方向,屏幕正方向按顺时针旋转270°; “portrait”: 竖屏正方向或反方向,根据设备重力感应器自动调整; “landscape”: 横屏正方向或反方向,根据设备重力感应器自动调整;

返回值:

void : 无

uni-app 数据缓存

1.uni.setStorage(OBJECT) 与 uni.getStorage(OBJECT)    这两个是异步缓存,简单说就是将数据放到本地缓存指定的key中,一个存一个取罢了

    uni.setStorage(OBJECT) :将数据缓存在本地缓存中指定的key中,会覆盖掉原来该key中的内容,这是一个异步接口。(

参数名      类型         是否必填    说明

key        String        是        本地缓存中的指定的 key

data       Object/String   是        需要存储的内容

success     Function       否       接口调用成功的回调函数

fail       Function       否       接口调用失败的回调函数

complete    Function      否        接口调用结束的回调函数(调用成功、失败都会执行)

uni.getStorage(OBJECT) : 从本地缓存中异步获取指定key对应的内容(

参数名       类型       是否必填     说明

 key          String      是         本地缓存中的指定的key

 success       Function     是         接口调用的回调函数res = {data: key对应的内容}

fail         Function     否         接口调用失败的回调函数

complete       Function     否         接口调用结束的回调函数(调用成功、失败都会执行)

     那个例子跟大伙说一下,最近公司做项目,涉及到了一个存放地址的, 将地址的信息缓存到本地指定的URL 这个key中 进行存数据与取数据的操作,废话不多说,上代码~~

 下一步进行将数据缓存在本地

 下一步读取缓存在本地数据

//这是获取key中的内容 {data: “/pages/newHouse/NewHouseDetail?id=41”, errMsg: “getStorage:ok”}

  绿色标记为我在本地缓存中获取到的数据,res.data 就是我们需要的url地址了,如果需要直接获取就可以了,这一步骤就给大家展示了uni.setStorage(OBJECT) 与 uni.getStorage(OBJECT)  整体过程

2.uni.setStorageStnc(KEY,DATA) 与 uni.getStorage(KEY)    其实这个跟第一个基本上是没有区别的,只不过是一个同步一个异步罢了,参数说明及写法展示给大家

  uni.setStorageStnc(KEY,DATA)     将data存储在本地缓存中个指定的key中,会覆盖相同key中对应的内容,这是一个同步接口

        参数名      类型         是否必填    说明

        key        String        是        本地缓存中的指定的 key

                            data       Any   是        需要存储的内容,只支持原生类型、及能够通过 JSON.stringify 序列化的对象

    还是以地址url为例,以上个例子为基础来说  

uni.getStorageStnc(KEY)     从本地缓存中同步获取指定 key 对应的内容。

        参数名      类型         是否必填    说明

        key        String        是        本地缓存中的指定的 key

获取同步的缓存key中地址信息         

3.uni.removeStorage(OBJECT) 与 uni.removeStorageSync(KEY)   这两个都是从本地缓存中移除指定的key   与上面的也是一样样样的~~~   前面是异步的后面是同步的     异步为例~~~~

这是我打印出来的数据   通过了一个点击事件,这是显示我移除成功了,然后的~~~~等等等  等你在点击的时候会发现他并不打印了   也许你会因此疑惑  那是因为我已经将这个key所对应的数据移除了呀,所以当然是什么都不打印的啦~~~

4.uni.clearStorage() 与 uni.clearStorageSync()   这两个都是清理本地数据的缓存    当然啦  还是一个异步一个同步的    这个跟上一个其实是一样的   就是说这个吧 emmmmm   清除了你所有的本地数据   上一个只是清除了你本地指定key中的内容

写法就是直接emmmm 写!!!    uni.clearStorage()和uni.clearStorageSync()

作者:家乡_a6ce
链接:https://www.jianshu.com/p/cef270f827fa
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

MySQL异常ERROR 1055 (420ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause

大致错误如:ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘aaa.test.age’ which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by

一般是执行类似如下语句报错:

mysql> select * from customers group by age;

解决方法:

1、登陆mysql服务器,执行以下两条命令,在global与session级都修改;

mysql> set global sql_mode=‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’;

mysql> set session sql_mode=‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’;

2、同时,在my.cnf文件的[mysqld]字段中,指定sql_mode的值:

[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

以上两种方法同时执行后,不用重启mysql

——————————————————————————————————
注:我只修改了第1步即可成功执行了
原文链接:https://www.cnblogs.com/weiyiming007/p/10025181.html

发一个WebSocket+PHP聊天室-PHP多进程实时服务器推送技术

这一阵子在家写了个PHP聊天室,给大家献上代码,有用到的可以拿去用。

先介绍下聊天室的特性:
1、服务端采用纯php开发,不依赖php-fpm、nginx、或者apache数据库等,部署简单
2、采用php多进程,充分利用cpu资源
3、gateway workers进程模型,gateway进程只负责网络IO,worker进程负责业务逻辑,各尽其责,稳定高效
4、支持libevent事件轮询库,支持Epoll高并发(理论上可以支持上万的不活跃连接),服务端实时推送
5、支持分布式部署,可横向扩容
6、使用Websocket协议,占用带宽小,性能更好
7、客户端跨浏览器支持,只要浏览器支持flash或者支持websocket即可使用

demo点击这里(http://workerman.net:55151/)

安装、启动、使用就三步(不支持Win系统):
1、下载代码 ,并解压到任意目录

2、启动服务 ./bin/workermand start 如图:

3、访问启动服务的服务器55151端口 例如:http://127.0.0.1:55151 如图:

最后说明:
        这个聊天室是基于workerman框架开发的,workerman不支持winows系统,所以这个聊天室服务端不能在windows上部署。
        这个聊天室只是一个简单的demo,业务逻辑+注释不到200行代码(applications/Chat/Event.php中),大家可以根据自己的需要加上分组、私聊、表情等功能。

最后再发一下PHP聊天室下载地址 workerman-chat
————————————————
版权声明:本文为CSDN博主「walkor」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/udefined/article/details/23177119

使用Linux服务器搭建直播服务器,并运用Apicloud平台简易快速的制作全功能直播APP

simpleLive

使用Linux服务器搭建直播服务器,并运用Apicloud平台简易快速的制作全功能直播APP。

代码目录结构介绍

widget文件夹里是源代码,熟悉Apicloud的朋友对这个文件夹都不陌生,这里面包括所用到的aliyunLive模块代码。具体其目录结构和代码结构需使用者自行研究。

app:我作为开发者所关注并开发的部分,我编写的代码都在这里面。注意,这个目录不包含aliyunLive模块。

服务器端

打开相关端口

Rtmp会使用到1935端口

  1. 到阿里云后台设置本实例安全组规则开放1935端口。
  2. 配置防火墙打开1935端口。

安装FFMPEG

  1. 安装依赖包和升级相关工具

yum -y install gcc glibc glibc-devel make nasm pkgconfig lib-devel openssl-devel expat-devel gettext-devel libtool mhash.x86_64 perl-Digest-SHA1.x86_64

yum -y update gcc

yum -y install gcc+ gcc-c++

  1. 安装相关工具包
  2. git

yum install -y git

  • zlib

yum install -y zlib

  • pcre

yum install -y pcre

  • yadmi

cd /

mkdir tommycd tommy

wget http://sourceforge.net/projects/yamdi/files/yamdi/1.4/yamdi-1.4.tar.gz/download

tar -zxvf downloadcd yamdi-1.4

make && make installcd ..

  • openssl

yum install -y openssl

  1. 安装ffmpeg及其依赖包
  2. Yasm

wget http://www.tortall.net/projects/yasm/releases/yasm-1.2.0.tar.gz

tar -zxvf yasm-1.2.0.tar.gz

cd yasm-1.2.0

./configuremake && make install

cd ..

  • x264

git clone git://git.videolan.org/x264

cd x264

./configure –enable-sharedmake && make install

cd ..

  • LAME

wget http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz

tar -zxvf lame-3.99.5.tar.gz

cd lame-3.99.5

./configure –enable-nasmmake && make install

cd ..

  • libogg

wget http://downloads.xiph.org/releases/ogg/libogg-1.3.0.tar.gz

tar -zxvf libogg-1.3.0.tar.gz

cd libogg-1.3.0

./configuremake && make install

cd ..

  • libvorbis

安装这个工具时废了很大力气,有报错,后来重复确认安装libogg后才成功。

wget http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.3.tar.gz

tar -zxvf libvorbis-1.3.3.tar.gz

cd libvorbis-1.3.3

./configuremake && make install

cd ..

  • libvpx

git clone http://git.chromium.org/webm/libvpx.git

cd libvpx

./configure  –enable-sharedmake && make install

cd ..

  • faad2

wget http://downloads.sourceforge.net/project/faac/faad2-src/faad2-2.7/faad2-2.7.tar.gz

tar -zxvf faad2-2.7.tar.gz

cd faad2-2.7

./configuremake && make install

cd ..

  • fdk-acc

新版本的FFMPEG已经取消对faac的支持,改用fdk-acc进行解码,效率会更高。

wget https://nchc.dl.sourceforge.net/project/opencore-amr/fdk-aac/fdk-aac-0.1.5.tar.gz

tar -zxvf fdk-aac-0.1.5.tar.gz

cd fdk-aac-0.1.5

./configuremake && make install

cd ..

  • Xvid

wget http://downloads.xvid.org/downloads/xvidcore-1.3.2.tar.gz

tar zxvf xvidcore-1.3.2.tar.gz

cd xvidcore/build/generic

./configuremake && make install

cd ..

  • ffmpeg

git clone https://github.com/FFmpeg/FFmpeg.gitcd ffmpeg

./configure  –prefix=/opt/ffmpeg/ –enable-version3 –enable-libvpx –enable-libfdk-aac –enable-libmp3lame –enable-libvorbis –enable-libx264 –enable-libxvid –enable-shared –enable-gpl –enable-postproc –enable-nonfree  –enable-avfilter –enable-pthreadscd ..

  • 修改/etc/ld.so.conf如下:

include ld.so.conf.d/*.conf

/lib

/lib64

/usr/lib

/usr/lib64

/usr/local/lib

/usr/local/lib64

/opt/ffmpeg/lib

然后在/etc目录执行相关命令

cd /etc

ldconfig

  • 测试ffmpeg

我上传了一个test.mp4到/tommy下,使用命令查看ffmpeg运行情况

ffmpeg -re -i /tommy/test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://my-ip:1935/live/steam

在终端看到以下命令则证明推流成功:

然后使用VCL(MAC端的一个播放器)等能播放rtmp流视频的播放器查看真正的结果是否成功:

查看播放结果:

如果上述都能如图显示,则证明FFMPEG安装和配置成功!

为Nginx安装nginx-rtmp-module

  1. 下载nginx-rtmp-module

cd /tommy

git clone https://github.com/arut/nginx-rtmp-module.git

  1. 动态添加nginx-rtmp-module模块

wget http://nginx.org/download/nginx-1.10.2.tar.gz

tar -zxvf nginx-1.10.2.tar.gz

cd nginx-1.10.2

./configure –prefix=/usr/share/nginx –sbin-path=/usr/sbin/nginx –modules-path=/usr/lib64/nginx/modules –conf-path=/etc/nginx/nginx.conf –error-log-path=/var/log/nginx/error.log –http-log-path=/var/log/nginx/access.log –http-client-body-temp-path=/var/lib/nginx/tmp/client_body –http-proxy-temp-path=/var/lib/nginx/tmp/proxy –http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi –http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi –http-scgi-temp-path=/var/lib/nginx/tmp/scgi –pid-path=/run/nginx.pid –lock-path=/run/lock/subsys/nginx –user=nginx –group=nginx –with-file-aio –with-ipv6 –with-http_ssl_module –with-http_v2_module –with-http_realip_module –with-http_addition_module –with-http_sub_module –with-http_dav_module –with-http_flv_module –with-http_mp4_module –with-http_gunzip_module –with-http_gzip_static_module –with-http_random_index_module –with-http_secure_link_module –with-http_degradation_module –with-http_slice_module –with-http_stub_status_module –with-mail_ssl_module –with-pcre –with-pcre-jit –with-stream=dynamic –with-stream_ssl_module –with-debug –add-module=/tommy/nginx-rtmp-module

cp /usr/sbin/nginx /usr/sbin/nginx.bak

cp ./objs/nginx /usr/sbin//usr/sbin/nginx -s reload

  1. 配置nginx.conf

vi /etc/nginx/nginx.conf

在server里添加

rtmp {

       server {

               listen 1935;

               application live {

                       live on;

               }

       }

}

重启nginx

/usr/sbin/nginx -s reload

APP端

将主播端和播放端集成到了同一个APP中,演示时请用两部安卓手机安装,一个做主播,一个负责播放。

概述

  1. 关于使用的平台
http://www.apicloud.com/
  1. 关于使用的模块

aliyunLive 封装了阿里云视频直播的 sdk,该模块包括视频流采集和视频流播放两部分。如使用阿里云直播服务器搭配此模块,需要在阿里云官网注册,在控制台创建直播流,并获取播放端相关参数,详情可以参照阿里云直播官网:阿里云视频直播。

注意: 该模块android版 播放器只适用于android 4.4+,推流需要 android 4.5 以上版本支持。iOS版最低版本为8.0。(iOS云编译的时候需要勾选相机和麦克风权限,并在高级设置里选择iOS版本为8.0)。

  1. 技术部分

  HTML5

  CSS3

  JS(Vue)

  1. 工具
  2. 编辑器Atom
  3. 直播模块aliyunLive,模块文档http://docs.apicloud.com/Client-API/Open-SDK/aliyunLive

主播部分

下方的地址最后一个参数steam可以随意更换,不同的地址可以视作不同的房间,并且观看端也配置相同的地址才能观看该房间的直播画面。

无论通过下方更换地址还是帧率等,都需要重新 配置直播流 并推流才能生效。

请依次点击“配置直播流”、“开始推流”进行直播:

播放部分

播放前必须先进行初始化,并点击准备按钮。

不同的地址可以视作不同的房间,只有和主播端相同的地址才能观看该房间的直播画面。

无论通过下方更换地址等,都需要重新 初始化->准备 才能生效。

Linux自建直播服务器一:使用Nginx+rtmp模块搭建流媒体服务器

2018-06-25 分类:服务器建站 阅读(6596) 评论(0)

技术服务于生活。当在日常遇到问题的时候,需要经常去思考如何解决这个问题。

最近网易的逆水寒开测了,非常荣幸拿到一个资格号,可以来体验一下这款国产巅峰级网游,一个会呼吸的江湖。作为一个极度业余的主播(一般只直播给朋友看),自然打开了斗鱼直播助手,准备直播一下。

可是比较无奈的是这款游戏的配置要求太高了,不开直播不卡,但是一开直播马上卡的起飞。一看斗鱼直播助手的cpu占用率飙到了30%。关闭这个软件,马上就好了。

后来想了一个解决方法,用OBS软件直接推流。OBS比较给力,一直占用10%左右的CPU。几乎没有任何卡感。OBS真是一个很强大的推流软件(关于OBS在后续文章会介绍)。后面的流媒体直播服务器,就是因为OBS而起。

闲话少说,下面进入正题吧。

既然有了推流软件,那么我们可以自己搭建一个私人的RTMP流媒体服务器,把屏幕录像转化成视频流推送到流媒体服务器,然后其他用户访问流媒体服务器就能看到直播了。用户不仅可以通过网页访问流媒体服务器,还可以通过播放器(potplayer、VLC)、APP等访问。流程如下图:

直播效果图(纯洁脸):

既然是直播服务器,那么大带宽、大流量是肯定的。如果只是偶尔私人直播,给几个朋友看看,那一般国外的云服务器也可满足要求(后续会更新本地搭建直播服务器,利用宽带直播)。但是要注意的是,你本地电脑到国外服务器如果延迟大的话,意味着丢包率也高。丢包率高了别人看你直播就会比较卡。如果使用国内云服务器,1M带宽….不用想了。所以这里我还是推荐香港的云服务器,到内地还是很稳定的。这篇博客就是以阿里云香港服务器30M带宽,1000G月流量为例。

环境:centos7系统,阿里云香港服务器

一、安装Nginx

流媒体服务器只需要Nginx+Nginx的rtmp模块就行了。首先ssh连接到服务器,通过LNMP一键安装包单独安装Nginx:

yum -y install wget

cd /root

wget -c http://soft.vpser.net/lnmp/lnmp1.5.tar.gz && tar zxf lnmp1.5.tar.gz

cd lnmp1.5./install.sh nginx

按任意键开始安装。等待安装完成后进入下一步。如果安装完成提示失败那么重新安装一次就行了

二、安装rtmp模块

  1. 下载nginx-rtmp-module:

yum -y install git

cd /root/lnmp1.5

git clone https://github.com/arut/nginx-rtmp-module.git

完成后在lnmp1.5目录下可以看到这个文件夹了:

  1. 使用如下命令查看编译安装参数:

nginx -V

可以看到:

在configure arguments后面就是安装参数了。把这个参数复制下来

  1. 重新编译安装nginx,在参数后面添加模块:

cd /root/lnmp1.5/src

tar zxvf nginx-1.14.0.tar.gz

cd nginx-1.14.0./configure –user=www –group=www –prefix=/usr/local/nginx –with-http_stub_status_module –with-http_ssl_module –with-http_v2_module –with-http_gzip_static_module –with-http_sub_module –with-stream –with-stream_ssl_module –with-openssl=/root/lnmp1.5/src/openssl-1.0.2o –add-module=../nginx-rtmp-module

make && make install

装完之后nginx就支持rtmp流媒体了。

三、配置推流地址以及播放页面

  1. 配置推流地址:

vim /usr/local/nginx/nginx.conf

如图,在events和http之间加入如图内容:

rtmp {

    server{

        listen 6666; #推流端口 可自定义

        application rtmplive{

             live on;

            max_connections 5;  #最大观看人数

         }

         application hls{

             live on;

            hls on;

            #wait_key on;

             hls_path /www/rtmp/hls;  #录像文件存储位置

             #hls_playlist_length 60s;

            hls_fragment 1s;  #视频切片大小

        }

     }

 }

  1. 配置播放页面

vim /usr/local/nginx/conf/vhost/你的域名.conf

加入如下内容:

  server {

       listen       80;

       server_name  你的域名;

       #charset koi8-r;

       #access_log  logs/host.access.log  main;

       location / {

          root   /www/rtmp;  #网站目录

          index  index.html index.htm;

      }

     #error_page  404              /404.html;

     # redirect server error pages to the static page /50x.html      #     error_page   500 502 503 504  /50x.html;      location = /50x.html {      root   html;     }  }

到这一步流媒体服务器就搭建完成了。下面再送一个web直播页面,很好用,强烈推荐:http://www.cuplayer.com/player/PlayerCodeCourse/2014/12171698.html

后面的以后再说

手把手教你搭建一个直播服务器(Nginx+Rtsp)

IOT_SHUN 2018-03-25 16:28:30  29479  收藏 15

分类专栏: 移动端/客户端音视频入门

版权

本教程感谢慕课网

常用工具:

                ffmpeg音视频编解码 

                ffplay音视频播放工具

搭建流媒体服务器

                准备流媒体服务器linux

                准备并安装Nginx服务

                配置RTMP并启动Nginx服务

1.linxu安装Nginx和rtmp 

1.先下载安装  nginx 和 nginx-rtmp 编译依赖工具

sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev

2. 创建一个工作目录,并切换到工作目录

mkdir ~/working

cd ~/working

3. 下载 nginx 和 nginx-rtmp源码

wget http://nginx.org/download/nginx-1.7.5.tar.gz

wget https://github.com/arut/nginx-rtmp-module/archive/master.zip

4. 安装unzip工具,解压下载的安装包

sudo apt-get install unzip

5.解压 nginx 和 nginx-rtmp安装包

tar -zxvf nginx-1.7.5.tar.gz

unzip master.zip

6. 切换到 nginx-目录

cd nginx-1.7.5

7.添加 nginx-rtmp 模板编译到 nginx

./configure –with-http_ssl_module –add-module=../nginx-rtmp-module-master

8.编译安装 

make

sudo make install

9. 安装nginx init 脚本

sudo wget https://raw.github.com/JasonGiedymin/nginx-init-ubuntu/master/nginx -O /etc/init.d/nginx

sudo chmod +x /etc/init.d/nginx

sudo update-rc.d nginx defaults

10. 启动和停止nginx 服务,生成配置文件

sudo service nginx start

sudo service nginx stop

11. 安装 FFmpeg

sudo apt-add-repository ppa:jon-severinsson/ffmpeg

sudo apt-get update

sudo apt-get install ffmpeg

原文PPA不可用,源码安装参见

http://blog.csdn.net/redstarofsleep/article/details/45092145

12. 配置 nginx-rtmp 服务器

打开 /usr/local/nginx/conf/nginx.conf

在末尾添加如下 配置

rtmp {    

    server {    

        listen 1935;  #监听的端口  

        chunk_size 4000;    

        application hls {  #rtmp推流请求路径  

            live on;    

            hls on;    

            hls_path /usr/local/nginx/html/hls;    

            hls_fragment 5s;    

        }    

    }    

}

13. 保存上面配置文件,然后重新启动nginx服务

sudo service nginx restart

14. 如果你使用了防火墙,请允许端口 tcp 1935

    用netstat -an | grep 1935

出现以下信息代表则代表启动nginx/rtmp成功

        netstat -an| grep 1935

        tcp        0      0 0.0.0.0:1935            0.0.0.0:*               LISTEN 

16: 使用 客户端,使用 rtmp协议进行视频实时采集

ffmpeg直播命令:

        推流:ffmpeg -re -i 1.mp4 -c copy -f flv rtmp://xxx.xxx.xxx.xxx:1935/hls/1.mp4

        拉流:ffmpeg -i  rtmp://xxx.xxx.xxx.xxx/1.mp4 -c copy dump.flv

        播放音视频:ffplay rtmp://xxx.xxx.xxx.xxx/hls

17.下载一个OBS(视频直播客户端)

(1)配置url

    (2)推流

    (3)服务器播放

                    ffplay rtmp://xxx.xxx.xxx.xxx/hls

18: 关于 nginx-rtmp 更多配置

https://github.com/arut/nginx-rtmp-module/wiki/Directives

flashplay播放器地址

http://bbs.chinaffmpeg.com/1.swf

————————————————

版权声明:本文为CSDN博主「IOT_SHUN」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/IOT_SHUN/java/article/details/79684856

解决使用Nginx错误 Failed to load resource: net::ERR_INCOMPLETE_CHUNKED_ENCODING问题

Failed to load resource: net::ERR_INCOMPLETE_CHUNKED_ENCODING问题

 

先说解决办法:直接删除Nginx缓存文件即可;

 

问题描述:

使用Nginx代理的服务,一直使用正常,突然昨天就访问不了了;通过IP访问和端口能正常访问。

 

原本以为是请求头文件过大导致资源未加载完问题;然后修改了Tomcat中配置中的请求头文件,

 

在Tomcat的conf中把server.xml中增加maxHttpHeaderSize的字段或者是把maxHttpHeaderSize的数值调大,如下:

 

 

 

修改后依然无效,浏览器中还是报如下错误:

 

 

 

以上问题原因:

当代理文件大小超过配置的proxy_temp_file_write_size值时,nginx会将文件写入到临时目录下(默认为/proxy_temp)。

 

如果nginx中/proxy_temp过大或者没有权限,就写不进去。

 

解决一:

直接删除Nginx缓存文件,应该就可以访问了:# rm -rf  /usr/local/nginx/proxy_temp

 

防止缓存文件过多,设置Nginx的缓存过期时间,如下:# vim /usr/local/nginx/conf/nginx.conf

 

server

{

listen 80;

server_name mjj.jybb.me; #主机名

location / {

proxy_cache cache_one;

proxy_cache_valid 200 304 3d; #正常状态缓存时间3天,按实际情况修改

proxy_cache_key $host$uri$is_args$args;

proxy_pass http://www.baidu.com/; #反代的网站

proxy_redirect off;

proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

expires 10d; #默认10天缓存

access_log /home/wwwlogs/mjj.log access; #日志文件,不开启日志请改为access_log off;

}

}

重启一下nginx,访问一下网站

 

解决二:

调整/proxy_temp权限为配置nginx的那个用户。

 

chown -R www:www /usr/local/nginx/proxy_temp