[Programming Recipe Series 1] Teach you step by step how to transform old mobile phones into face recognition monitoring

Tip: After the article is written, the table of contents can be automatically generated. For how to generate it, please refer to the help document on the right.


Preface

Recipe programming, follow the recipe, and you can achieve the same effect as me.
All roads lead to Rome. My method is not necessarily the best. It is just a personal technical summary for reference only.


1. Prepare ingredients

1. Download livego [Responsible for configuring the local live broadcast environment]
2. Download uniapp [Responsible for the use of live streaming on mobile phones]
3. Download Anaconda [Responsible for python Opencv package management]
4. Download OBS Studio [Responsible for testing live streaming] [ Optional]
5. Download VLC media player [responsible for testing whether the streaming of the live broadcast environment is successful] [optional]

2. Configure the live broadcast environment

1. Download: livego compilation package https://github.com/gwuhaolin/livego/releases

Choose the appropriate one according to your working environment. The win environment is used by default here.

Insert image description here
Usage of win version:
After downloading:
Insert image description here
1. Open: livego.exe program

You will see the program running

time="2022-06-15T12:56:11+08:00" level=warning msg="open livego.yaml: The system cannot find the file specified."
time="2022-06-15T12:56:11+08:00" level=info msg="Using default config"
time="2022-06-15T12:56:11+08:00" level=info msg="\n     _     _            ____       \n    | |   (_)_   _____ / ___| ___  \n    | |   | \\ \\ / / _ \\ |  _ / _ \\ \n    | |___| |\\ V /  __/ |_| | (_) |\n    |_____|_| \\_/ \\___|\\____|\\___/ \n        version: master\n\t"
time="2022-06-15T12:56:11+08:00" level=info msg="HLS listen On :7002"
time="2022-06-15T12:56:11+08:00" level=info msg="HTTP-API listen On :8090"
time="2022-06-15T12:56:11+08:00" level=info msg="HLS server enable...."
time="2022-06-15T12:56:11+08:00" level=info msg="HTTP-FLV listen On :7001"
time="2022-06-15T12:56:11+08:00" level=info msg="RTMP Listen On :1935"

It means the operation is successful. Please refer to the documentation.
Insert image description here2. Open the browser and enter the url (" http://localhost:8090/control/get?room=movie") .
You will see
{"status":200,"data":"rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk"}
that the environment has been successfully run.

1. Test whether the live broadcast environment is effective

Download
OBS Studio download [responsible for testing the live streaming] [optional]
VLC media player download [responsible for testing whether the live streaming environment is successful] [optional]
any .mp4 video

1. Open the OBS software
Insert image description here
2. Open the settings page and enter the push URL and key value (the key value, which is the value when you first open the browser). Push URL:
{"status":200,"data":"rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk"}
If there are no special requirements, you can enter it directly: Enterrtmp://localhost:1935/live/ the default push URL address.
Remember to save the corresponding value
Insert image description here
3. Select the mp4 file to start streaming

Insert image description here
Insert image description here
Indicates that the push stream has been successfully
tested to see if the live broadcast is valid.
Open VLC media player to download [responsible for testing whether the pull stream of the live broadcast environment is successful] [optional]
enter the pull address.
Insert image description here
Insert image description here

Insert image description here
At this point, it means that the basic functions of live push and pull streaming have been implemented.

3. Configure the Anaconda environment (you can skip it if you know how)

Download Anaconda download [responsible for python Opencv package management]
注意* 下载Anaconda不是必须的,只是因为纯粹为了好管理python的包,Opencv才是这次做菜关键的目的

1. After installing Anaconda, open Anaconda Navigator (conda).exe

Insert image description here
2.PyCharm associates the Anaconda environment
and opens PyCharm
Insert image description here
Insert image description here
Insert image description here

Test whether opencv is successful (example):

import cv2 as cv
#读取图片
img = cv.imread('1.jpeg')
#暂示图片
cv.imshow('ceshi',img)
#关闭窗口
cv.waitKey(0)
cv.destroyAllWindows()

You can see that the picture represents that opencv is valid
Insert image description here


4. Uniapp part - write an app that can live broadcast and install it on your mobile phone

uni-app writes an app that can push streams and install it on the mobile phone. Here is the documentation:
https://uniapp.dcloud.io/api/media/live-player-context.html
Insert image description here

uniapp part: First create a homepage, then jump to create a live broadcast, and then jump to the live broadcast room to push the stream.
The part responsible for pulling the stream is to use the python code in the following part to fill in the stream address and run it.

uniapp: mobile app homepage part


<template>
	<view class="content">
		<button type="default" @click="createlive()">创建我的直播间</button>
	</view>
</template>

<script>
	export default {
    
    
		data() {
    
    
			return {
    
    	
			}
		},
		onLoad() {
    
    
		},
		methods: {
    
    
			createlive(){
    
    
				uni.redirectTo({
    
    
					url:'../../components/createlive/createlive'
				})
			}
		}
	}
</script>
<style lang="scss">
</style>

Create a live broadcast room

<template>
	<view>
		<live-pusher
		id='livePusher'
		ref="livePusher" 
		class="livePusher" 
		:url="url"
		:mode="mode" 
		:enable-camera="enableCamera" 
		:auto-focus="true" 
		:device-position="position"
		:beauty="beauty" 
		:whiteness="whiteness"
		aspect="9:16" 
		@statechange="statechange" 
		@netstatus="netstatus" 
		@error = "error"
		:style="'height: '+windowHeight+'px;'"
		style="width: 750rpx;"></live-pusher>
		
		
		<view v-if="showBars" >
			
			<view style="position: fixed;left: 0;right: 0;height: 500rpx;" :style="'top:'+statusBarHeight+'px;'">
				<view class="flex align-center justify-center" style="width: 90rpx;height: 90rpx;" @click="back">
					<text class="iconfont text-white">X</text>
				</view>
				
				<view class="position-absolute rounded p-2 flex align-center" style="left: 90rpx;right: 100rpx;height: 160rpx;background-color: rgba(0,0,0,0.2);">
					<view style="height: 120rpx;width: 120rpx;" class="position-relative rounded" @click="chooseCover">
						<image :src="form.cover || '/static/gift/3.png'" style="height: 120rpx;width: 120rpx;"></image>
						<text class="text-white position-absolute font" style="left: 0;right: 0;bottom: 0;">更换封面</text>
					</view>
					<view class="flex-1 ml-2">
						<input type="text" v-model="form.title" placeholder="请输入直播标题" class="mb-2"/>
						<!-- <text class="text-white font">#请选择分类</text> -->
					</view>
				</view>
				
				<view class="position-absolute right-0 flex flex-column " style="width: 100rpx;" >
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="switchCamera">
						<text class="iconfont text-white mb-1">切换  镜头</text>
						<!-- <text class="text-white font">翻转</text> -->
					</view>
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="openPopup('mode')">
						<text class="iconfont text-white mb-1">设置  画质</text>
						<!-- <text class="text-white font">画质</text> -->
					</view>
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="openPopup('beauty')">
						<text class="iconfont text-white mb-1">设置  美颜</text>
						<!-- <text class="text-white font">美颜</text> -->
					</view>
					<view style="height: 120rpx;width: 100rpx;" class="flex flex-column align-center justify-center" @click="openPopup('whiteness')">
						<text class="iconfont text-white mb-1">设置  美白</text>
						<!-- <text class="text-white font">美白</text> -->
					</view>
				</view>
				
			</view>
			
			
			<view class="position-fixed bg-main flex align-center justify-center rounded-circle" style="left: 100rpx;right: 100rpx;bottom: 100rpx;height: 120rpx;" @click="openLiveRoom">
				<text class="text-white font-md">开始视频直播</text>
			</view>
			
			
			<uni-popup type="bottom" ref="popup">
				<view class="bg-white">
					<view class="flex align-center justify-center border-bottom" style="height: 90rpx;">
						<text class="font-md">{
    
    {
    
    popupTitle}}</text>
					</view>
					<!-- 画质选择 -->
					<view v-if="popupType === 'mode'">
						<view class="flex align-center justify-center py-2" v-for="(item,index) in modeList" :key="index" :class="mode === item.type ? 'bg-main' : ''" @click="chooseMode(item)">
							<text class="font-md" :class="mode === item.type ? 'text-white' : ''">{
    
    {
    
    item.desc}}</text>
						</view>
					</view>
					<!-- 美颜 -->
					<view v-else-if="popupType === 'beauty'">
						<slider :min="0" :max="9" :step="1" :value="beauty" :block-size="18" show-value @change="handleSliderChange"/>
					</view>
					<!-- 美白 -->
					<view v-else>
						<slider :min="0" :max="9" :step="1" :value="whiteness" :block-size="18" show-value @change="handleSliderChange"/>
					</view>
					<view class="f-divider"></view>
					<view  class="flex align-center justify-center"
					style="height: 90rpx;" hover-class="bg-light"
					@click="closePopup">
						<text class="font-md">取消</text>
					</view>
				</view>
			</uni-popup>
		</view>
		
	</view>
</template>

<script>
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	export default {
    
    
		components: {
    
    
			uniPopup
		},
		data() {
    
    
			return {
    
    
				url:"",
				mode:"SD",
				enableCamera:true,
				position:"back",
				beauty:0,
				whiteness:0,
				windowHeight:0,
				context:null,
				statusBarHeight:0,
				modeList:[{
    
    
					type:"SD",
					desc:"标清"
				},{
    
    
					type:"HD",
					desc:"高清"
				},{
    
    
					type:"FHD",
					desc:"超清"
				}],
				popupType:"mode",
				showBars:true,
				form:{
    
    
					title:"",
					cover:"https://profile-avatar.csdnimg.cn/c213bed6000f404fa2dd41a6ed271fe3_qq_40750573.jpg!2"
				}
			}
		},
		onLoad() {
    
    
			let res = uni.getSystemInfoSync()
			this.windowHeight = res.windowHeight
			this.statusBarHeight = res.statusBarHeight
		},
		computed: {
    
    
			popupTitle() {
    
    
				let o = {
    
    
					mode:"画质",
					beauty:"美颜",
					whiteness:"美白",
				}
				return o[this.popupType]
			}
		},
		onReady() {
    
    
			this.context = uni.createLivePusherContext('livePusher', this)
			this.startPreview()
		},
		onBackPress() {
    
    
			this.showBars = false
		},
		methods: {
    
    
			chooseCover(){
    
    
				uni.chooseImage({
    
    
					count:1,
					success: (res) => {
    
    
						$H.upload('/upload',{
    
    
							filePath:res.tempFilePaths[0]
						},(p)=>{
    
    
							console.log(p);
						}).then(res=>{
    
    
							this.form.cover = $C.imageUrl + res.url
						})
					}
				})
			},
			back(){
    
    
				uni.reLaunch({
    
    
				    url: '/pages/index/index'
				});
			},
			// 画质选择
			chooseMode(item){
    
    
				this.mode = item.type
				uni.showToast({
    
    
					title: '画质切换为' + item.desc,
					icon: 'none'
				});
				this.$refs.popup.close()
			},
			openPopup(type){
    
    
				this.popupType = type
				this.$refs.popup.open()
			},
			closePopup(){
    
    
				this.$refs.popup.close()
			},
			// 切换摄像头
			switchCamera(){
    
    
				this.context.switchCamera({
    
    
					success:(e)=>{
    
    
						this.position = this.position === 'back' ? 'front' : 'back'
					}
				})
			},
			// 开启预览
			startPreview(){
    
    
				this.context.startPreview({
    
    
					success:(e)=>{
    
    
						console.log(e);
					}
				})
			},
			// 直播状态变化
			statechange(e){
    
    
				console.log(e);
			},
			// 直播网络变化
			netstatus(e){
    
    
				console.log(e);
			},
			error(e){
    
    
				console.log(e);
			},
			handleSliderChange(e){
    
    
				this[this.popupType] = e.detail.value
			},
			openLiveRoom(){
    
    
					let options = {
    
    
						mode:this.mode,
						position:this.position,
						beauty:this.beauty,
						whiteness:this.whiteness
					}  
					uni.redirectTo({
    
    
						url: '../liveing/liveing'
					});
			}
		}
	}
</script>

<style>

</style>

Start live broadcast
This is the push address: rtmp://localhost:1935/live/rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk(for reference only)

<template>
	<view class="page">
		
		<live-pusher
		id='livePusher'
		ref="livePusher" 
		class="livePusher" 
		:url="mysrc"
		:mode="mode" 
		:muted="false"
		:enable-camera="enableCamera" 
		:auto-focus="true" 
		:device-position="position"
		:beauty="beauty" 
		:whiteness="whiteness"
		aspect="9:16" 
		:orientation="fangxaing"
		:local-mirror='yulan'
		:enable-mic='maikefeng'
		:min-bitrate='malv'
		:audio-quality='hezi'
		:audio-volume-type='yinliang'
		:enable-agc='true'
		:enable-ans='true'
		audio-reverb-type=6	
		@statechange="statechange" 
		@netstatus="netstatus" 
		@error = "error"
		:style="'height: '+windowHeight+'px;'"
		style="width: 750rpx;"></live-pusher>
	
		<!-- 头部 -->
		<view style="position: fixed;left: 0;right: 0;" :style="'top:'+statusBarHeight+'px'">
			<!-- 个人信息|观看详细信息 -->
			<view style="height: 80rpx;" class="px-2 flex justify-between align-center">
				<view style="width: 325rpx;background-color: rgba(0,0,0,0.4);" class="flex rounded-circle">
					<view class="p">
						<image :src="detail.user.avatar || '/static/tabbar/min.png'" style="width: 70rpx;height: 70rpx;" class="rounded-circle"></image>
					</view>
					<view class="flex-1 flex flex-column justify-center">
						<text class="text-white font">{
    
    {
    
     detail.user.nickname || detail.user.username }}</text>
						<text class="text-white font-sm">{
    
    {
    
     detail.look_count }}</text>
					</view>
					<view class="p">
						<view class="rounded-circle flex align-center justify-center bg-danger" style="width: 70rpx;height: 70rpx;">
							<text class="text-white">+</text>
						</view>
					</view>
				</view>
				
				<view style="width: 325rpx;background-color: rgba(0,0,0,0.4);" class="flex rounded-circle">
					<scroll-view scroll-x="true" class="flex-1 flex">
						<view class="p" v-for="(item,index) in list" :key="index">
							<image :src="item.avatar || '/static/tabbar/min.png'" style="width: 70rpx;height: 70rpx;" class="rounded-circle"></image>
						</view>
					</scroll-view>
					<view class="p">
						<view class="rounded-circle flex align-center justify-center bg-danger" style="width: 70rpx;height: 70rpx;">
							<text class="text-white font-sm">{
    
    {
    
     list.length }}</text>
						</view>
					</view>
				</view>
			</view>
			<!-- 金币 -->
			<view style="height: 80rpx;" class="px-2 my-2"  >
				<view style="width: 325rpx;background-color: rgba(0,0,0,0.4);" class="flex rounded-circle align-center">
					<view class="p">
						<text class="text-warning">金币</text>
					</view>
					<view class="flex-1 flex flex-column justify-center">
						<text class="text-white font">{
    
    {
    
     detail.coin }}</text>
					</view>
				</view>
			</view>
			<!-- 收到礼物 -->
		<!-- 	<f-gift ref="gift"></f-gift> -->
		</view>
		
		<!-- 弹幕 -->
		<view style="position: fixed;bottom: 120rpx;left: 0;right: 0;">
			<scroll-view scroll-y="true" style="width: 520rpx;height: 300rpx;" scroll-with-animation class="pl-3" :scroll-into-view="scrollInToView">
				<view :id="'danmu'+item.id" class="flex justify-start align-start rounded p-2 mb-2" style="background-color: rgba(255,255,255,0.125);" v-for="(item,index) in listdan" :key="index">
					<text class="font-md text-danger">{
    
    {
    
    item.name}}</text>
					<text class="font-md text-white">{
    
    {
    
    item.content}}</text>
				</view>
			</scroll-view>
		</view>
		
		<!-- 底部 -->
		<view style="position: fixed;left: 0;bottom: 0;right: 0;height: 120rpx;" class="flex align-center justify-between">
			
			<view class="flex-1 flex flex-column align-center justify-center" v-for="(item,index) in btns" :key="index" @click="handleBottomEvent(item)">
				<text class="iconfont text-white mb-1">{
    
    {
    
    item.icon}}</text>
				<text class="text-white font">{
    
    {
    
    item.name}}</text>
			</view>
		</view>
		
		<uni-popup type="bottom" ref="popup">
			<view class="bg-white">
				<view class="flex align-center justify-center border-bottom" style="height: 90rpx;">
					<text class="font-md">{
    
    {
    
    popupTitle}}</text>
				</view>
				<!-- 画质选择 -->
				<view v-if="popupType === 'mode'">
					<view class="flex align-center justify-center py-2" v-for="(item,index) in modeList" :key="index" :class="mode === item.type ? 'bg-main' : ''" @click="chooseMode(item)">
						<text class="font-md" :class="mode === item.type ? 'text-white' : ''">{
    
    {
    
    item.desc}}</text>
					</view>
				</view>
				<!-- 美颜 -->
				<view v-else-if="popupType === 'beauty'">
					<slider :min="0" :max="9" :step="1" :value="beauty" :block-size="18" show-value @change="handleSliderChange"/>
				</view>
				<!-- 美白 -->
				<view v-else-if="popupType === 'whiteness'">
					<slider :min="0" :max="9" :step="1" :value="whiteness" :block-size="18" show-value @change="handleSliderChange"/>
				</view>
				<!-- 更多 -->
				<view v-else class="flex flex-wrap">
					<view class="flex flex-column align-center justify-center" style="width: 150rpx;height: 150rpx;" @click="pauseOrPlay">
						<text class="iconfont mb-1">&#xe611;</text>
						<text class="font">{
    
    {
    
     isPause ? '继续直播' : '暂停直播' }}</text>
					</view>
					<view class="flex flex-column align-center justify-center" style="width: 150rpx;height: 150rpx;" @click="back">
						<text class="iconfont mb-1">直播</text>
						<text class="font">退出</text>
					</view>
				</view>
				
				<view class="f-divider"></view>
				<view  class="flex align-center justify-center"
				style="height: 90rpx;" hover-class="bg-light"
				@click="closePopup">
					<text class="font-md">取消</text>
				</view>
			</view>
		</uni-popup>
		
	</view>
</template>

<script>
	import fGift from '@/components/live/f-gift.vue';
	import uniPopup from '@/components/uni-ui/uni-popup/uni-popup.vue';
	import {
    
     mapState } from 'vuex';
	export default {
    
    
		components: {
    
    
			fGift,
			uniPopup
		},
		data() {
    
    
			return {
    
    
				mysrc:"rtmp://192.168.3.2:1935/live/rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk",//推流地址,也就是OBS设置的那个部分
				statusBarHeight:0,
				content:"",
				gifts:[],
				giftActiveId:0,
				fangxaing:'vertical',//屏幕头方向
				yulan:'auto',
				malv:'200',//最小码率。
				hezi:'low',//声音赫兹,音质
				maikefeng:'true',//是否开启麦克风
				yinliang:'media',//音量类型
				mode:"SD",
				enableCamera:true,
				position:"back",
				beauty:0,
				whiteness:0,
				windowHeight:0,
				context:null,
				scrollInToView:"",
				listdan: [],
				modeList:[{
    
    
					type:"SD",
					desc:"标清"
				},{
    
    
					type:"HD",
					desc:"高清"
				},{
    
    
					type:"FHD",
					desc:"超清"
				}],
				popupType:"mode",
				
				btns:[{
    
    
					name:"翻转",
					icon:"",
					event:"switchCamera"
				},{
    
    
					name:"画质",
					icon:"",
					event:"openPopup",
					params:"mode"
				},{
    
    
					name:"美颜",
					icon:"",
					event:"openPopup",
					params:"beauty"
				},{
    
    
					name:"美白",
					icon:"",
					event:"openPopup",
					params:"whiteness"
				},{
    
    
					name:"更多",
					icon:"",
					event:"openPopup",
					params:"more"
				}],
				detail:{
    
    
					"created_time": "",
					"id": 0,
					"title": "",
					"cover": "",
					"user_id": 0,
					"look_count": 0,
					"coin": 0,
					"key": "",
					"status": 0,
					"userId": 0,
					"user": {
    
    
						"id": 0,
						"username": "XXXXXX的直播间",
						"avatar": "https://profile-avatar.csdnimg.cn/c213bed6000f404fa2dd41a6ed271fe3_qq_40750573.jpg!2"
					}
				},
				sign:"",
				list:[{
    
    'avatar':'http://img2.woyaogexing.com/2022/03/31/4730b59516814a9c9b375492fcde1a69!400x400.jpeg'},{
    
    'avatar':'https://profile-avatar.csdnimg.cn/c213bed6000f404fa2dd41a6ed271fe3_qq_40750573.jpg!2'},{
    
    'avatar':'http://img2.woyaogexing.com/2022/03/31/bd0cc5184f8c4a4f84bfdb69982c633e!400x400.jpeg'},{
    
    'avatar':'http://img2.woyaogexing.com/2022/03/31/f98b46592727466083b857321fd37d90!400x400.jpeg'}],
				
				// 是否开始推流
				isStart:false,
				isPause:false,
				isget:false
			}
		},
		onReady() {
    
    
			this.context = uni.createLivePusherContext('livePusher', this)
			this.startPreview()
			// 开始推流
			 this.start()
		},
		onLoad(e) {
    
    
			let res = uni.getSystemInfoSync()
			this.statusBarHeight = res.statusBarHeight
			this.windowHeight = res.windowHeight
			
			uni.connectSocket({
    
    
			  url: 'ws://192.168.3.28:8282'
			});
			uni.onSocketError(function (res) {
    
    
			  console.log('WebSocket连接打开失败,请检查!');
			});
			uni.onSocketMessage(function (res) {
    
    
			  console.log('收到服务器内容:' + res.data);
			});
			uni.onSocketOpen(function (res) {
    
     
			// 	//发送id绑定workerman给予缓存
				let mgg = {
    
    type:'zhibo_bing','ymuid':1,'uid':0,'iszhubo':1};				 
				uni.sendSocketMessage({
    
    
					data:JSON.stringify(mgg),
				});
			});
			this.workerman();
		},
		mounted() {
    
    
			setInterval(()=>{
    
    
				uni.sendSocketMessage({
    
    
					data:JSON.stringify({
    
    type:'refreshServer'})
				});
			},3000)
		},
		destroyed() {
    
    
		},
		computed: {
    
    
			popupTitle() {
    
    
				let o = {
    
    
					mode:"画质",
					beauty:"美颜",
					whiteness:"美白",
					more:"更多"
				}
				return o[this.popupType]
			},
		},
		onBackPress() {
    
    
			if(!this.isget){
    
    
				this.back()
				return true
			}
		},
		methods: {
    
    
			// 简单粗暴:监听是否掉线
			onSocket(){
    
    
				uni.onSocketClose(function (res) {
    
    
					 console.log('WebSocket 已关闭');
					uni.connectSocket({
    
    
					  url: this.WokerUrl,
					});
					uni.onSocketOpen(function (res) {
    
     
					  console.log("链接成功"); 
					});
				});
			},
			//接收消息
			workerman(){
    
    
				let that = this ;
				let id = 1 ;
				uni.onSocketMessage(function (res) {
    
    
					 var data = JSON.parse(res.data);
					if(data.type == 'zhubo'){
    
    
						var gta = {
    
    
							id:id,
							name:"系统提示",
							content:data.msg
						};
						that.senddata(gta);
					}else if(data.type == 'guanzong'){
    
    
						var gta = {
    
    
							id:id,
							name:data.name,
							content:data.msg
						};
						that.senddata(gta);
					}
					 id++;
				});
			},
			// 发送弹幕
			 senddata(data) {
    
    
				this.listdan.push(data)
				// 置于底部
				this.toBottom()
			},
			toBottom() {
    
    
				setTimeout(()=>{
    
    
					let len = this.listdan.length
					if(len > 0 && this.listdan[len - 1]){
    
    
						this.scrollInToView = 'danmu' + this.listdan[len - 1].id
					}
				},300)
			},
			
			pauseOrPlay(){
    
    
				if(!this.isPause){
    
    
					return uni.showModal({
    
    
						content: '是否要暂停推流?',
						success: (res)=>{
    
    
							if (res.cancel) {
    
    
								return
							}
							this.pause()
						}
					});
				}
				// 继续推流
				this.resume()
			},
			// 退出直播
			back(){
    
    
				uni.showModal({
    
    
					content: '是否要退出直播间?',
					success: (res)=> {
    
    
						if (res.cancel) {
    
    
							return
						}
						this.stop()
						this.isget = true
						uni.reLaunch({
    
    
						    url: '/pages/index/index'
						});
						uni.showToast({
    
    
							title: '退出直播间成功',
							icon: 'none'
						});
					}
				});
			},
			// 开始推流
			start(){
    
    
				this.context.start({
    
    
					success:(e)=>{
    
    
						console.log("开始推流" + JSON.stringify(e));
						this.isStart = true
					}
				})
			},
			// 暂停推流
			pause(){
    
    
				this.context.pause({
    
    
					success:()=>{
    
    
						this.isPause = true
					}
				})
			},
			// 继续推流
			resume(){
    
    
				this.context.resume({
    
    
					success:()=>{
    
    
						this.isPause = false
					}
				})
			},
			stop(){
    
    
				this.context.stop({
    
    
					success:()=>{
    
    
						this.isStart = false
					}
				})
			},
			handleLiveEvent(e){
    
    
				let d = e.data
				switch (e.type){
    
    
					case 'online':
					if(d.action === 'join'){
    
    
						this.list = d.data
					}
						break;
					case 'comment':
					this.$refs.danmu.send({
    
    
						id:d.id,
						name:d.user.name,
						content:d.content
					})
						break;
					case 'gift':
					this.detail.coin += d.gift_coin * d.num
					this.$refs.gift.send(d)
						break;
					default:
						break;
				}
			},
			// 加入或离开直播间
			joinOrLeaveLive(type){
    
    
				if(this.socket && this.token){
    
    
					this.socket.emit( type + 'Live',{
    
    
						live_id:this.detail.id,
						token:this.token
					})
				}
			},
			handleBottomEvent(item){
    
    
				this[item.event](item.params)
			},
			// 画质选择
			chooseMode(item){
    
    
				this.mode = item.type
				uni.showToast({
    
    
					title: '画质切换为' + item.desc,
					icon: 'none'
				});
				this.$refs.popup.close()
			},
			openPopup(type){
    
    
				this.popupType = type
				this.$refs.popup.open()
			},
			closePopup(){
    
    
				this.$refs.popup.close()
			},
			// 切换摄像头
			switchCamera(){
    
    
				this.context.switchCamera({
    
    
					success:(e)=>{
    
    
						this.position = this.position === 'back' ? 'front' : 'back'
					}
				})
			},
			// 开启预览
			startPreview(){
    
    
				this.context.startPreview({
    
    
					success:(e)=>{
    
    
					}
				})
			},
			// 直播状态变化
			statechange(e){
    
    
			},
			// 直播网络变化
			netstatus(e){
    
    
			},
			error(e){
    
    
			},
			handleSliderChange(e){
    
    
				this[this.popupType] = e.detail.value
			},
		}
	}
</script>

<style>
.page{
    
    
	flex: 1;
}
.btn{
    
    
	height: 80rpx;
	border-radius: 100rpx;
	background-color: rgba(255,255,255,0.12);
	align-items: center;
	justify-content: center;
}
.btn-icon{
    
    
	width: 80rpx;
	margin-right: 20rpx;
}
</style>

5. python code part

import cv2 as cv
# 读取照片,转换成灰度图
def face_detect_img(img):
    gray_img = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
    # 将级联算法加载到一个变量中
    #每个人的路径不一样请根据自己的路径填写-----人脸识别
    haar_face_cascade = cv.CascadeClassifier('E:/conda/envs/pycharm/Library/etc/haarcascades/haarcascade_frontalface_alt.xml')
    faces = haar_face_cascade.detectMultiScale(gray_img, scaleFactor=1.1,minNeighbors=3)
    # 在图像中画上矩形框和圆框
    for (x, y, w, h) in faces:
        print(x,y,w,h)
        cv.rectangle(img, (x, y), (x + w, y + h), (156, 114, 32), 2)

    cv.imshow("detect",img)
# 加载图片
#加载直播流
cap = cv.VideoCapture('rtmp://localhost:1935/live/movie')#如果你不想用手机也可以直接使用OBS也是可以的
# cap = cv.VideoCapture('1.mp4')
while True:
    flag,frame=cap.read()
    if not flag:
        break
    face_detect_img(frame)
    if ord('q') == cv.waitKey(10):
        break
cv.destroyAllWindows() # 释放内存
cap.release()

cv.CascadeClassifier('请填写自己的路径')
E:/conda/envs/pycharm/Library/etc/haarcascades/haarcascade_frontalface_alt.xml
is for reference only. We mainly obtain: haarcascade_frontalface_alt.xmluser face recognition usage.
This is the information found online:
Opencv comes with a trained face detection model. Stored in the sources/data/haarcascades folder and sources/data/lbpcascades folder. Several of the .xml files are as follows:
Face detector (default): haarcascade_frontalface_default.xml
Face detector (fast Harr): haarcascade_frontalface_alt2.xml
Face detector (side view): haarcascade_profileface.xml
Eye detector (left eye ): haarcascade_lefteye_2splits.xml
Eye detector (right eye): haarcascade_righteye_2splits.xml
Mouth detector: haarcascade_mcs_mouth.xml
Nose detector: haarcascade_mcs_nose.xml
Body detector: haarcascade_fullbody.xml
Face detector (fast LBP): lbpcascade_frontalface. xml

here first shows the effect of using OBS to push streaming

Insert image description here
opencv divides the live streaming video into pictures and then identifies them

5. Final effect

Summarize

This is my own small project about live broadcast practice, which has taken many detours. Write a summary of yourself about the entire process.

Guess you like

Origin blog.csdn.net/qq_40750573/article/details/125289747