0

new MediaRecorder(stream[, options]);

I want record the user camera and audio

I need mixing the song.mp3 to the audio track in recording.

and result export a video file to download by link.

But the MediaRecorder first params stream can living modify ?

But When I use recoder.stop()

It tips error: Failed to execute 'stop' on 'MediaRecorder': The MediaRecorder's state is 'inactive'.

My code:

    function getFileBuffer(filepath) {
      return fetch(filepath, {method: 'GET'}).then(response => response.arrayBuffer())
    }
    
    function mp3play() {
      getFileBuffer('song.mp3')
      .then(buffer => context.decodeAudioData(buffer))
      .then(buffer => {
        console.log(buffer)
        const source = context.createBufferSource()
        source.buffer = buffer
        let volume = context.createGain()
        volume.gain.value = 1
        source.connect(volume)
        dest = context.createMediaStreamDestination()
        volume.connect(dest)
        // volume.connect(context.destination)
        source.start(0)
    
        const _audioTrack = stream.getAudioTracks();
        if (_audioTrack.length > 0) {
          _audioTrack[0].stop();
          stream.removeTrack(_audioTrack[0]);
        }
        //
        // console.log(dest.stream)
        // console.log(dest.stream.getAudioTracks()[0])
        // stream.addTrack(dest.stream.getAudioTracks()[0])
      })
    }
    
    function startRecording() {
      recorder = new MediaRecorder(stream, {
        mimeType: 'video/webm'
      })
      recorder.start()
      stopBtn.removeAttribute('disabled')
      startBtn.disabled = true
    }
Lykos94
  • 573
  • 4
  • 10
zhishaofei3
  • 117
  • 1
  • 5

1 Answers1

0

No we still can't record a MediaStream whose tracks are changed after the recording began, doing so will stop() the MediaRecorder. Here is a very related Q/A which was more about recording video.

What can be done though is to create a kind of merger MediaStream.
It's way easier with audio, moreover since you are already using the WebAudio API: all you need to do is to create an other MediaStreamDestination node, and connect / disconnect the different sources.

const base = "https://upload.wikimedia.org/wikipedia/en/d/";
const urls = [
  "d3/Beach_Boys_-_Good_Vibrations.ogg",
  "dc/Strawberry_Fields_Forever_%28Beatles_song_-_sample%29.ogg"
].map( url => base + url );
const context = new AudioContext();
const button = document.querySelector( 'button' );

button.onclick = async () => {
  button.disabled = true;
  context.resume();
  const audiobuffers = await Promise.all( urls.map( fetchAsAudioBuffer ) );
  button.remove();
  const streamNode = context.createMediaStreamDestination();
  const stream = streamNode.stream;
  
  const recorder = new MediaRecorder( stream );
  const chunks = [];
  recorder.ondataavailable = evt => chunks.push( evt.data );
  recorder.onstop = evt => exportAudio( new Blob( chunks ) );
  document.getElementById( 'record-stopper' ).onclick = evt => {
    recorder.stop();
    current_source.stop( 0 );
  };

  let current_index = 0;
  let current_source = null;
  
  document.getElementById( 'switcher' ).onclick = switchAudioSource;
  
  switchAudioSource();
  recorder.start();

  function switchAudioSource() {
    if( current_source ) {
      current_source.stop( 0 );
    }
    current_index = (current_index + 1) % audiobuffers.length;
    current_source = context.createBufferSource();
    current_source.buffer = audiobuffers[ current_index ];
    current_source.loop = true;
    current_source.connect( streamNode );
    current_source.connect( context.destination );
    current_source.start( 0 );
  }

};

function exportAudio( blob ) {
  const aud = new Audio( URL.createObjectURL( blob ) );
  aud.controls = true;
  document.body.prepend( aud );
}

async function fetchAsAudioBuffer( url ) {
  const buf = await fetchAsBuffer( url );
  return context.decodeAudioData( buf );
}
async function fetchAsBuffer( url ) {
  const resp = await fetch( url );
  return resp.arrayBuffer();
}
button+.recording-controls,
  audio+.recording-controls {
    display: none;
  }
<button>begin</button>
<div class="recording-controls">
  <label>Recording...</label>
  <button id="switcher">Switch Audio Sources</button>
  <button id="record-stopper">Stop Recording</button>
</div>

For video that would imply recording a CanvasMediaStreamTrack and drawing the different video streams on the source <canvas>, but we generally loose a lot of quality doing so...

Kaiido
  • 123,334
  • 13
  • 219
  • 285