登录
    Technology changes quickly but people's minds change slowly.

直播调研-流程分析

技术宅 破玉 1308次浏览 0个评论

流程分析

    1. 选取合适的厂商服务,比如腾讯、阿里的直播服务进行搭建。
    2. 配置好厂商服务后,根据厂商的服务编写后端代码,生成推流地址和播放地址
    3. 前端或app创建房间请求后端,后端根据房间生成推流地址,将推流返给前端,前端或app开始推流,后端生成数据入库,比如播放地址
    4. 播放列表,前端根据后端的播放地址播放直播。

云服务搭建(以腾讯云为例)

步骤:
1) 注册 腾讯云账号,并完成 实名认证。
2) 进入 腾讯云直播服务开通页,勾选同意《腾讯云服务协议》,并单击【申请开通】即可开通云直播服务,购买相关套餐
3) 需要自己的域名来(推流和播放)域名可以以CNAME的形式来配置,默认提供推流域名,自己需要设置播放域名(域名需要通过备案)https://cloud.tencent.com/document/product/267/20381
选择云直播控制台的 【域名管理】>【添加域名】添加您已备案后的推流域名和播放域名。
将域名解析地址 CNAME 到云直播控制台的域名列表中对应域名的 CNAME 地址。以 DNS 服务商为腾讯云为例,添加 CNAME 记录操作步骤。
4) 获取推流地址 (需要后端写程序辅助),地址生成器
5) 直播推流 (将生成好的推流地址输入到对应的推流软件中)
6) 获取播放地址
7) 推流成功后,选择【流管理】>【在线流】,查看推流地址状态,单击【测试】在线播放观看。
8) 选择【辅助工具】>【地址生成器】 获取播放地址

后端搭建

防盗链生成工具类:

package com.xyl.live.utils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

/**
 * 直播工具类
 */
public  class LiveUtils {
    private static final char[] DIGITS_LOWER =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    /*
     * KEY+ streamName + txTime
     */
    public static String getSafeUrl(String key, String streamName, long txTime) {
        String input = new StringBuilder().
                append(key).
                append(streamName).
                append(Long.toHexString(txTime).toUpperCase()).toString();

        String txSecret = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            txSecret  = byteArrayToHexString(
                    messageDigest.digest(input.getBytes("UTF-8")));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return txSecret == null ? "" :
                new StringBuilder().
                        append("txSecret=").
                        append(txSecret).
                        append("&").
                        append("txTime=").
                        append(Long.toHexString(txTime).toUpperCase()).
                        toString();
    }

    /**
     * 字节数组转字符串
     * @param data
     * @return
     */
    private static String byteArrayToHexString(byte[] data) {
        char[] out = new char[data.length << 1];

        for (int i = 0, j = 0; i >> 4];
            out[j++] = DIGITS_LOWER[0x0F & data[i]];
        }
        return new String(out);
    }

    /**
     * generate uuid
     * @return
     */
    public static String createUUID(){
        return UUID.randomUUID().toString().replace("-","").toLowerCase();
    }
}

服务类:

package com.xyl.live.service;

import com.xyl.live.utils.LiveUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class MagicLiveService {
    @Value("${tencent.live.pushkey}")
    private String pushKey;
    @Value("${tencent.live.pushdomain}")
    private String pushDomain;
    @Value("${tencent.live.broadcastkey}")
    private String broadcastKey;
    @Value("${tencent.live.broadcastdomain}")
    private String broadcastDomain;
    @Value("${tencent.live.broadcastexpiretime}")
    private Long broadcastExpireTime;
    // 过期时间暂时设置为一天
    private long pushtime=60*60*24;
    //播放过期时间
    private long pulltime=60*60*24;

    // 获取直播推流地址
    public String getPushUrl(String streamName){
       return "rtmp://"+pushDomain+"/live/"+streamName+"?"+ LiveUtils.getSafeUrl(pushKey,streamName,pushtime+(System.currentTimeMillis()/1000));
    }

    // 获取直播拉流播放地址
    public String getPullUrl(String streamName){
       // return "http://"+broadcastDomain+"/live/"+streamName+".m3u8?"+ LiveUtils.getSafeUrl(pushKey,streamName,pulltime+broadcastExpireTime);
        return "http://"+broadcastDomain+"/live/"+streamName+".m3u8?"+ LiveUtils.getSafeUrl(broadcastKey,streamName,pulltime+(System.currentTimeMillis()/1000)-broadcastExpireTime);
    }
}

yml 文件配置:

# 推流防盗链
tencent:
  live:
    pushkey: 12345678
    pushdomain: xxxxx.hello.cloud.com
    broadcastkey: 123456789012
    broadcastdomain: hello.live.cm
    broadcastexpiretime: 3600

再编写对应的接口就可以了。

app 端搭建

采用 uni-app 的形式搭建app(参考: https://ext.dcloud.net.cn/plugin?id=226)

<template>
    <view class="content">		
		<view class="butlist">
			<view class="buticon" @click="startPusher">
				<view class="x_f"></view>
				<view :class="begin==true?'givebegin':'give'" >开始</view>
				<view class="pulse" v-if="begin"></view>
			</view>
			<view>
				推流地址:<input placeholder="请输入推流地址" v-model="livepushurl"/>
			</view>
		</view>
    </view>
</template>

<script>
    export default {
		data() {
			return {
			    begin:false,//开始录制
				currentWebview:null,
				pusher:null,
				livepushurl:'rtmp://xxxx.live.aaaa.com/live/abc?txSecret=1a2333f7dfgh49013e34691a7df1a3984af&txTime=5E44DD1C'
			}
		},
        onLoad(res) {
	        this.getwebview()//获取webview
        },
		methods: {
			/**
			 * 获取当前显示的webview
			 */
			getwebview(){
				var pages = getCurrentPages();
				var page = pages[pages.length - 1];
				// #ifdef APP-PLUS
				var getcurrentWebview = page.$getAppWebview();
				console.log(this.pages)
				console.log(this.page)
				console.log(JSON.stringify(page.$getAppWebview()))
				this.currentWebview=getcurrentWebview;
				// #endif
				this.plusReady()//创建LivePusher对象
			},

			/**
			 * 创建LivePusher对象 即推流对象
			 */ 
			plusReady(){				
				// 创建直播推流控件
				this.pusher =new plus.video.LivePusher('pusher',{
					url:'',
					top:'0',
					left:'0px',
					width: '100%',
					height:  uni.getSystemInfoSync().windowHeight-155 + 'px',				
					position: 'absolute',//static静态布局模式,如果页面存在滚动条则随窗口内容滚动,absolute绝对布局模式,如果页面存在滚动条不随窗口内容滚动; 默认值为"static"
					beauty:'1',//美颜 0-off  1-on  
					whiteness:'0',//0、1、2、3、4、5,0不使用美白,值越大美白程度越大。
					aspect:'9:16',					
 				});
				this.currentWebview.append(this.pusher);
				// 监听状态变化事件  
				this.pusher.addEventListener('statechange',(e)=>{
					console.log('statechange: '+JSON.stringify(e));
				}, false);
			},			
			// 开始推流
			startPusher(){
				this.beginlivepush()
			},
			beginlivepush() {
				if(this.begin==false){//未开启推流
					this.begin=true;//显示录制动画
					// 设置推流服务器  ***此处需要通过ajax向后端获取
					this.pusher.setOptions({
						url:this.livepushurl //推流地址********************************* 此处设置推流地址
					});
					this.pusher.start();//推流开启
					uni.showToast({
						title: '开始录制',
						icon:'none',
						duration: 2000,					 
					});
				}else{
						this.begin=false;//关闭录制动画
						this.pusher.pause();;//暂停推流
						uni.showToast({
							title: '暂停录制',
							icon:'none',
							duration: 2000,					 
						});
					}

				}
			}
		}
</script>

<style>
	.content{
		background: #000;
		overflow: hidden;
	}
	.butlist{
		height: 140upx;
		position: absolute;
		bottom: 0;
		display: flex;
		width: 100%;
		justify-content: space-around;
	    padding-top: 20upx;
		border-top: 1px solid #fff;
	}
	.buticon{
		height: 120upx;
		width: 120upx;
		color: #fff;
		position: relative;
		text-align: center;
		margin-bottom: 20upx;
	}
	.buticon image{
		height: 64upx;
		width: 64upx;
	}
	.buticon .mar10{
		margin-top: -20upx;
	}
	.martp10{
		margin-top: 10upx;

	}
	.give {
		width: 90upx;
		height: 90upx;
		background: #F44336;	
		border-radius: 50%;
		box-shadow: 0 0 22upx 0 rgb(252, 94, 20);
	 	 position: absolute; 
		left:15upx;
		top:15upx; 
		    font-size: 44upx;
    line-height: 90upx;
	}
	.givebegin {
		width: 60upx;
		height: 60upx;
		background: #F44336;	
		border-radius: 20%;
		box-shadow: 0 0 22upx 0 rgb(252, 94, 20);
	 	 position: absolute; 
		left:30upx;
		top:30upx; 
	}
	.x_f{
		/* border: 6upx solid #F44336; */
		width: 120upx;
		height: 120upx;
		background: #fff;
		border-radius: 50%;
		position: absolute;
		text-align: center;
		top:0;
		left: 0;
	  box-shadow: 0 0 28upx 0 rgb(251, 99, 24);
	}
	
	/* 产生动画(向外扩散变大)的圆圈  */
	.pulse {
		width: 160upx;
		height: 160upx;
		position: absolute;
	    border: 12upx solid #F44336;
	    border-radius: 100%;
	    z-index: 1;
	    opacity: 0;
	    -webkit-animation: warn 2s ease-out;
	    animation: warn 2s ease-out;
	    -webkit-animation-iteration-count: infinite;
	    animation-iteration-count: infinite;
	    left: -28upx;
	    top: -28upx;
	}
		
	
	/**
	 * 动画
	 */
	@keyframes warn {
	0% {
		transform: scale(0);
		opacity: 0.0;
	}
	25% {
		transform: scale(0);
		opacity: 0.1;
	}
	50% {
		transform: scale(0.1);
		opacity: 0.3;
	}
	75% {
		transform: scale(0.5);
		opacity: 0.5;
	}
	100% {
		transform: scale(1);
		opacity: 0.0;
	}
}
	 
</style>

web端播放(vue)

技术是 vue-video-player+videojs-contrib-hls 插件

<template>
<div class="container">
     <video-player  class="video-player vjs-custom-skin"
                     ref="videoPlayer"
                     :playsinline="true"
                     :options="playerOptions"
      >
      </video-player>
</div>
</template>
<script>
import 'video.js/dist/video-js.css'
import { videoPlayer } from 'vue-video-player'
import'videojs-contrib-hls';
export default {
    data(){
        return {
        playerOptions:{
        autoplay: false, 
        muted: false, 
        loop: false, 
        preload: 'auto', 
        language: 'zh-CN',
        aspectRatio: '16:9',
        fluid: true, 
        sources: [{
          type: "application/x-mpegURL",
          src: "http://live.xxx.com/live/dfg.m3u8?txSecret=48ddddcddfs7adddd12c2c51ff978160e522&txTime=5E44DF78" //你的视频地址(必填)
        }],
        width: document.documentElement.clientWidth,
        notSupportedMessage: '此视频暂无法播放,请稍后再试', 
      }
        }
    },
    components: {
    videoPlayer
  },
  methods: {
      
  },
  
}
</script>
<style type="text/css" scoped>
  .container {
    background-color: #efefef;
    height: 30%;
    width:30%;
  }
</style>


华裳绕指柔, 版权所有丨如未注明 , 均为原创|转载请注明直播调研-流程分析
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址