微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将 MediaRecorder 分块到 Google Cloud 平台时视频上传损坏

如何解决将 MediaRecorder 分块到 Google Cloud 平台时视频上传损坏

我目前正在使用 react hook 驱动的组件来记录我的屏幕,然后将其上传到 Google Cloud Storage。但是,当它完成时,在 Google Cloud 中创建的文件似乎已损坏。

这是我的 React 组件中代码的要点,其中 useMediaRecorder 来自此处:https://github.com/wmik/use-media-recorder -

let {
    error,status,mediaBlob,stopRecording,getMediaStream,startRecording,liveStream,} = useMediaRecorder({
    onCancelScreenShare: () => {
      stopRecording();
    },onDataAvailable: (chunk) => {
      // do the uploading here:
      onChunk(chunk);
    },recordScreen: true,blobOptions: { type: "video/webm;codecs=vp8,opus" },mediaStreamConstraints: { audio: audioEnabled,video: true },});

当数据通过这个钩子变得可用时——它调用 onChunk(chunk) 将一个二进制 Blob 传递给那个方法,以执行上传,我结合这部分代码来执行上传

 const onChunk = (binaryData) => {
    var formData = new FormData();
    formData.append("data",binaryData);
    let customerApi = new CustomerVideoApi();
    customerApi.uploadRecording(
      videoUUID,formData,(res) => {},(err) => {}
    );
  };

customerApi.uploadRecording 看起来像这样(使用 axios)。

const uploadRecording = (uuid,data,fn,fnErr) => {
    axios
      .post(endpoint + "/stream/upload",{
        headers: {
          "Content-Type": "multipart/form-data",},})
      .then(function (response) {
        fn(response);
      })
      .catch(function (error) {
        fnErr(error.response);
      });
  };

HTTP 请求成功,一切顺利:要上传的服务器端代码基于 Laravel:

// 这是控制器内部。

 public function index( Request $request )
    {

            // Set file attributes.
            $filepath = '/public/chunks/';
            $file = $request->file('data');
            $filename = $uuid . ".webm"; 
          
            // streamupload
            File::streamUpload($filepath,$filename,$file,true);

            return response()->json(['uploaded' => true,'uuid'=>$uuid]);
      
    }

// 有一个服务提供者用于在 File:: 对象上创建一个新的宏,提供适当处理流的工具:

public function boot()
    {
        File::macro('streamUpload',function($path,$fileName,$overWrite = true) {
            
            $resource = fopen($file->getRealPath(),'r+');
           
            $storageClient = new StorageClient([
                'projectId' => 'myprjectid','keyFilePath' => '/my/path/to/servicejson.json',]);
            
            $bucket = $storageClient->bucket('mybucket');
            $adapter = new GoogleStorageAdapter($storageClient,$bucket);
            $filesystem = new Filesystem($adapter);

            return $overWrite 
                    ? $filesystem->putStream($fileName,$resource) 
                    : $filesystem->writeStream($fileName,$resource);
        });
    }

重申:

  1. React 应用程序将 blob 分块,
  2. 服务器端决定是否应该在 Google Cloud Storage 中创建或附加
  3. 服务器端成功。

4) Google Cloud 平台内的视频已损坏。

但是,Google Cloud 容器内的视频文件已损坏,无法播放。我不确定它为什么会损坏,但到目前为止我的猜测是:

  1. 某种 Dodgy Mime 类型问题.. - 不同的浏览器似乎对编解码器/文件类型的处理与媒体记录器不同:例如Chrome 似乎是 x-matroska (.mkv?) - firefox 又不同了.. 理想情况下我会有一个 .webm 的容器 - 注意我如何设置文件名服务器端,它不是来自客户端。应该是?我不确定如何强制 MediaRecorder 成为特定的 mimeType - 我认为 blobOptions 选项应该这样做,但更改扩展名和 mime 类型似乎对发生的损坏几乎没有影响。

  2. 上传过程中出现的某种问题,其中 HTTP 请求未按顺序执行和完成 - 例如

1 onDataAvailable completes second
2 onDataAvailable completes first
3 onDataAvailable completes third

我已经排除了这一点,因为我认为块应该足够小。

  1. 我正在使用的 Google Cloud Storage API 存在某种问题,可能是使用了错误的方式?云平台是否支持流式传输,该库是否发送了正确的参数?

  2. 上传的方式存在某种问题 - axios 标头应该是多部分表单数据还是其他内容

这是我用于服务器端的包:https://github.com/Superbalist/flysystem-google-cloud-storage

谁能说明如何在不损坏媒体记录器中的视频的情况下实现流式传输到 Google Cloud 的目标?希望问题中有足够的细节来帮助弄清楚。如图所示,问题不在于将文件获取到谷歌云,而是生成文件无法以任何视频格式播放。

更新

我现在已经订购了我的块客户端,并在让它们到达服务器之前将它们正确排队。输出没有区别。正如一些人所建议的 - 单个 blob 上传请求工作正常。

尝试使用可流式配置参数(从阅读源代码来看,在 Google 将它们识别为可恢复上传之前,块似乎需要具有特定大小

$filesystem = new Filesystem($adapter,[ '可恢复'=> 真 ]);

不确定如何:https://cloud.google.com/storage/docs/performing-resumable-uploads - 在我使用的库中实现(或者在 Google Cloud API 本身中实现?)。我需要自己实现吗? Google 方面的文档很少。

解决方法

简短版本: 您应该做的第一件事是在本地缓冲整个视频,然后将单个有效负载发送到服务器和谷歌驱动器。这将验证您的小视频代码实际上是正确的。一旦您可以验证这一点,您就可以继续处理多块上传。

更长的版本: 首先,您没有将 uuid 传递给请求,它正在被使用:

const uploadRecording = (uuid,data,fn,fnErr) => {
    axios
      .post(endpoint + "/stream/upload",{
        headers: {
          "Content-Type": "multipart/form-data",},})
      .then(function (response) {
        fn(response);
      })
      .catch(function (error) {
        fnErr(error.response);
      });
  };

接下来,你不能相信分块是如何工作的,我想你用分块记录的乱序结果验证了这个行为。你需要假设在你的服务器上你会得到乱序的块并正确处理它们。

您在服务器上获得的每个块都需要放在正确的位置,您不能只是“writeStream”,您需要写入显式二进制块。具体来说,在每个请求上指定字节范围:Google docs:

curl -i -X PUT --data-binary @CHUNK_LOCATION \
    -H "Content-Length: CHUNK_SIZE" \
    -H "Content-Range: bytes CHUNK_FIRST_BYTE-CHUNK_LAST_BYTE/TOTAL_OBJECT_SIZE" \
    "SESSION_URI"
  • CHUNK_LOCATION 是本地路径 您当前正在上传的块。
  • CHUNK_SIZE 是 您在当前请求中上传的字节数。例如,524288。
  • CHUNK_FIRST_BYTE 是 您正在上传的块的整体对象中的起始字节
  • CHUNK_LAST_BYTE 是 您上传的块包含的整体对象。
  • TOTAL_OBJECT_SIZE 是总大小 您正在上传的对象。
  • SESSION_URI 是在 启动可续传上传时的位置标头。
,

尝试消除尽可能多的变量并查明文件损坏的确切位置。

由于您使用的是 React(JS) -> Laravel(PHP) -> GoogleCloud 路径, 我建议的第一件事是分别测试每个步骤:

  • React -> Laravel - 将文件保存在您的服务器上并检查此时是否已损坏
  • Laravel -> GoogleCloud - 从服务器文件系统加载文件并上传到云端,看看它是否被损坏

我没有使用谷歌云的经验,但我对 AWS 做了一些非常相似的事情,发现他们的视频上传服务对请求(包括发送的标头的顺序)非常挑剔。 尝试将您正在使用的服务的规范与您的输入进行比较,使尽可能小的东西有效并开始添加变量,直到达到最终状态。

此外,我在您的代码中没有看到任何类型的数据排序。 如果您的数据块彼此靠近,并且使用流式传输很有可能,那么它们到达的顺序可能会与最初发送的顺序不同。如果您只是将它们附加到文件中而没有对排序进行任何控制,那么该文件确实会损坏。不确定对于 webm 是否会导致视频的部分损坏或整个视频死亡。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。