skip to Main Content

This is my first time working with videos and it’s a bit overwhelming.

Problem

I would like to be able to record user’s screen and every 5 seconds send the recorded screen to my backend server. Then, on my backend server append each 5 seconds into a full video.

If I send 3 parts of 5 second, I would like to have the full 15 seconds video.

What I have tried

I have the following frontend code:


function accessRecord(id) {
    navigator.mediaDevices.getDisplayMedia({
        audio: false,
        video: true
    }).then(startStream).catch(failedStream)
}

function startStream(stream) {
    const mediaRecorder = new MediaRecorder(stream, {
        // I have tried adding a mimeType as well
        // mimeType: 'video/mp4'
    });

    mediaRecorder.start(5000);

    mediaRecorder.ondataavailable = (e) => {
        fetch('http://localhost/api/session/12/video/stream', {
            method: 'POST',
            body: new Blob([e.data]),
        }).then(() => {
            console.log('success')
        }).catch((e) => {
            console.log('error')
            console.log(e);
        })
    };
}

Then, on my backend server [Laravel] I do the following logic:

Route::post('session/{id}/video/stream', function (Request $request) {
    $videoData = $request->getContent();

    $filename = 'video_uuid-video.mp4';

    if (!file_exists(storage_path('app/videos/' . $filename))) {
        touch(storage_path('app/videos/' . $filename));
    }

    IlluminateSupportFacadesStorage::append('videos/' . $filename, $videoData);

    return response()->json(['message' => 'Video frame uploaded successfully']);
});

Whenever I stop the streaming, and I try to open the video on my MAC (MacOS) the video doesn’t open:

enter image description here

I’m not sure what I’m doing wrong here. I would like to record every 5 seconds the video, then append to the current video and in the end (when the stream ends) I would like to be able to play the video.

2

Answers


  1. It seems that you are trying to send individual video frames as separate requests to the server and append them to create a full video. However, this approach is not sufficient for creating a playable video file.

    To achieve your goal, you’ll need to use a video container format (e.g., MP4) and ensure that the video frames are encoded and organized correctly within the container.

    Here’s an updated approach that utilizes the MediaRecorder API on the frontend and FFmpeg on the backend to create a playable video:

    let mediaRecorder;
    let recordedChunks = [];
    
    function accessRecord(id) {
      navigator.mediaDevices.getDisplayMedia({
        audio: false,
        video: true
      }).then(startStream).catch(failedStream);
    }
    
    function startStream(stream) {
      mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
    
      mediaRecorder.addEventListener('dataavailable', function(e) {
        if (e.data.size > 0) {
          recordedChunks.push(e.data);
        }
      });
    
      mediaRecorder.start(5000);
    
      setTimeout(stopStream, 15000);
    }
    
    function stopStream() {
      mediaRecorder.stop();
    
      const blob = new Blob(recordedChunks, { type: 'video/webm' });
    
      const reader = new FileReader();
      reader.onload = function() {
        const videoData = reader.result;
    
        fetch('http://localhost/api/session/12/video/save', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/octet-stream'
          },
          body: videoData
        }).then(() => {
          console.log('Video saved successfully');
        }).catch((e) => {
          console.log('Error saving video');
          console.log(e);
        });
      };
    
      reader.readAsArrayBuffer(blob);
    }
    

    Backend (Laravel) Code:

    Install FFmpeg (if not already installed) on your server. You can follow the instructions for your operating system to install FFmpeg.

    Install the PHP-FFMpeg library for Laravel. You can install it using Composer:

    composer require php-ffmpeg/php-ffmpeg
    

    Update your Laravel route and controller to handle video saving and concatenation:

    use FFMpeg;
    use IlluminateHttpRequest;
    
    Route::post('session/{id}/video/save', 'VideoController@saveVideo');
    
    class VideoController extends Controller
    {
        public function saveVideo(Request $request, $id)
        {
            $videoData = $request->getContent();
            $filename = 'video_uuid-video.webm'; // Change the filename and extension as needed
    
            $videoPath = storage_path('app/videos/' . $filename);
    
            file_put_contents($videoPath, $videoData);
    
            return response()->json(['message' => 'Video frame uploaded successfully']);
        }
    
        public function concatenateVideos()
        {
            $videoPath = storage_path('app/videos/'); // Path to your video files
            $outputFilename = 'concatenated-video.mp4'; // Output filename and extension
    
            $ffmpeg = FFMpegFFMpeg::create();
            $videoConcatenation = $ffmpeg->concatenate();
    
            // Loop through the video files in the storage directory
            foreach (Storage::files('videos') as $file) {
                $videoConcatenation->addFromPath(storage_path('app/' . $file));
            }
    
            // Save the concatenated video
            $videoConcatenation->saveFromSameCodecs($videoPath . $outputFilename);
    
            return response()->json(['message' => 'Videos concatenated successfully']);
        }
    }
    

    In the above code, the first route ‘session/{id}/video/save’ is used to save each video chunk sent from the frontend. The {id} parameter represents the session ID or any other identifier you’re using.

    The second route ‘session/concatenate-videos’ is used to trigger the concatenation of the saved video chunks into a single video file. You can modify the route URL as needed.

    Make sure to adjust the route URLs and controller method names according to your specific application needs.

    Login or Signup to reply.
  2. You can not combine or append video file to another video file using normal append like append text file.

    You need an encoder, for an example FFMPEG

    here the example code:

        $existing_file = 'video_uuid-video.mp4';
        $filename = rand(0,9999999).".mp4";
    
        if (!file_exists(storage_path('app/videos/'.$filename))) {
            touch(storage_path('app/videos/'.$filename));
        }
    
        Storage::put('videos/'.$filename, $videoData);
        shell_exec("ffmpeg -i $filename -i $existing_file -filter_complex "concat=n=2:v=0:a=1" -vn -y output.mp4");
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search