/** * 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}