The length of video files created by MediaRecorder is not retained

This component prompts the user for camera access, displays a video preview, and allows the user to watch it again with video controls such as downloading or navigating to specific moments. However, there is an issue where the recorded video seems to be missing duration information, causing the <video> element not to know the length of the video. How can we prevent this problem? Any suggestions for improving the code are welcome. Thank you!

export default function RecordVideo() {
  const [state, setState] = createSignal<State>("idle");
  const [blob, setBlob] = createSignal<Blob | null>(null);
  const [count, setCount] = createSignal<number>(0);
  const [message, setMessage] = createSignal<string>("start recording");

  let preview: { srcObject: MediaStream };
  let recorder: MediaRecorder;
  let interval: NodeJS.Timer;

  const startRecording = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        audio: true,
        video: true,
      });
      setState("recording");
      preview.srcObject = stream;
      recorder = new MediaRecorder(stream);
      const chunks: Blob[] = [];
      recorder.ondataavailable = ({ data }) => chunks.push(data);
      recorder.onstop = () => {
        setBlob(new Blob(chunks, { type: "video/mp4" }));
      };
      recorder.start();
      interval = setInterval(() => setCount(prev => prev + 1), 1000);
    } catch ({ message }: any) {
      setMessage(message);
    }
  };

  const stopRecording = () => {
    preview.srcObject.getTracks().forEach(track => track.stop());
    recorder.stop();
    clearInterval(interval);
    setState("stopped");
  };

  const deleteRecording = () => {
    setBlob(null);
    setCount(0);
    setState("idle");
  };

  const downloadRecording = () =>
    Object.assign(document.createElement("a"), {
      href: URL.createObjectURL(blob()),
      download: "a.mp4",
      type: "video/mp4",
    }).click();

  onCleanup(() => {
    clearInterval(interval);
    preview.srcObject?.getTracks().forEach(track => track.stop());
  });

return (
    <section>
      <Switch>
        <Match when={state() === "idle"}>
          <div onClick={startRecording}>
            {message()}
          </div>
        </Match>
        <Match when={state() === "recording"}>
          <div>
            <video
              ref={preview}
              autoplay
              muted
            />
            <div>
              <span>{formatCount(count())}</span>
              <button onClick={stopRecording}>
                stop
              </button>
            </div>
          </div>
        </Match>
        <Match when={state() === "stopped" && blob()}>
          <div>
            <video
              src={URL.createObjectURL(blob())}
              autoplay
              controls
            />
            <div>
              <button onClick={deleteRecording}>delete</button>
              <button onClick={downloadRecording}>
                download
              </button>
            </div>
          </div>
        </Match>
      </Switch>
    </section>
  );
}

Answer №1

Your component state is currently fragmented, which can lead to inconsistencies when updating it. It's best to maintain the component state as a single object to ensure that updates are always consistent.

Let me illustrate this with an example using a promise:

Imagine representing the status of a promise like this:

const [status, setStatus] = createSignal('pending');
const [data, setData] = createSignal(undefined);
const [error, setError] = createSignal(undefined);

When we make a request, the status will be 'pending', while data and error will be null. Upon receiving data, the status should change to 'resolved', data will contain the received information, and error will remain null.

Updating three signals individually may result in inconsistent states at some point. To prevent this, use batch to update all signals simultaneously:

batch(() => {
  setStatus('resolved');
  setData(dataWeReceived);
});

By utilizing batch, you ensure that effects are only executed once all state updates are completed, eliminating inconsistent states.

Learn more about batch here

Alternatively, consolidating the states into a single object can also maintain consistency:

type Resource<T = any, E = Error> = 
| { status: 'pending' }
| { status: 'success', success: T }
| { status: 'error', error: E }
;

const [state, setState] = createSignal({ status: 'pending' })

Now, by updating a single object, you ensure that the state remains consistent:

setState({ status: 'resolved', data: dataWeReceived )});

For your specific case, merging all individual states into one object is recommended for consistency:

const [state, setState] = createSignal({
  status: 'idle',
  count: 0,
  blob: null,
  message: "Start Recording",
});

Whenever you need to update properties, do so within a function to avoid inconsistencies:

setState(prev => ({...prev, count: prev + 1 });

If you prefer using primitive states, ensure to wrap updates in a batch to avoid inconsistencies. Alternatively, consider using a store for efficient handling of reactive objects.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

VS Code is flagging TypeScript errors following the recent software update

After updating my VS Code, I started seeing TypeScript error messages like the following: ButtonUnstyled.types.d.ts: Module '"/components/node_modules/@types/react/index"' can only be default-imported using the 'esModuleInterop&a ...

Expanding a Zod object by merging it with a different object and selecting specific entries

Utilizing Zod, a TypeScript schema validation library, to validate objects within my application has led me to encounter a specific scenario. I find myself in need of validating an object with nested properties and extending it with another object while se ...

Something went wrong trying to retrieve the compiled source code of https://deno.land/[email protected]/path/mod.ts. It seems the system is unable to locate the specified path. (os error 3)

Upon executing my main.ts script using deno, I encounter the following error message: error: Failed to retrieve compiled source code from https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bfcccbdbff8f918a86918f"& ...

Resolving a persistent AngularJS 1 constant problem with Typescript

I'm currently developing an application using TypeScript and AngularJS 1, and I've encountered a problem while trying to create a constant and passing it to another class. The constant in question is as follows: module app{ export class A ...

Typescript-optimized Npm library utilizing the module-alias feature

As I work on creating an npm library with Typescript, I have made use of the paths setting in tsconfig.json and the module-alias tool to streamline my imports, allowing me to use syntax like import * from '@/utils'. However, I have encountered an ...

Is there a way to access the state value within the reducer function of createSlice?

Currently, I am utilizing redux-toolkit within my react project. A concern arises in a specific reducer inside the createSlice method where I aim to incorporate an existing array of entities from the state and then merge it with a new array before finalizi ...

The assignment of type 'string' to type 'UploadFileStatus | undefined' is not permissible

import React, { useState } from 'react'; import { Upload } from 'antd'; import ImgCrop from 'antd-img-crop'; interface uploadProps{ fileList:string; } const ImageUploader:React.FC <uploadProps> ...

Are there any methods to utilize Zod for validating that a number contains a maximum of two decimal places?

How can I ensure that a numeric property in my object has only up to 2 decimal digits? For example: 1 // acceptable 1.1 // acceptable 1.11 // acceptable 1.111 // not acceptable Is there a method to achieve this? I checked Zod's documentation and sea ...

I am encountering an error in TypeScript stating that a property does not exist on the Response type

Hey there, I've been working on a custom advanced results page that allows for various queries like /articles?name="". This is my first time using TypeScript and while migrating my code over, I encountered a TypeScript error at the very end. // esli ...

Achieving checkbox values in Typescript: A guide

I need help with capturing the values of checked checkboxes and storing them in a string to use in my API. I want to retrieve the value if a checkbox is unchecked. <div *ngFor="let x of groupesTable"> <input type="checkbox" [(ngModel)] ...

TypeScript's attempt to replicate Scala's underscore feature has been implemented, but it proves to

I've been working on a personal project for the past 2 years trying to implement Scala's underscore in TypeScript, but haven't been successful. Here is my attempted implementation and its effect. The only thing that I really care about typi ...

looking to restrict the interface to only specific types

Currently working with TypeScript and I have a query regarding the utilization of TypeScript interface. Is there a way to selectively extract and output specific key types from the interface being used? While I am able to isolate the type I need, I am inte ...

Javascript/Typescript Performance Evaluation

I am looking to create a visual report in the form of a table that displays the count of each rating based on the date. The ratings are based on a scale of 1 to 5. Below is the object containing the data: [ { "Date": "01/11/2022", ...

Inversify: class-based contextual dependency injection

I am currently experimenting with injecting loggers into various classes using inversify. My goal is to pass the target class name to the logger for categorization. The challenge I'm facing is the inability to access the target name from where I am c ...

Typescript error points out that the property is not present on the specified type

Note! The issue has been somewhat resolved by using "theme: any" in the code below, but I am seeking a more effective solution. My front-end setup consists of React (v17.0.2) with material-ui (v5.0.0), and I keep encountering this error: The 'palet ...

Looking to resolve a module-specific error in Angular that has not been identified

While practicing Angular, I encountered an error during compilation: Module not found: Error: Can't resolve './app.component.css' in 'D:\hello-world-app\src\app' i 「wdm」: Failed to compile. This is my app.compo ...

When dynamically selecting an item from a dropdown menu, the object property does not display as expected when using the patchValue

When attempting to set the value for a sort object with specific type and format, I encountered an issue where it was not being rendered. Below is my code snippet using patch to set the value: let arr = <FormArray>this.myForm.controls.users; arr.c ...

What steps should I take to retrieve my information from a typescript callback function?

While I am familiar with using callbacks in JavaScript, I have recently started learning Angular and TypeScript. I am facing an issue with getting my data from a service back to where I need it after executing the callback function. The callback itself i ...

Clicking the button on an Ionic/Angular ion-item will toggle the visibility of that item, allowing

I'm currently working with Ionic 5 / Angular and I have a collection of ion-item's, each containing a button. Take a look at the code snippet below: <ion-list> <ion-item> <ion-label>One</ion-label> ...

Issue with CORS when starting SAM local API

I have encountered a CORS issue while using AWS CDK (Typescript) and running SAM local start-api to launch an API connected to lambda resolvers. The problem arises when attempting to access the API from a web browser. Below is the code snippet causing the ...