MOBILE-2314 core: Remove cordova-plugin-media
parent
4013855ae9
commit
495d395beb
|
@ -196,11 +196,6 @@
|
|||
<param name="android-package" value="com.adobe.phonegap.push.PushPlugin" />
|
||||
</feature>
|
||||
</config-file>
|
||||
<config-file parent="/*" target="res/xml/config.xml">
|
||||
<feature name="Media">
|
||||
<param name="android-package" value="org.apache.cordova.media.AudioHandler" />
|
||||
</feature>
|
||||
</config-file>
|
||||
<config-file parent="/*" target="AndroidManifest.xml">
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false" />
|
||||
</config-file>
|
||||
|
|
|
@ -4167,21 +4167,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@ionic-native/media": {
|
||||
"version": "5.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic-native/media/-/media-5.36.0.tgz",
|
||||
"integrity": "sha512-WIDCeUlX7bCbse/x2Rr7mAIQJnLo18ZWcmsVgSTTBVS7ObU2DBl4ieqRx6y9PAAV+3tNZqMV4JAWDfMiFokpJg==",
|
||||
"requires": {
|
||||
"@types/cordova": "^0.0.34"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/cordova": {
|
||||
"version": "0.0.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
|
||||
"integrity": "sha512-rkiiTuf/z2wTd4RxFOb+clE7PF4AEJU0hsczbUdkHHBtkUmpWQpEddynNfJYKYtZFJKbq4F+brfekt1kx85IZA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@ionic-native/media-capture": {
|
||||
"version": "5.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@ionic-native/media-capture/-/media-capture-5.36.0.tgz",
|
||||
|
@ -14340,11 +14325,6 @@
|
|||
"resolved": "https://registry.npmjs.org/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.2.0.tgz",
|
||||
"integrity": "sha512-yDUG+9ieKVRitq5mGlNxjaZh/MgEhFFIgTIPhqSbUaQ8UuZbawy5mhJAVClqY97q8/rcQtL6dCDa7x2sEtCLcA=="
|
||||
},
|
||||
"cordova-plugin-media": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-media/-/cordova-plugin-media-5.0.4.tgz",
|
||||
"integrity": "sha512-mAqincYqOT5gu5LWyfgJu3qmOq+lhLAKhnOZULpG622FvYiHjjfsoJ/fkI55WwI3FIcHeeyhToGvHXBCNJePZg=="
|
||||
},
|
||||
"cordova-plugin-media-capture": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cordova-plugin-media-capture/-/cordova-plugin-media-capture-3.0.3.tgz",
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
"@ionic-native/ionic-webview": "5.36.0",
|
||||
"@ionic-native/keyboard": "5.36.0",
|
||||
"@ionic-native/local-notifications": "5.36.0",
|
||||
"@ionic-native/media": "5.36.0",
|
||||
"@ionic-native/media-capture": "5.36.0",
|
||||
"@ionic-native/network": "5.36.0",
|
||||
"@ionic-native/push": "5.36.0",
|
||||
|
@ -104,7 +103,6 @@
|
|||
"cordova-plugin-file": "6.0.2",
|
||||
"cordova-plugin-geolocation": "4.1.0",
|
||||
"cordova-plugin-ionic-keyboard": "2.2.0",
|
||||
"cordova-plugin-media": "5.0.4",
|
||||
"cordova-plugin-media-capture": "3.0.3",
|
||||
"cordova-plugin-network-information": "3.0.0",
|
||||
"cordova-plugin-prevent-override": "1.0.1",
|
||||
|
@ -225,9 +223,6 @@
|
|||
"ANDROID_SUPPORT_V4_VERSION": "26.+"
|
||||
},
|
||||
"cordova-plugin-media-capture": {},
|
||||
"cordova-plugin-media": {
|
||||
"KEEP_AVAUDIOSESSION_ALWAYS_ACTIVE": "NO"
|
||||
},
|
||||
"cordova-plugin-network-information": {},
|
||||
"@moodlehq/cordova-plugin-qrscanner": {},
|
||||
"cordova-plugin-splashscreen": {},
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<core-loading [hideUntil]="readyToCapture">
|
||||
<div class="core-av-wrapper">
|
||||
<!-- Video stream for image and video. -->
|
||||
<video *ngIf="!isAudio" [hidden]="hasCaptured" class="core-webcam-stream" autoplay #streamVideo></video>
|
||||
<video [hidden]="hasCaptured" class="core-webcam-stream" autoplay #streamVideo></video>
|
||||
|
||||
<!-- For video recording, use 2 videos and show/hide them because a CSS rule caused problems with the controls. -->
|
||||
<video *ngIf="isVideo" [hidden]="!hasCaptured" class="core-webcam-video-captured" controls #previewVideo
|
||||
|
@ -25,21 +25,6 @@
|
|||
<canvas *ngIf="isImage" class="core-webcam-image-canvas" #imgCanvas></canvas>
|
||||
<img *ngIf="isImage" [hidden]="!hasCaptured" class="core-webcam-image" alt="{{ 'core.capturedimage' | translate }}"
|
||||
#previewImage>
|
||||
|
||||
<!-- Recording audio. -->
|
||||
<div *ngIf="isAudio" class="core-audio-record-container">
|
||||
<!-- Canvas to show audio waves when recording audio in browser. -->
|
||||
<canvas [hidden]="hasCaptured || isCordovaAudioCapture" class="core-audio-canvas" #streamAudio></canvas>
|
||||
|
||||
<!-- Button to start/stop in mobile devices. -->
|
||||
<ion-button fill="clear" *ngIf="!hasCaptured && isCordovaAudioCapture" (click)="actionClicked()" [attr.aria-label]="title">
|
||||
<ion-icon *ngIf="!isCapturing" name="fas-microphone" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
<ion-icon *ngIf="isCapturing" name="fas-square" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
|
||||
<!-- Audio player to listen to the result. -->
|
||||
<audio [hidden]="!hasCaptured" class="core-audio-captured" controls #previewAudio controlsList="nodownload"></audio>
|
||||
</div>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
@ -48,8 +33,7 @@
|
|||
<ion-row>
|
||||
<ion-col></ion-col>
|
||||
<ion-col class="ion-text-center">
|
||||
<ion-button fill="clear" *ngIf="!hasCaptured && !isCordovaAudioCapture" (click)="actionClicked()" [attr.aria-label]="title">
|
||||
<ion-icon *ngIf="!isCapturing && isAudio" name="fas-microphone" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" *ngIf="!hasCaptured" (click)="actionClicked()" [attr.aria-label]="title">
|
||||
<ion-icon *ngIf="!isCapturing && isVideo" name="fas-video" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
<ion-icon *ngIf="isImage" name="fas-camera" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
<ion-icon *ngIf="isCapturing" name="fas-square" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
|
|
|
@ -21,30 +21,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
|
||||
.icon {
|
||||
font-size: 120px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
audio {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
video, img {
|
||||
|
|
|
@ -13,23 +13,20 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, ViewChild, ElementRef, ChangeDetectorRef, Input } from '@angular/core';
|
||||
import { MediaObject } from '@ionic-native/media/ngx';
|
||||
import { FileEntry } from '@ionic-native/file/ngx';
|
||||
import { MediaFile } from '@ionic-native/media-capture/ngx';
|
||||
|
||||
import { CoreFile, CoreFileProvider } from '@services/file';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||
import { CoreTimeUtils } from '@services/utils/time';
|
||||
import { ModalController, Media, Translate } from '@singletons';
|
||||
import { ModalController, Translate } from '@singletons';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreCaptureError } from '@classes/errors/captureerror';
|
||||
import { CoreCanceledError } from '@classes/errors/cancelederror';
|
||||
import { CorePath } from '@singletons/path';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
|
||||
/**
|
||||
* Page to capture media in browser, or to capture audio in mobile devices.
|
||||
* Page to capture media in browser.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-emulator-capture-media',
|
||||
|
@ -38,7 +35,7 @@ import { CorePlatform } from '@services/platform';
|
|||
})
|
||||
export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input() type?: 'audio' | 'video' | 'image' | 'captureimage';
|
||||
@Input() type?: 'video' | 'image' | 'captureimage';
|
||||
@Input() maxTime?: number; // Max time to capture.
|
||||
@Input() facingMode?: string; // Camera facing mode.
|
||||
@Input() mimetype?: string;
|
||||
|
@ -50,30 +47,20 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
@ViewChild('previewVideo') previewVideo?: ElementRef;
|
||||
@ViewChild('imgCanvas') imgCanvas?: ElementRef;
|
||||
@ViewChild('previewImage') previewImage?: ElementRef;
|
||||
@ViewChild('streamAudio') streamAudio?: ElementRef;
|
||||
@ViewChild('previewAudio') previewAudio?: ElementRef;
|
||||
|
||||
title?: string; // The title of the page.
|
||||
isAudio?: boolean; // Whether it should capture audio.
|
||||
isVideo?: boolean; // Whether it should capture video.
|
||||
isImage?: boolean; // Whether it should capture image.
|
||||
readyToCapture?: boolean; // Whether it's ready to capture.
|
||||
hasCaptured?: boolean; // Whether it has captured something.
|
||||
isCapturing?: boolean; // Whether it's capturing.
|
||||
resetChrono?: boolean; // Boolean to reset the chrono.
|
||||
isCordovaAudioCapture?: boolean; // Whether it's capturing audio using Cordova plugin.
|
||||
|
||||
protected isCaptureImage?: boolean; // To identify if it's capturing an image using media capture plugin (instead of camera).
|
||||
protected mediaRecorder?: MediaRecorder; // To record video/audio.
|
||||
protected previewMedia?: HTMLAudioElement | HTMLVideoElement; // The element to preview the audio/video captured.
|
||||
protected mediaRecorder?: MediaRecorder; // To record video.
|
||||
protected previewMedia?: HTMLVideoElement; // The element to preview the video captured.
|
||||
protected mediaBlob?: Blob; // A Blob where the captured data is stored.
|
||||
protected localMediaStream?: MediaStream;
|
||||
protected audioDrawer?: {start: () => void; stop: () => void }; // To start/stop the display of audio sound.
|
||||
|
||||
// Variables for Cordova Media capture.
|
||||
protected mediaFile?: MediaObject;
|
||||
protected filePath?: string;
|
||||
protected fileEntry?: FileEntry;
|
||||
|
||||
constructor(
|
||||
protected changeDetectorRef: ChangeDetectorRef,
|
||||
|
@ -84,12 +71,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
*/
|
||||
ngOnInit(): void {
|
||||
this.initVariables();
|
||||
|
||||
if (this.isCordovaAudioCapture) {
|
||||
this.initCordovaMediaPlugin();
|
||||
} else {
|
||||
this.initHtmlCapture();
|
||||
}
|
||||
this.initHtmlCapture();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,71 +90,10 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
if (this.type == 'video') {
|
||||
this.isVideo = true;
|
||||
this.title = 'core.capturevideo';
|
||||
} else if (this.type == 'audio') {
|
||||
this.isAudio = true;
|
||||
this.title = 'core.captureaudio';
|
||||
} else if (this.type == 'image') {
|
||||
this.isImage = true;
|
||||
this.title = 'core.captureimage';
|
||||
}
|
||||
|
||||
this.isCordovaAudioCapture = CorePlatform.isMobile() && this.isAudio;
|
||||
|
||||
if (this.isCordovaAudioCapture) {
|
||||
this.extension = CorePlatform.is('ios') ? 'wav' : 'aac';
|
||||
this.returnDataUrl = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init recording with Cordova media plugin.
|
||||
*
|
||||
* @returns Promise resolved when ready.
|
||||
*/
|
||||
protected async initCordovaMediaPlugin(): Promise<void> {
|
||||
|
||||
try {
|
||||
await this.createFileAndMediaInstance();
|
||||
|
||||
this.readyToCapture = true;
|
||||
this.previewMedia = this.previewAudio?.nativeElement;
|
||||
} catch (error) {
|
||||
this.dismissWithError(-1, error.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file entry and the cordova media instance.
|
||||
*/
|
||||
protected async createFileAndMediaInstance(): Promise<void> {
|
||||
this.filePath = this.getFilePath();
|
||||
|
||||
// First create the file.
|
||||
this.fileEntry = await CoreFile.createFile(this.filePath);
|
||||
|
||||
// Now create the media instance.
|
||||
let absolutePath = CorePath.concatenatePaths(CoreFile.getBasePathInstant(), this.filePath);
|
||||
|
||||
if (CorePlatform.is('ios')) {
|
||||
// In iOS we need to remove the file:// part.
|
||||
absolutePath = absolutePath.replace(/^file:\/\//, '');
|
||||
}
|
||||
|
||||
this.mediaFile = Media.create(absolutePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the file and the cordova media instance.
|
||||
*/
|
||||
protected async resetCordovaMediaCapture(): Promise<void> {
|
||||
if (this.filePath) {
|
||||
// Remove old file, don't block the user for this.
|
||||
CoreFile.removeFile(this.filePath);
|
||||
}
|
||||
|
||||
this.mediaFile?.release();
|
||||
|
||||
await this.createFileAndMediaInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -183,8 +104,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
*/
|
||||
protected async initHtmlCapture(): Promise<void> {
|
||||
const constraints = {
|
||||
video: this.isAudio ? false : { facingMode: this.facingMode },
|
||||
audio: !this.isImage,
|
||||
video: { facingMode: this.facingMode },
|
||||
};
|
||||
|
||||
try {
|
||||
|
@ -196,22 +116,18 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
if (!this.isImage) {
|
||||
if (this.isVideo) {
|
||||
this.previewMedia = this.previewVideo?.nativeElement;
|
||||
} else {
|
||||
this.previewMedia = this.previewAudio?.nativeElement;
|
||||
this.initAudioDrawer(this.localMediaStream);
|
||||
this.audioDrawer?.start();
|
||||
}
|
||||
|
||||
this.mediaRecorder = new MediaRecorder(this.localMediaStream, { mimeType: this.mimetype });
|
||||
|
||||
// When video or audio is recorded, add it to the list of chunks.
|
||||
// When video is recorded, add it to the list of chunks.
|
||||
this.mediaRecorder.ondataavailable = (e): void => {
|
||||
if (e.data.size > 0) {
|
||||
chunks.push(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
// When recording stops, create a Blob element with the recording and set it to the video or audio.
|
||||
// When recording stops, create a Blob element with the recording and set it to the video.
|
||||
this.mediaRecorder.onstop = (): void => {
|
||||
this.mediaBlob = new Blob(chunks);
|
||||
chunks = [];
|
||||
|
@ -267,91 +183,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the audio drawer. This code has been extracted from MDN's example on MediaStream Recording:
|
||||
* https://github.com/mdn/web-dictaphone
|
||||
*
|
||||
* @param stream Stream returned by getUserMedia.
|
||||
*/
|
||||
protected initAudioDrawer(stream: MediaStream): void {
|
||||
if (!this.streamAudio) {
|
||||
return;
|
||||
}
|
||||
|
||||
let skip = true;
|
||||
let running = false;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const audioCtx = new (window.AudioContext || (<any> window).webkitAudioContext)();
|
||||
const canvasCtx = this.streamAudio.nativeElement.getContext('2d');
|
||||
const source = audioCtx.createMediaStreamSource(stream);
|
||||
const analyser = audioCtx.createAnalyser();
|
||||
const bufferLength = analyser.frequencyBinCount;
|
||||
const dataArray = new Uint8Array(bufferLength);
|
||||
const width = this.streamAudio.nativeElement.width;
|
||||
const height = this.streamAudio.nativeElement.height;
|
||||
const drawAudio = (): void => {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the draw every animation frame.
|
||||
requestAnimationFrame(drawAudio);
|
||||
|
||||
// Skip half of the frames to improve performance, shouldn't affect the smoothness.
|
||||
skip = !skip;
|
||||
if (skip) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sliceWidth = width / bufferLength;
|
||||
let x = 0;
|
||||
|
||||
analyser.getByteTimeDomainData(dataArray);
|
||||
|
||||
canvasCtx.fillStyle = 'rgb(200, 200, 200)';
|
||||
canvasCtx.fillRect(0, 0, width, height);
|
||||
|
||||
canvasCtx.lineWidth = 1;
|
||||
canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
|
||||
|
||||
canvasCtx.beginPath();
|
||||
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
const v = dataArray[i] / 128.0;
|
||||
const y = v * height / 2;
|
||||
|
||||
if (i === 0) {
|
||||
canvasCtx.moveTo(x, y);
|
||||
} else {
|
||||
canvasCtx.lineTo(x, y);
|
||||
}
|
||||
|
||||
x += sliceWidth;
|
||||
}
|
||||
|
||||
canvasCtx.lineTo(width, height / 2);
|
||||
canvasCtx.stroke();
|
||||
};
|
||||
|
||||
analyser.fftSize = 2048;
|
||||
source.connect(analyser);
|
||||
|
||||
this.audioDrawer = {
|
||||
start: (): void => {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
|
||||
running = true;
|
||||
drawAudio();
|
||||
},
|
||||
stop: (): void => {
|
||||
running = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Main action clicked: record or stop recording.
|
||||
*/
|
||||
|
@ -369,15 +200,7 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
this.isCapturing = true;
|
||||
this.resetChrono = false;
|
||||
|
||||
if (this.isCordovaAudioCapture) {
|
||||
this.mediaFile?.startRecord();
|
||||
if (this.previewMedia) {
|
||||
this.previewMedia.src = '';
|
||||
}
|
||||
} else {
|
||||
this.mediaRecorder?.start();
|
||||
}
|
||||
|
||||
this.mediaRecorder?.start();
|
||||
this.changeDetectorRef.detectChanges();
|
||||
} else {
|
||||
if (!this.imgCanvas) {
|
||||
|
@ -419,11 +242,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
|
||||
// Send a "cancelled" error like the Cordova plugin does.
|
||||
this.dismissWithCanceledError('Canceled.', 'Camera cancelled');
|
||||
|
||||
if (this.isCordovaAudioCapture && this.filePath) {
|
||||
// Delete the tmp file.
|
||||
CoreFile.removeFile(this.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -432,11 +250,6 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
async discard(): Promise<void> {
|
||||
this.previewMedia?.pause();
|
||||
this.streamVideo?.nativeElement.play();
|
||||
this.audioDrawer?.start();
|
||||
|
||||
if (this.isCordovaAudioCapture) {
|
||||
await this.resetCordovaMediaCapture();
|
||||
}
|
||||
|
||||
this.hasCaptured = false;
|
||||
this.isCapturing = false;
|
||||
|
@ -492,30 +305,23 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.mediaBlob && !this.isCordovaAudioCapture) {
|
||||
if (!this.mediaBlob) {
|
||||
// Shouldn't happen.
|
||||
CoreDomUtils.showErrorModal('Please capture the media first.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let fileEntry = this.fileEntry;
|
||||
const loadingModal = await CoreDomUtils.showModalLoading();
|
||||
|
||||
try {
|
||||
if (!this.isCordovaAudioCapture) {
|
||||
// Capturing in browser. Write the blob in a file.
|
||||
if (!this.mediaBlob) {
|
||||
// Shouldn't happen.
|
||||
throw new Error('Please capture the media first.');
|
||||
}
|
||||
|
||||
fileEntry = await CoreFile.writeFile(this.getFilePath(), this.mediaBlob);
|
||||
// Capturing in browser. Write the blob in a file.
|
||||
if (!this.mediaBlob) {
|
||||
// Shouldn't happen.
|
||||
throw new Error('Please capture the media first.');
|
||||
}
|
||||
|
||||
if (!fileEntry) {
|
||||
throw new CoreError('File not found.');
|
||||
}
|
||||
const fileEntry = await CoreFile.writeFile(this.getFilePath(), this.mediaBlob);
|
||||
|
||||
if (this.isImage && !this.isCaptureImage) {
|
||||
this.dismissWithData(fileEntry.toURL());
|
||||
|
@ -560,30 +366,20 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Stop capturing. Only for video and audio.
|
||||
* Stop capturing. Only for video.
|
||||
*/
|
||||
stopCapturing(): void {
|
||||
this.isCapturing = false;
|
||||
this.hasCaptured = true;
|
||||
|
||||
if (this.isCordovaAudioCapture) {
|
||||
this.mediaFile?.stopRecord();
|
||||
if (this.previewMedia && this.fileEntry) {
|
||||
this.previewMedia.src = CoreFile.convertFileSrc(this.fileEntry.toURL());
|
||||
}
|
||||
} else {
|
||||
this.streamVideo && this.streamVideo.nativeElement.pause();
|
||||
this.audioDrawer && this.audioDrawer.stop();
|
||||
this.mediaRecorder && this.mediaRecorder.stop();
|
||||
}
|
||||
this.streamVideo && this.streamVideo.nativeElement.pause();
|
||||
this.mediaRecorder && this.mediaRecorder.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.mediaFile?.release();
|
||||
|
||||
if (this.localMediaStream) {
|
||||
const tracks = this.localMediaStream.getTracks();
|
||||
tracks.forEach((track) => {
|
||||
|
@ -592,14 +388,13 @@ export class CoreEmulatorCaptureMediaComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
this.streamVideo?.nativeElement.pause();
|
||||
this.previewMedia?.pause();
|
||||
this.audioDrawer?.stop();
|
||||
delete this.mediaBlob;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type CaptureMediaComponentInputs = {
|
||||
type: 'audio' | 'video' | 'image' | 'captureimage';
|
||||
type: 'video' | 'image' | 'captureimage';
|
||||
maxTime?: number; // Max time to capture.
|
||||
facingMode?: string; // Camera facing mode.
|
||||
mimetype?: string;
|
||||
|
|
|
@ -14,23 +14,18 @@
|
|||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CameraOptions } from '@ionic-native/camera/ngx';
|
||||
import { CaptureAudioOptions, CaptureImageOptions, CaptureVideoOptions, MediaFile } from '@ionic-native/media-capture/ngx';
|
||||
import { CaptureImageOptions, CaptureVideoOptions, MediaFile } from '@ionic-native/media-capture/ngx';
|
||||
|
||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||
import { makeSingleton, ModalController } from '@singletons';
|
||||
import { CaptureMediaComponentInputs, CoreEmulatorCaptureMediaComponent } from '../components/capture-media/capture-media';
|
||||
|
||||
/**
|
||||
* Helper service with some features to capture media (image, audio, video).
|
||||
* Helper service with some features to capture media (image, video).
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreEmulatorCaptureHelperProvider {
|
||||
|
||||
protected possibleAudioMimeTypes = {
|
||||
'audio/webm': 'weba',
|
||||
'audio/ogg': 'ogg',
|
||||
};
|
||||
|
||||
protected possibleVideoMimeTypes = {
|
||||
'video/webm;codecs=vp9': 'webm',
|
||||
'video/webm;codecs=vp8': 'webm',
|
||||
|
@ -38,22 +33,20 @@ export class CoreEmulatorCaptureHelperProvider {
|
|||
};
|
||||
|
||||
videoMimeType?: string;
|
||||
audioMimeType?: string;
|
||||
|
||||
/**
|
||||
* Capture media (image, audio, video).
|
||||
* Capture media (image, video).
|
||||
*
|
||||
* @param type Type of media: image, audio, video.
|
||||
* @param type Type of media: image, video.
|
||||
* @param options Optional options.
|
||||
* @returns Promise resolved when captured, rejected if error.
|
||||
*/
|
||||
captureMedia(type: 'image', options?: MockCameraOptions): Promise<string>;
|
||||
captureMedia(type: 'captureimage', options?: MockCaptureImageOptions): Promise<MediaFile[]>;
|
||||
captureMedia(type: 'audio', options?: MockCaptureAudioOptions): Promise<MediaFile[]>;
|
||||
captureMedia(type: 'video', options?: MockCaptureVideoOptions): Promise<MediaFile[]>;
|
||||
async captureMedia(
|
||||
type: 'image' | 'captureimage' | 'audio' | 'video',
|
||||
options?: MockCameraOptions | MockCaptureImageOptions | MockCaptureAudioOptions | MockCaptureVideoOptions,
|
||||
type: 'image' | 'captureimage' | 'video',
|
||||
options?: MockCameraOptions | MockCaptureImageOptions | MockCaptureVideoOptions,
|
||||
): Promise<MediaFile[] | string> {
|
||||
options = options || {};
|
||||
|
||||
|
@ -67,10 +60,6 @@ export class CoreEmulatorCaptureHelperProvider {
|
|||
const mimeAndExt = this.getMimeTypeAndExtension(type, options.mimetypes);
|
||||
params.mimetype = mimeAndExt.mimetype;
|
||||
params.extension = mimeAndExt.extension;
|
||||
} else if (type == 'audio') {
|
||||
const mimeAndExt = this.getMimeTypeAndExtension(type, options.mimetypes);
|
||||
params.mimetype = mimeAndExt.mimetype;
|
||||
params.extension = mimeAndExt.extension;
|
||||
} else if (type == 'image') {
|
||||
if ('sourceType' in options && options.sourceType !== undefined && options.sourceType != 1) {
|
||||
return Promise.reject('This source type is not supported in browser.');
|
||||
|
@ -121,7 +110,7 @@ export class CoreEmulatorCaptureHelperProvider {
|
|||
/**
|
||||
* Get the mimetype and extension to capture media.
|
||||
*
|
||||
* @param type Type of media: image, audio, video.
|
||||
* @param type Type of media: image, video.
|
||||
* @param mimetypes List of supported mimetypes. If undefined, all mimetypes supported.
|
||||
* @returns An object with mimetype and extension to use.
|
||||
*/
|
||||
|
@ -148,10 +137,6 @@ export class CoreEmulatorCaptureHelperProvider {
|
|||
// No mimetype found, use default extension.
|
||||
result.mimetype = this.videoMimeType;
|
||||
result.extension = this.possibleVideoMimeTypes[result.mimetype!];
|
||||
} else if (type == 'audio') {
|
||||
// No mimetype found, use default extension.
|
||||
result.mimetype = this.audioMimeType;
|
||||
result.extension = this.possibleAudioMimeTypes[result.mimetype!];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -170,20 +155,12 @@ export class CoreEmulatorCaptureHelperProvider {
|
|||
* Initialize the mimetypes to use when capturing.
|
||||
*/
|
||||
protected initMimeTypes(): void {
|
||||
// Determine video and audio mimetype to use.
|
||||
for (const mimeType in this.possibleVideoMimeTypes) {
|
||||
if (window.MediaRecorder.isTypeSupported(mimeType)) {
|
||||
this.videoMimeType = mimeType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const mimeType in this.possibleAudioMimeTypes) {
|
||||
if (window.MediaRecorder.isTypeSupported(mimeType)) {
|
||||
this.audioMimeType = mimeType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,9 +186,6 @@ export interface MockCameraOptions extends CameraOptions {
|
|||
export interface MockCaptureImageOptions extends CaptureImageOptions {
|
||||
mimetypes?: string[]; // Allowed mimetypes.
|
||||
}
|
||||
export interface MockCaptureAudioOptions extends CaptureAudioOptions {
|
||||
mimetypes?: string[]; // Allowed mimetypes.
|
||||
}
|
||||
export interface MockCaptureVideoOptions extends CaptureVideoOptions {
|
||||
mimetypes?: string[]; // Allowed mimetypes.
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
MediaCapture,
|
||||
CaptureAudioOptions,
|
||||
CaptureImageOptions,
|
||||
CaptureVideoOptions,
|
||||
MediaFile,
|
||||
|
@ -29,16 +28,6 @@ import { CoreEmulatorCaptureHelper } from './capture-helper';
|
|||
@Injectable()
|
||||
export class MediaCaptureMock extends MediaCapture {
|
||||
|
||||
/**
|
||||
* Start the audio recorder application and return information about captured audio clip files.
|
||||
*
|
||||
* @param options Options.
|
||||
* @returns Promise resolved when captured.
|
||||
*/
|
||||
captureAudio(options: CaptureAudioOptions): Promise<MediaFile[]> {
|
||||
return CoreEmulatorCaptureHelper.captureMedia('audio', options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the camera application and return information about captured image files.
|
||||
*
|
||||
|
|
|
@ -29,7 +29,6 @@ import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
|
|||
import { WebView } from '@ionic-native/ionic-webview/ngx';
|
||||
import { Keyboard } from '@ionic-native/keyboard/ngx';
|
||||
import { LocalNotifications } from '@ionic-native/local-notifications/ngx';
|
||||
import { Media } from '@ionic-native/media/ngx';
|
||||
import { MediaCapture } from '@ionic-native/media-capture/ngx';
|
||||
import { Push } from '@ionic-native/push/ngx';
|
||||
import { QRScanner } from '@ionic-native/qr-scanner/ngx';
|
||||
|
@ -54,7 +53,6 @@ export const CORE_NATIVE_SERVICES = [
|
|||
InAppBrowser,
|
||||
Keyboard,
|
||||
LocalNotifications,
|
||||
Media,
|
||||
MediaCapture,
|
||||
Push,
|
||||
QRScanner,
|
||||
|
@ -82,7 +80,6 @@ export const CORE_NATIVE_SERVICES = [
|
|||
InAppBrowser,
|
||||
Keyboard,
|
||||
LocalNotifications,
|
||||
Media,
|
||||
MediaCapture,
|
||||
Push,
|
||||
QRScanner,
|
||||
|
|
|
@ -52,7 +52,6 @@ import { InAppBrowser as InAppBrowserService } from '@ionic-native/in-app-browse
|
|||
import { WebView as WebViewService } from '@ionic-native/ionic-webview/ngx';
|
||||
import { Keyboard as KeyboardService } from '@ionic-native/keyboard/ngx';
|
||||
import { LocalNotifications as LocalNotificationsService } from '@ionic-native/local-notifications/ngx';
|
||||
import { Media as MediaService } from '@ionic-native/media/ngx';
|
||||
import { MediaCapture as MediaCaptureService } from '@ionic-native/media-capture/ngx';
|
||||
import { Push as PushService } from '@ionic-native/push/ngx';
|
||||
import { QRScanner as QRScannerService } from '@ionic-native/qr-scanner/ngx';
|
||||
|
@ -184,7 +183,6 @@ export const Geolocation = makeSingleton(GeolocationService);
|
|||
export const InAppBrowser = makeSingleton(InAppBrowserService);
|
||||
export const Keyboard = makeSingleton(KeyboardService);
|
||||
export const LocalNotifications = makeSingleton(LocalNotificationsService);
|
||||
export const Media = makeSingleton(MediaService);
|
||||
export const MediaCapture = makeSingleton(MediaCaptureService);
|
||||
export const NativeHttp = makeSingleton(HTTP);
|
||||
export const Push = makeSingleton(PushService);
|
||||
|
|
Loading…
Reference in New Issue