apache / cordova-plugin-media-capture

Apache Cordova Media Capture Plugin
https://cordova.apache.org/
Apache License 2.0
307 stars 489 forks source link

cordova-plugin-media-capture video not working android 13 #286

Open CaiqueNds opened 7 months ago

CaiqueNds commented 7 months ago

Bug Report

cordova-plugin-media-capture video not working android 13

Problem

In new versions of Android 13, it is not possible to move video files to the application directory, as the plugin is returning videos from the path file:///storage/emulated/0/DCIM/Camera/

What is expected to happen?

The returned path is expected to be: file:///data/user/0/myapp/cache/

What does actually happen?

the path returned by the plugin file:///storage/emulated/0/DCIM/Camera/

Information

use an android 13 and make a video. Check whether it will be stored in the dcim folder or in the application cache android bug sample: MicrosoftTeams-image

Command or Code

you need to use the cordova-media-capture plugin and make a video and check that the video path is correct

Environment, Platform, Device

android 13 Samsung

Version information

cordova-android 12.0.0 ionic 4 android

Checklist

breautek commented 7 months ago

In new versions of Android 13, it is not possible to move video files to the application directory, as the plugin is returning videos from the path file:///storage/emulated/0/DCIM/Camera/

To be clear, under scoped storage rules (which is effective since API 29) you wouldn't be able to move, but you should be able to copy it using the file plugin.

Files recorded will be owned by the camera app rather than your own app, therefore all write operations via the filesystem is restricted which would include the move operation.

v5 of this plugin should already request the READ_MEDIA_VIDEO permission so that the recorded video can be read.

The plugin storing at /storage/emulated/0/DCIM/Camera/ is nothing new, in fact it's not the plugin that decides this location by the underlying camera app.

CaiqueNds commented 7 months ago

I can't copy files from dcim to my directory within the app in this specific case even if I try to copy the file instead of moving it using cordova-plugin-file

This error appears to me: image

This error is being presented in newer Android 13 versions with Samsung. Other versions such as Xiaomi or Google Pixel do not occur. Or even older Android 13 with Samsung

sample video file path: image application file path: image

breautek commented 7 months ago

INVALID_MODIFICATION_ERR should only be thrown if you're attempting to modify the file I believe..

Can you share the code that produces this error?

CaiqueNds commented 7 months ago

This is where the error occurs and I capture the video from media capture and pass it to the plugin file:


async criaEstruturaDiretoriosCopiaArquivo(
    new_path: string,
    path_file: string,
    file_name: string,
    gerar_nome_random: boolean = true
  ): Promise<string> {
    let new_path_file = '';
    //This is where I set the path where the file will be saved
    await this.criaCaminhoDiretorio(new_path).then((x) => {
      new_path_file = x;
    });

    const new_file_name = !gerar_nome_random
      ? file_name
      : (Math.floor(Math.random() * (10000 - 1 + 1)) + 1).toString() +
        "." +
        file_name.slice(file_name.lastIndexOf(".") + 1); 
    return this.file
      .copyFile(path_file, file_name, new_path_file, new_file_name)
      .then(() => {
        return new_path + "/" + new_file_name;
      })
      .catch((err) => {
        console.error(err);
        return "";
      });
  }`
breautek commented 7 months ago

Thanks for your co-operation, it's helping me understand the full picture.

based on the provided code, this does appear to be a bug...

On the topic of changing the default path, we were experimenting with PR #277 though I believe that PR only changed the audio recording directory to the app's internal cache directory.

I think the plugin as it stands now gets a content url which it resolves to a file url which gets passed to the webview. Perhaps we can learn from that PoC PR and use the content resolver and get the input stream so that plugin can copy the content using the media store api to the app's cache directory then it can return the file url to the cache directory instead. There is a performance concern if you're copying a large video file... but it will be necessary if we want to use the file plugin to access the file I think.

The plugin I don't think exposes the content url, but if it did, then a media store plugin could be used to do file manipulation operations as well. This could allow us to avoid copying the file unless explicitly desired by the developer... as an alternate solution...

CaiqueNds commented 6 months ago

I partially solved it by taking this file, generating a blob of it and using the file plugin to record it within my app

CaiqueNds commented 6 months ago

my solution was. Instead of copying or moving the file received by the video plugin from the device's camera folder. It was creating a blob of this file and writing this blob directly to the desired path..

async workAroundBugGravacaoVideo(
    path_file,
    file_name,
    new_path_file,
    new_file_name
  ) {
    const destinationDirectoryEntry = await this.file.resolveDirectoryUrl(
      new_path_file
    );
    const originDirectoryEntry = await this.file.resolveDirectoryUrl(path_file);
    const fileEntry = await this.file.getFile(originDirectoryEntry, file_name, {
      create: false,
    });

    return this.saveBinaryFile(
      fileEntry,
      destinationDirectoryEntry,
      new_file_name
    );
  }
 saveBinaryFile(fileEntry, dirEntry, fileName) {
    return new Promise((resolve, reject) => {
      fileEntry.file(function (file) {
        var reader = new FileReader();
        reader.readAsArrayBuffer(file);
        dirEntry.getFile(
          fileName,
          { create: true, exclusive: false },
          function (fileEntry) {
            fileEntry.createWriter(function (fileWriter) {
              fileWriter.onwriteend = function () {
                resolve(null);
              };
              fileWriter.onerror = function (e) {
                console.log("Failed file write: " + e.toString());
                reject();
              };
              fileWriter.write(file);
            });
          }
        );
      });
    });
  }