/**
* Sample for Pili react-native SDK
*/
import merge from 'merge'
import React, { Component } from 'react'
import { SafeAreaView, Text, StatusBar, ScrollView, View, Button, Platform, PermissionsAndroid, TextInput } from 'react-native'
import { consts, Streaming } from 'pili-streaming-react-native'
import { FileInput, AvCodecTypeInput, CameraResolutionInput, CameraFocusModeInput, CameraVideoOrientationInput, MicrophoneSampleRateInput, MicrophoneChannelInput, SwitchInput, VideoEncodeOrientationInput, VideoH264ProfileInput, BitrateAdjustModeInput, EncoderRCModeInput, CameraInput, NumberInput } from './components/Input'
const isAndroid = Platform.OS === 'android'
export default class App extends Component {
state = {
androidPermissionGranted: false,
state: null,
streamInfo: null,
audioMixProgress: null,
streamingConfigInput: '',
streamingConfigError: null,
streamingConfig: {
rtmpURL: 'rtmp://pili-publish.qnsdk.com/sdk-live/111',
camera: 'back',
muted: false,
zoom: 0,
focus: false,
started: true,
faceBeautyEnable: false,
faceBeautySetting: {
beautyLevel: 1,
whiten: 1,
redden: 0.8,
},
watermarkSetting: {
src: null, // or `''`?
alpha: 122,
position: {
x: 0,
y: 0
},
size: {
width: 50,
height: 20
},
},
pictureStreamingFile: null,
pictureStreamingEnable: false,
torchEnable: false,
previewMirrorEnable: false,
encodingMirrorEnable: false,
audioMixFile: {
filePath: null, // or `''`?
loop: true,
},
playMixAudio: false,
audioMixVolume: {
micVolume: 0.5,
musicVolume: 0.5,
},
playbackEnable: false,
profile: {
videoStreamingSetting: {
fps: 30,
bps: 1000 * 1024,
maxFrameInterval: 60,
encodeOrientation: consts.videoEncodeOrientations.portrait,
h264Profile: (
isAndroid
? consts.videoH264Profiles_android.baseline
: consts.videoH264Profiles_iOS.baseline31
),
customVideoEncodeSize: {
width: 1024,
height: 720
}
},
audioStreamingSetting: {
rate: 44100,
bitrate: 96 * 1024,
},
encodingSize: consts.videoEncodings.e480,
avCodecType: (
isAndroid
? consts.avCodecTypes_android.SW_VIDEO_WITH_SW_AUDIO_CODEC
: consts.avCodecTypes_iOS.PLH264EncoderType_AVFoundation
),
cameraStreamingSetting: {
resolution: (
isAndroid
? consts.cameraResolutions_android.MEDIUM_RATIO_16_9
: consts.cameraResolutions_iOS.AVCaptureSessionPresetMedium
),
focusMode: consts.cameraFocusModes.continuousVideo,
videoOrientation: consts.cameraVideoOrientations.portrait
},
microphoneSteamingSetting: {
sampleRate: consts.microphoneSampleRates.r44100,
channel: consts.microphoneChannels.mono,
isAecEnable: false
},
quicEnable: false,
bitrateAdjustMode: consts.bitrateAdjustModes.auto,
adaptiveBitrateRange: {
minBitrate: 1024,
maxBitrate: 1024*1024,
},
encoderRCMode: consts.encoderRCModes.bitratePriority,
streamInfoUpdateInterval: 5,
},
},
}
handleStateChange = state => this.setState({ state })
handleStreamInfoChange = streamInfo => this.setState({ streamInfo })
handleAudioMixProgress = audioMixProgress => this.setState({ audioMixProgress })
handleStreamingConfigInputChange = text => this.setState({ streamingConfigInput: text })
handleStreamingConfigInputSubmit = () => {
this.setState({ streamingConfigError: null })
try {
const toMerge = JSON.parse(this.state.streamingConfigInput)
const streamingConfig = merge.recursive(true, this.state.streamingConfig, toMerge)
this.setState({ streamingConfig })
} catch (e) {
this.setState({ streamingConfigError: e && e.message })
}
}
useStateOfPath = keyPath => {
const toMerge = {}
const keys = keyPath.split('.')
const lastKey = keys[keys.length - 1]
const lastObj = keys.slice(0, -1).reduce((obj, key) => {
return obj[key] = {}
}, toMerge)
const value = keys.reduce((obj, key) => obj[key], this.state)
const onChange = value => {
lastObj[lastKey] = value
const newState = merge.recursive(true, this.state, toMerge)
this.setState(newState)
}
return [value, onChange]
}
bindStateOfPath = keyPath => {
const [value, onChange] = this.useStateOfPath(keyPath)
return { value, onChange }
}
componentDidMount() {
if (isAndroid) {
PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.CAMERA,
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE,
PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION
]).then(() => {
this.setState({ androidPermissionGranted: true })
})
}
}
render() {
const {
androidPermissionGranted,
state,
streamInfo,
audioMixProgress,
streamingConfigInput,
streamingConfigError,
streamingConfig
} = this.state
if (isAndroid && !androidPermissionGranted) {
return (
<>
Permission not granted
>
)
}
const streamingConfigErrorText = (
streamingConfigError != null
? {streamingConfigError}
: null
)
const stateText = state != null ? state : 'none'
const streamInfoText = streamInfo != null ? JSON.stringify(streamInfo) : 'none'
const audioMixProgressText = audioMixProgress != null ? JSON.stringify(audioMixProgress) : 'none'
const props = {
...streamingConfig,
// TODO: 后续不再需要
profile: {
...streamingConfig.profile,
video: streamingConfig.profile.videoStreamingSetting,
audio: streamingConfig.profile.audioStreamingSetting,
},
onStateChange: this.handleStateChange,
onStreamInfoChange: this.handleStreamInfoChange,
onAudioMixProgress: this.handleAudioMixProgress,
style: {
width: '100%',
height: 200,
backgroundColor: 'transparent',
borderBottomColor: '#333',
borderBottomWidth: 1,
},
}
const streamingConfigText = JSON.stringify(streamingConfig, null, 2)
this.state.streamingConfig.audioMixFile.filePath
return (
<>
{streamingConfigErrorText}
Pili@ReactNative
State: {stateText}
StreamInfo: {streamInfoText}
AudioMixProgress: {audioMixProgressText}
streamingConfig:
{streamingConfigText}
>
)
}
}