react-native-nodemediaclient icon indicating copy to clipboard operation
react-native-nodemediaclient copied to clipboard

Hey, How to set zoom?

Open t1amat9409 opened this issue 5 years ago • 14 comments

Hey there creator, is there a way to add zoom to the camera view?

t1amat9409 avatar Nov 01 '19 14:11 t1amat9409

any progress of zoom?

RupamShaw avatar Apr 11 '20 13:04 RupamShaw

@RupamShaw I ended up using the native libraries with NativeScript, they do have an option to set zoom

t1amat9409 avatar Apr 11 '20 17:04 t1amat9409

@t1amat9409 can you please show code snippet to get an idea. and how did you use in nativescript in react native project which have this react-native-nodemediaclient library.

NodecameraView props having onGestureHandlerEvent onGestureHandlerStateChange . how to implememt it for zoom?

 this.scale = new Animated.Value(1)
 onZoomEvent = Animated.event(
   [
     {
       nativeEvent: {scale: this.scale},
     },
   ],
   {
     useNativeDriver: true,
   }
 )

 onZoomStateChange = event => {
   if (event.nativeEvent.oldState === State.ACTIVE) {
     Animated.spring(this.scale, {
       toValue: 1,
       useNativeDriver: true,
     }).start()
   }
 }

how to use it. even I tried

 <NodeCameraView
              style={[
                styles.backgroundVideo
              ]}
              ref={vb => {
                this.vb = vb
              }}
              outputUrl={outputUrl}
              camera={{cameraId: 0, cameraFrontMirror: true}}
              audio={{bitrate: 32000, profile: 1, samplerate: 44100}}
              video={{
                preset: 1,
                bitrate: 500000,
                profile: 1,
                fps: 15,
                videoFrontMirror: false,
              }}
              onGestureHandlerEvent={this.onZoomEvent}
              onGestureHandlerStateChange={this.onZoomStateChange}
              smoothSkinLevel={3}
              autopreview>

RupamShaw avatar Apr 25 '20 09:04 RupamShaw

it's not working even used NodeCameraView props onGestureHandlerEvent/ onGestureHandlerStateChange

constructor(props) {
    super(props)
    this.scale = new Animated.Value(1)
    this._baseScale = new Animated.Value(1)

    this._pinchScale = new Animated.Value(1)
  
    this._scale = Animated.multiply(this._baseScale, this._pinchScale)
  
    this._lastScale = 1
}
  _onPinchGestureEvent = Animated.event(
    [{ 
      nativeEvent: { scale: this._pinchScale }
      }],
    { useNativeDriver: true }
  );

  _onPinchHandlerStateChange = event => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this._lastScale *= event.nativeEvent.scale
      this._baseScale.setValue(this._lastScale)
      this._pinchScale.setValue(1)
    }
  };
<PinchGestureHandler
          onGestureEvent={this._onPinchGestureEvent}
          onHandlerStateChange={this._onPinchHandlerStateChange}>
          <Animated.View 
            style={{transform: [ { scale: this._scale } ]}  }
           >
               <NodeCameraView
                onGestureHandlerEvent={this._onPinchGestureEvent}
               onHandlerStateChange={this._onPinchHandlerStateChange}
              ref={vb => {
                this.vb = vb
              }}
              outputUrl={outputUrl}
              camera={{cameraId: 0, cameraFrontMirror: true}}
              audio={{bitrate: 32000, profile: 1, samplerate: 44100}}
              video={{
                preset: 1,
                bitrate: 500000,
                profile: 1,
                fps: 15,
                videoFrontMirror: false,
              }}
              smoothSkinLevel={3}
              autopreview/>
                ....
          </NodeCameraView> 
     </Animated.View> 
 </PinchGestureHandler>

resources (https://software-mansion.github.io/react-native-gesture-handler/docs/handler-pinch.html - https://www.youtube.com/watch?v=0FVnzuyFNSE)

its zooming only in nodeCameraview but in client which uses NodePlayerView thats not showing zoomed in or out while streaming.

Please help..

RupamShaw avatar Apr 26 '20 11:04 RupamShaw

Hi @RupamShaw , I think in order for you to be able to set zoom, you need to extend the RN module, add a set zoom property there, because in the Native library there is a method for setting zoom. And in NativeScript, I only linked the native lib then since you're able to execute native code from JS in NativeScript, i just instantiated a native NodeCameraView then attached to my placeholder container. If you'd be able to expose the zoom property to the RN environment I think you'd be able to achieve this...

Check here https://github.com/NodeMedia/NodeMediaClient-Android/blob/master/nodemediaclient/src/main/java/cn/nodemedia/NodePublisher.java#L216 for android and https://github.com/NodeMedia/NodeMediaClient-iOS/blob/master/NodeMediaClient/NodeMediaClient.framework/Headers/NodePublisher.h#L87 for ios

I hope that helps

t1amat9409 avatar Apr 27 '20 14:04 t1amat9409

can you please show your code snippet of react native of nodeCameraView as well as Ios native changes for zoom. How you have made react native UI component from native nodecameraview(did you use this https://reactnative.dev/docs/native-components-ios). Thanks

RupamShaw avatar Apr 28 '20 07:04 RupamShaw

I am able to do it by changing native modules for zoom level and its reflecting in playerscreen also. Now how to control it with pinch or tap gesture {zoom scale number 1 to 100} .

changes are in in nodemodules/react-naative-nodemediaclient/ios/

 file RCTNodeCameraView.h
@property (nonatomic) NSInteger zoomScale;
file RTCNodeCameraManager.m
RCT_EXPORT_VIEW_PROPERTY(zoomScale, int);
file   RCTNodeCameraView.m
- (void)setZoomScale:(NSInteger)zoomScale {
  [_np setZoomScale:zoomScale];
}
 onPinchHandlerStateChange = event => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastScale *= event.nativeEvent.scale
      if (this.lastScale < 1) {
        this.lastScale = 1
      }
      if (this.lastScale> 10) {
        this.lastScale = 10
      }
      this.baseScale.setValue(this.lastScale)
      this.pinchScale.setValue(1)
    }
   const nmScale = Math.round(this.baseScale._value * 10)
    this.setState({nmScale})
  }

   <PinchGestureHandler
          onGestureEvent={this.onPinchGestureEvent}
          onHandlerStateChange={this.onPinchHandlerStateChange}>
          <Animated.View
            style={[
              {
                flex: 1,
                marginBottom: 0,
                padding: 0,
                transform: [{scale: this.scale}],
              },
              styles.backgroundVideo,
            ]}>
            <NodeCameraView
              style={[styles.backgroundVideo]}
              ref={vb => {
                this.vb = vb
              }}
             outputUrl={outputUrl}
              camera={{cameraId: 0, cameraFrontMirror: true}}
              audio={{bitrate: 32000, profile: 1, samplerate: 44100}}
              video={{
                preset: 1,
                bitrate: 500000,
                profile: 1,
                fps: 15,
                videoFrontMirror: 3,
              }}
              smoothSkinLevel={3}
              autopreview
              zoomScale={this.state.nmScale}
            />
          </Animated.View>
        </PinchGestureHandler>
  <NodePlayerView
          style={styles.backgroundVideo}
          ref={ref => {
            this.player = ref
          }}
          inputUrl={`rtmp://${RTMPIP}:${RTMPPORT}/live/${currentLivingSale.id}`}
         // scaleMode="ScaleAspectFit"
          bufferTime={300}
          maxBufferTime={1000}
          smoothSkinLevel={3}
          autoplay/>

issue is player screen is scaling less when nodecameraview is full zoomed. so both zoom levels are not same. currently I did it using slider set zoomscale.

 sliderValue = val => {
    this.setState({ nmScale:val})
  }

  <Slider
          style={{width: 200, height: 40}}
          minimumValue={1}
          onValueChange={this.sliderValue}
          maximumValue={100}
          minimumTrackTintColor="#FFFFFF"
          maximumTrackTintColor="#000000"
        />

RupamShaw avatar Apr 28 '20 08:04 RupamShaw

@RupamShaw I'm not using React Native. I mean, I did, but I wasn't able to set the zoom value there. Only came back to use NodeMediaClient when I had to port the app to NativeScript.

I think if you want to use gestures, you'll have to take the y or x value then scale them using a range of 100 to be able to set the accurate value. You can log the value until you achieve accuracy.

Zooming on the player, I think you'll need to consult the creator of this for that as I have no idea how the encoding works

t1amat9409 avatar Apr 28 '20 11:04 t1amat9409

@RupamShaw which RTMP server are you using?

t1amat9409 avatar Apr 28 '20 11:04 t1amat9409

I have own server setUp using nodemediaserver for rtmpserver .

RupamShaw avatar Apr 28 '20 13:04 RupamShaw

I'm also using that one, but I've never previewed a zoomed video

t1amat9409 avatar Apr 28 '20 16:04 t1amat9409

This may be helpful to some:

I was able to add a zoom prop to the NodeCameraView component and got it working for both iOS and Android today. Here's the fork: https://github.com/jkerb8/react-native-nodemediaclient Android part also relies on this fork: https://github.com/jkerb8/NodeMediaClient-Android (I removed the demo because it was causing me issues)

Nothing is final in those forks and I'm sure some parts could be improved, but hopefully this helps if someone wants to throw it in this repo. I also implemented a way to zoom in and out by swiping up and down on the camera viewfinder. Here's the relevant code for that:

import React from 'react';
import { View, TouchableOpacity, StatusBar, PermissionsAndroid, Platform, Alert, PanResponder, Dimensions } from 'react-native';
import { PropTypes } from 'prop-types';
import { Button, Text } from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import { NodeCameraView } from 'react-native-nodemediaclient';
//...irrelevant

const screenHeight = Dimensions.get('window').height;
const screenWidth = Dimensions.get('window').width;
const lowerDim = screenWidth < screenHeight ? screenWidth : screenHeight;
const ZOOM_MAX = Platform.OS === 'ios' ? 100 : 0.5;
const ZOOM_INT = Platform.OS === 'ios' ? 1 : 0.005;

class StreamCaptureScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isPublish: false,
      cameraOpen: false,
      zoom: 0,
      bitrate: 1500,
      fps: 30,
      stream: undefined,
    };
    this.cameraRef = null;
  }

  //...irrelevant

  setRef(ref) {
    this.cameraRef = ref;
  }

  toggleCaptureVideo() {
    if (this.state.isPublish) {
      this.setState({ isPublish: false });
      this.cameraRef.stop();
    } else {
      this.setState({ isPublish: true });
      this.cameraRef.start();
      console.log('started');
    }
  }

  render() {
    return (
      <View style={{ flex: 1 }}>
        {this.state.cameraOpen ? (
          <>
            <ZoomView
              onZoomProgress={change => {
                this.setState({ zoom: Math.min(ZOOM_MAX, Math.max(this.initZoom + change, 0)) });
              }}
              onZoomStart={() => {
                this.initZoom = this.state.zoom;
              }}
              onZoomEnd={() => {
                this.initZoom = this.state.zoom;
              }}
            >
              <NodeCameraView
                style={Style.cameraContainer}
                ref={ref => this.setRef(ref)}
                outputUrl={this.state.stream.input + '/' + this.state.stream.streamkey}
                camera={{ cameraId: 0, cameraFrontMirror: true }}
                audio={{ bitrate: 32000, profile: 1, samplerate: 44100 }}
                video={{ preset: 5, bitrate: this.state.bitrate * kbps, profile: 1, fps: this.state.fps, videoFrontMirror: false }}
                smoothSkinLevel={3}
                zoomScale={this.state.zoom}
                autopreview={true}
                onStatus={(code, msg) => {
                  console.log('onStatus=' + code + ' msg=' + msg);
                }}
              />
              <View style={Style.cameraChildren}>
                <View style={Style.controlsOffset}>
                  <TouchableOpacity style={Style.flexMenu}></TouchableOpacity>
                </View>
                <View style={Style.cameraControls}>
                  <View style={Style.captureButton}>
                    <Text style={Style.clearText}>Camera</Text>
                  </View>
                  <TouchableOpacity onPress={() => this.toggleCaptureVideo()} style={Style.captureButton}>
                    <Icon name={this.state.isPublish ? 'stop' : 'circle-thin'} size={60} color={this.state.isPublish ? Colors.btnDanger : Colors.highlight}></Icon>
                  </TouchableOpacity>
                  <TouchableOpacity
                    onPress={() => {
                      this.toggleCameraClosed();
                    }}
                    style={Style.captureButton}
                  >
                    <Icon name="times" size={30} color="white"></Icon>
                  </TouchableOpacity>
                </View>
              </View>
            </ZoomView>
          </>
        ) : (
        <View style={Style.mainSection}>
            ...irrelevant
        </View>
        )}
      </View>
    );
  }
}

export default StreamCaptureScreen;

class ZoomView extends React.Component {
  constructor(props) {
    super(props);
    this._panResponder = PanResponder.create({
      onPanResponderMove: (e, { dy }) => {
        const change = (-dy * ZOOM_MAX) / (lowerDim * 0.5);
        if (Math.abs(change) >= ZOOM_INT) {
          return this.props.onZoomProgress(change);
        }
        return null;
      },
      onMoveShouldSetPanResponder: (ev, { dy }) => {
        return dy !== 0;
      },
      onPanResponderGrant: () => {
        return this.props.onZoomStart();
      },
      onPanResponderRelease: () => {
        return this.props.onZoomEnd();
      },
    });
  }
  render() {
    return (
      <View style={Style.zoomView} {...this._panResponder.panHandlers}>
        {this.props.children}
      </View>
    );
  }
}

ZoomView.propTypes = {
  children: PropTypes.array,
  onZoomProgress: PropTypes.func,
  onZoomStart: PropTypes.func,
  onZoomEnd: PropTypes.func,
};

jkerb8 avatar Sep 15 '20 23:09 jkerb8

Thanks @jkerb8 I'll try it out

t1amat9409 avatar Sep 16 '20 10:09 t1amat9409

@jkerb8 it's amazing this implementation. I had to do some refinement for iOS but other than that, the behaviour is great. I think this should be proposed as PR and accepted.

developer-appdam avatar Oct 15 '20 12:10 developer-appdam