react-native-vlc-media-player icon indicating copy to clipboard operation
react-native-vlc-media-player copied to clipboard

Record video from RTSP stream

Open dgreasi opened this issue 4 months ago • 2 comments

RTSP Stream Recording Not Working

Description

I'm trying to record an RTSP stream in my React Native app using react-native-vlc-media-player. Playback works, but recording does not produce any video file on Android (tested on emulator). I have tried saving to both DocumentDirectoryPath and ExternalDirectoryPath using react-native-fs. The onRecordingCreated callback is never triggered.

I am able to successfully record and save videos with the emulators's camera, so there is no issue with permissions or something like that.

Environment

  • Platform: Android (emulator)
  • React Native version: 0.78.2
  • react-native-vlc-media-player version: 1.0.87
  • react-native-fs version: 2.20.0

Steps to Reproduce

  1. Use the example code below in a React Native project.
  2. Replace the EXAMPLE_RTSP_URL with a valid RTSP stream.
  3. Run the app on an Android emulator.
  4. Start and stop recording using the provided buttons.
  5. Check the output directory (ExternalDirectoryPath or DocumentDirectoryPath) for the video file.

Expected Behavior

A video file (e.g., recording_example_<timestamp>.mp4) should be created in the specified directory after stopping the recording.

Actual Behavior

No video file is created in the output directory. No errors are thrown unless the recording methods themselves fail.

Example Code

import React, { useCallback, useRef, useState } from 'react';
import { Button, Platform, StyleSheet, Text, View } from 'react-native';
import { DocumentDirectoryPath, ExternalDirectoryPath } from 'react-native-fs';
import { VLCPlayer, VLCPlayerProps } from 'react-native-vlc-media-player';

/**
 * Example component for demonstrating RTSP stream recording in React Native.
 * This version contains NO sensitive data and is suitable for sharing in a public GitHub issue.
 *
 * - Uses react-native-vlc-media-player for RTSP playback and recording.
 * - Attempts to record to DocumentDirectoryPath or ExternalDirectoryPath.
 * - Shows the output path and basic error handling.
 *
 * Replace the RTSP URL with your own for actual use.
 */

const EXAMPLE_RTSP_URL = 'rtsp://your_rtsp_server_address/your_stream_path';

const VLC_PLAYER_INIT_OPTIONS: string[] = [
  '--rtsp-tcp',
  '--no-audio',
  '--verbose=2',
];

export const ExternalCameraAppExample: React.FC = () => {
  const playerRef = useRef<VLCPlayerProps | null>(null);
  const [isRecording, setIsRecording] = useState(false);
  const [recordingPath, setRecordingPath] = useState('');
  const [error, setError] = useState<string | null>(null);

  // Choose output path: DocumentDirectoryPath or ExternalDirectoryPath
  const outputPath =
    Platform.OS === 'android'
      ? `${ExternalDirectoryPath}/recording_example_${Date.now()}.mp4`
      : `${DocumentDirectoryPath}/recording_example_${Date.now()}.mp4`;

  const startRecording = useCallback(async () => {
    setError(null);
    setRecordingPath(outputPath);
    setIsRecording(true);
    try {
      await playerRef?.current?.startRecording?.(outputPath);
    } catch (e: any) {
      setIsRecording(false);
      setError('Failed to start recording: ' + (e?.message || e));
    }
  }, [outputPath]);

  const stopRecording = useCallback(async () => {
    setError(null);
    try {
      await playerRef?.current?.stopRecording?.();
      setIsRecording(false);
    } catch (e: any) {
      setIsRecording(false);
      setError('Failed to stop recording: ' + (e?.message || e));
    }
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>RTSP Recording Example</Text>
      <View style={styles.playerContainer}>
        <VLCPlayer
          ref={playerRef}
          source={{
            uri: EXAMPLE_RTSP_URL,
            initOptions: VLC_PLAYER_INIT_OPTIONS,
          }}
          autoplay={true}
          autoAspectRatio={true}
          resizeMode="cover"
          style={styles.cameraPreview}
          onError={e => setError('VLC Error: ' + JSON.stringify(e))}
        />
      </View>
      <View style={styles.controls}>
        {!isRecording ? (
          <Button title="Start Recording" onPress={startRecording} />
        ) : (
          <Button title="Stop Recording" onPress={stopRecording} />
        )}
      </View>
      <Text style={styles.info}>Recording path: {recordingPath}</Text>
      {error && <Text style={styles.error}>{error}</Text>}
      <Text style={styles.note}>
        Note: On Android, check the file at:
        {'\n'}
        {outputPath}
        {'\n'}
        (Use Device File Explorer or adb to verify.)
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 24,
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  title: {
    fontWeight: 'bold',
    fontSize: 18,
    marginBottom: 12,
  },
  playerContainer: {
    width: '100%',
    height: 200,
    borderWidth: 1,
    borderColor: '#ccc',
    backgroundColor: '#000',
    marginBottom: 16,
  },
  cameraPreview: {
    width: '100%',
    height: '100%',
  },
  controls: {
    flexDirection: 'row',
    marginVertical: 16,
  },
  info: {
    fontSize: 12,
    color: '#333',
    marginBottom: 8,
  },
  error: {
    color: 'red',
    marginBottom: 8,
  },
  note: {
    fontSize: 12,
    color: '#666',
    marginTop: 16,
    textAlign: 'center',
  },
});

export default ExternalCameraAppExample;

dgreasi avatar Jun 16 '25 10:06 dgreasi