Cách sử dụng MediaRecorder làm MediaSource


8

Là một bài tập trong việc học WebRTC, tôi đang cố gắng hiển thị webcam cục bộ và cạnh nhau với việc phát lại webcam bị trì hoãn. Để đạt được điều này, tôi đang cố gắng chuyển các đốm màu được ghi lại vào BufferSource và sử dụng MediaSource tương ứng làm nguồn cho một yếu tố video.

// the ondataavailable callback for the MediaRecorder
async function handleDataAvailable(event) {
  // console.log("handleDataAvailable", event);
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }

  if (recordedBlobs.length > 5) {
    if (recordedBlobs.length === 5)
      console.log("buffered enough for delayed playback");
    if (!updatingBuffer) {
      updatingBuffer = true;
      const bufferedBlob = recordedBlobs.shift();
      const bufferedAsArrayBuffer = await bufferedBlob.arrayBuffer();
      if (!sourceBuffer.updating) {
        console.log("appending to buffer");
        sourceBuffer.appendBuffer(bufferedAsArrayBuffer);
      } else {
        console.warn("Buffer still updating... ");
        recordedBlobs.unshift(bufferedBlob);
      }
    }
  }
}
// connecting the media source to the video element
recordedVideo.src = null;
recordedVideo.srcObject = null;
recordedVideo.src = window.URL.createObjectURL(mediaSource);
recordedVideo.controls = true;
try {
  await recordedVideo.play();
} catch (e) {
  console.error(`Play failed: ${e}`);
}

Tất cả mã: https://jsfiddle.net/43rm7258/1/

Khi tôi chạy cái này trong Chromium 78, tôi nhận được NotSupportedError: Failed to load because no supported source was found.từ playphần tử của phần tử video.

Tôi không biết tôi đang làm gì sai hay làm thế nào để tiến hành vào thời điểm này.

Đây là về một cái gì đó tương tự, nhưng không giúp tôi: MediaSource dừng video ngẫu nhiên

Ví dụ này là điểm khởi đầu của tôi: https://webrtc.github.io/samples/src/content/getusermedia/record/

Câu trả lời:


9

Tóm tắt

Làm cho nó hoạt động trong Firefox và Chrome thật dễ dàng: bạn chỉ cần thêm một codec âm thanh vào danh sách codec của bạn! video/webm;codecs=opus,vp8

Bắt nó hoạt động trong Safari phức tạp hơn đáng kể. MediaRecorder là một tính năng "thử nghiệm" phải được bật thủ công trong các tùy chọn nhà phát triển. Sau khi kích hoạt, Safari thiếu một isTypeSupportedphương thức, vì vậy bạn cần xử lý nó. Cuối cùng, bất kể bạn yêu cầu gì từ MediaRecorder, Safari sẽ luôn cung cấp cho bạn một tệp MP4 - không thể truyền phát theo cách WEBM có thể. Điều này có nghĩa là bạn cần thực hiện chuyển đổi trong JavaScript để chuyển đổi định dạng bộ chứa video một cách nhanh chóng

Android sẽ hoạt động nếu Chrome hoạt động

iOS không hỗ trợ Tiện ích mở rộng nguồn phương tiện, do đó, SourceBufferkhông được xác định trên iOS và toàn bộ giải pháp sẽ không hoạt động

Bài gốc

Nhìn vào JSFiddle mà bạn đã đăng, một cách khắc phục nhanh trước khi chúng tôi bắt đầu:

  • Bạn tham chiếu một biến errorMsgElementkhông bao giờ được xác định. Bạn nên thêm một <div>trang vào một ID thích hợp và sau đó tạo một const errorMsgElement = document.querySelector(...)dòng để chụp nó

Bây giờ một điều cần lưu ý khi làm việc với Tiện ích mở rộng nguồn phương tiện và MediaRecorder là hỗ trợ sẽ rất khác nhau trên mỗi trình duyệt. Mặc dù đây là một phần "được tiêu chuẩn hóa" của thông số HTML5, nhưng nó không nhất quán trên các nền tảng. Theo kinh nghiệm của tôi, việc MediaRecorder hoạt động trong Firefox không tốn quá nhiều công sức, để nó hoạt động trong Chrome khó hơn một chút, để nó hoạt động trong Safari là điều gần như không thể, và làm cho nó hoạt động trên iOS theo nghĩa đen là không một cái gì đó bạn có thể làm.

Tôi đã trải qua và gỡ lỗi này trên cơ sở mỗi trình duyệt và ghi lại các bước của tôi, để bạn có thể hiểu một số công cụ có sẵn cho bạn khi gỡ lỗi các vấn đề truyền thông

Firefox

Khi tôi kiểm tra JSFiddle của bạn trong Firefox, tôi đã thấy lỗi sau trong bảng điều khiển:

NotSupportedError: Không thể ghi đoạn âm thanh: video / webm; codecs = vp8 chỉ ra một codec không được hỗ trợ

Tôi nhớ rằng VP8 / VP9 là một cú hích lớn của Google và vì như vậy có thể không hoạt động trong Firefox, vì vậy tôi đã thử thực hiện một điều chỉnh nhỏ cho mã của bạn. Tôi xóa , options)tham số từ cuộc gọi của bạn đến new MediaRecorder(). Điều này cho trình duyệt sử dụng bất kỳ codec nào họ muốn, do đó bạn có thể sẽ nhận được đầu ra khác nhau trong mỗi trình duyệt (nhưng ít nhất nó sẽ hoạt động trong mọi trình duyệt)

Điều này hoạt động trong Firefox, vì vậy tôi đã kiểm tra Chrome.

Trình duyệt Chrome

Lần này tôi gặp một lỗi mới:

(index): 409 Uncaught (trong lời hứa) DOMException: Không thể thực thi 'appendBuffer' trên 'SourceBuffer': SourceBuffer này đã bị xóa khỏi nguồn phương tiện chính. tại MediaRecorder.handleDataAv Available ( https://fiddle.jshell.net/43rm7258/1/show/:409:22 )

Vì vậy, tôi đã chuyển sang chrome: // media-internals / trong trình duyệt của mình và thấy điều này:

Opus codec dòng âm thanh không phù hợp với codec SourceBuffer.

Trong mã của bạn, bạn chỉ định một codec video (VP9 hoặc VP8) nhưng không phải là codec âm thanh, vì vậy MediaRecorder cho phép trình duyệt chọn bất kỳ codec âm thanh nào họ muốn. Theo mặc định, có vẻ như trong MediaRecorder của Chrome chọn "opus" làm codec âm thanh, nhưng SourceBuffer của Chrome theo mặc định lại chọn một thứ khác. Điều này đã được sửa chữa tầm thường. Tôi đã cập nhật hai dòng của bạn để thiết lập options.mimeTypenhư vậy:

  • options = { mimeType: "video/webm;codecs=opus, vp9" };
  • options = { mimeType: "video/webm;codecs=opus, vp8" };

Vì bạn sử dụng cùng một optionsđối tượng để khai báo MediaRecorder và SourceBuffer, nên thêm codec âm thanh vào danh sách có nghĩa là SourceBuffer hiện được khai báo với codec âm thanh hợp lệ và video phát

Để có biện pháp tốt, tôi đã thử nghiệm mã mới (có codec âm thanh) trên Firefox. Điều này đã làm việc! Vì vậy, chúng tôi là 2 cho 2 chỉ bằng cách thêm codec âm thanh vào optionsdanh sách (và để nó trong các tham số để khai báo MediaRecorder)

Có vẻ như VP8 và opus hoạt động trong Firefox, nhưng không phải mặc định (mặc dù không giống như Chrome, mặc định cho MediaRecorder và SourceBuffer là như nhau, đó là lý do tại sao xóa optionstham số hoàn toàn hoạt động)

Safari

Lần này chúng tôi đã gặp một lỗi mà chúng tôi không thể khắc phục được:

Từ chối lời hứa chưa được xử lý: ReferenceError: Không thể tìm thấy biến: MediaRecorder

Điều đầu tiên tôi làm là Google "Safari MediaRecorder", đã bật lên bài viết này . Tôi nghĩ tôi sẽ thử, vì vậy tôi đã xem qua. Đảm bảo đủ:

Thuộc tính Safari

Tôi đã bấm vào đây để kích hoạt MediaRecorder và đã gặp những điều sau trong bảng điều khiển:

Từ chối lời hứa chưa được xử lý: TypeError: MediaRecorder.isTypeSupported không phải là một chức năng. (Trong 'MediaRecorder.isTypeSupported (Options.mimeType)', 'MediaRecorder.isTypeSupported' không xác định)

Vì vậy, Safari không có isTypeSupportedphương pháp. Đừng lo lắng, chúng tôi sẽ chỉ nói "nếu phương thức này không tồn tại, giả sử đó là Safari và đặt loại phù hợp"

  if (MediaRecorder.isTypeSupported) {
    options = { mimeType: "video/webm;codecs=vp9" };
    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
      console.error(`${options.mimeType} is not Supported`);
      errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
      options = { mimeType: "video/webm;codecs=vp8" };
      if (!MediaRecorder.isTypeSupported(options.mimeType)) {
        console.error(`${options.mimeType} is not Supported`);
        errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
        options = { mimeType: "video/webm" };
        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
          console.error(`${options.mimeType} is not Supported`);
          errorMsgElement.innerHTML = `${options.mimeType} is not Supported`;
          options = { mimeType: "" };
        }
      }
    }
  } else {
    options = { mimeType: "" };
  }

Bây giờ tôi chỉ cần tìm một mimeType mà Safari hỗ trợ. Một số Googling nhẹ gợi ý rằng H.264 được hỗ trợ, vì vậy tôi đã thử:

options = { mimeType: "video/webm;codecs=h264" };

Điều này đã cho tôi thành công MediaRecorder started, nhưng thất bại ở dòng addSourceBuffercó lỗi mới:

NotSupportedError: Thao tác không được hỗ trợ.

Tôi sẽ tiếp tục thử và chẩn đoán cách để ứng dụng này hoạt động trong Safari, nhưng hiện tại tôi đã ít nhất giải quyết được Firefox và Chrome

Cập nhật 1

Tôi đã tiếp tục làm việc trên Safari. Thật không may, Safari thiếu công cụ của Chrome và Firefox để đào sâu vào các phương tiện truyền thông, do đó, có rất nhiều phỏng đoán liên quan.

Trước đây tôi đã nhận ra rằng chúng tôi đã gặp lỗi "Thao tác không được hỗ trợ" khi cố gắng gọi addSourceBuffer. Vì vậy, tôi đã tạo một trang một lần để thử và chỉ gọi phương thức này trong các trường hợp khác nhau:

  • Có thể thêm bộ đệm nguồn trước khi playđược gọi trên video
  • Có thể thêm bộ đệm nguồn trước khi nguồn phương tiện được gắn vào thành phần video
  • Có thể thêm một bộ đệm nguồn với các codec khác nhau
  • Vân vân

Tôi thấy rằng vấn đề vẫn là codec và thông báo lỗi về "hoạt động" không được phép là hơi sai lệch. Đó là các thông số không được phép. Đơn giản chỉ cần cung cấp "h264" cho MediaRecorder, nhưng SourceBuffer cần tôi chuyển các tham số codec .

Một trong những điều đầu tiên tôi đã thử là hướng đến trang mẫu MDN và sao chép các codec họ sử dụng ở đó : 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'. Điều này đã đưa ra cùng một lỗi "hoạt động không được phép". Đào sâu vào ý nghĩa của các thông số codec (như những gì heck không 42E01Ethậm chí có ý nghĩa ?). Trong khi tôi ước mình có câu trả lời tốt hơn, trong khi Googling nó, tôi tình cờ thấy bài đăng StackOverflow này được đề cập bằng cách sử dụng 'video/mp4; codecs="avc1.64000d,mp4a.40.2"'trên Safari. Tôi đã thử nó và các lỗi giao diện điều khiển đã biến mất!

Mặc dù các lỗi giao diện điều khiển đã biến mất nhưng tôi vẫn không thấy bất kỳ video nào. Vì vậy, vẫn còn việc phải làm.

Cập nhật 2

Nghiên cứu sâu hơn về Trình gỡ lỗi trong Safari (đặt nhiều điểm dừng và kiểm tra các biến ở mỗi bước của quy trình) thấy rằng handleDataAvailablekhông bao giờ được gọi trong Safari. Có vẻ như trong Firefox và Chrome mediaRecorder.start(100)sẽ tuân thủ đúng thông số kỹ thuật và gọi ondatavailablecứ sau 100 mili giây, nhưng Safari bỏ qua tham số và đệm mọi thứ vào một Blob lớn. Gọi mediaRecorder.stop()thủ công gây ra ondataavailableđược gọi với tất cả mọi thứ đã được ghi lại cho đến thời điểm đó

Tôi đã thử sử dụng setIntervalđể gọi mediaRecorder.requestData()cứ sau 100 mili giây, nhưng requestDatakhông được xác định trong Safari (giống như cách isTypeSupportedkhông được xác định). Điều này đặt tôi vào một chút ràng buộc.

Tiếp theo, tôi đã cố gắng dọn sạch toàn bộ đối tượng MediaRecorder và tạo một đối tượng mới cứ sau 100 mili giây, nhưng điều này đã gây ra lỗi trên dòng await bufferedBlob.arrayBuffer(). Tôi vẫn đang điều tra lý do tại sao một thất bại

Cập nhật 3

Một điều tôi nhớ về định dạng MP4 là nguyên tử "moov" được yêu cầu để phát lại bất kỳ nội dung nào. Đây là lý do tại sao bạn không thể tải xuống nửa giữa của tệp MP4 và phát nó. Bạn cần tải tập tin WHOLE. Vì vậy, tôi tự hỏi nếu thực tế rằng tôi chọn MP4 là lý do tôi không nhận được cập nhật thường xuyên.

Tôi đã thử thay đổi video/mp4thành một vài giá trị khác nhau và nhận được kết quả khác nhau:

  • video/webm - Hoạt động không được hỗ trợ
  • video/x-m4v- Hành xử như MP4, tôi chỉ nhận được dữ liệu khi .stop()được gọi
  • video/3gpp - Hành xử như MP4
  • video/flv - Hoạt động không được hỗ trợ
  • video/mpeg - Hành xử như MP4

Mọi thứ hoạt động như MP4 khiến tôi phải kiểm tra dữ liệu thực sự được truyền đến handleDataAvailable. Đó là khi tôi nhận thấy điều này:

nhập mô tả hình ảnh ở đây

Không có vấn đề tôi chọn để định dạng video, Safari luôn đem lại cho tôi một MP4!

Đột nhiên tôi nhớ tại sao Safari lại là một cơn ác mộng như vậy và tại sao tôi lại phân loại nó thành "chết tiệt - gần như không thể". Để kết hợp một số MP4 sẽ cần một bộ truyền JavaScript

Đó là khi tôi nhớ, đó chính xác là những gì tôi đã làm trước đây . Tôi đã làm việc với MediaRecorder và SourceBuffer chỉ hơn một năm trước để thử và tạo trình phát JavaScript RTMP. Khi trình phát đã hoàn tất, tôi muốn thêm hỗ trợ cho DVR (tìm kiếm lại các phần của video đã được phát trực tuyến), điều mà tôi đã làm bằng cách sử dụng MediaRecorder và giữ bộ đệm vòng trong bộ nhớ của các đốm video 1 giây. Trên Safari, tôi đã chạy các đốm video này thông qua bộ truyền mà tôi đã mã hóa để chuyển đổi chúng từ MP4 sang ISO-BMFF để tôi có thể ghép chúng lại với nhau.

Tôi ước tôi có thể chia sẻ mã với bạn, nhưng tất cả đều thuộc sở hữu của chủ nhân cũ của tôi - vì vậy tại thời điểm này, giải pháp đã bị mất đối với tôi. Tôi biết rằng ai đó đã trải qua sự cố khi biên dịch FFMPEG thành JavaScript bằng cách sử dụng emscripten, vì vậy bạn có thể tận dụng lợi thế đó.


Ồ Cám ơn bạn rất nhiều về điều này. Tôi thực sự ngạc nhiên bởi những nỗ lực bạn bỏ ra. Tôi sẽ xem xét những phát hiện của bạn và xem liệu tôi có thể khiến mọi thứ hoạt động được không.
André

1
Với các gợi ý của bạn Tôi thực sự có nó hoạt động trong Chrome, tôi đã gần với giải pháp nhưng không biết làm thế nào để tiếp tục với nó. Cảm ơn đã giải thích các bước bạn đã thực hiện để theo dõi nó. Cảm ơn một lần nữa, câu trả lời của bạn thực sự hữu ích và tôi đã học được rất nhiều.
André
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.