capacitor-native-video-compressor 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -6
- package/android/src/main/java/com/atomic/videocompressor/NativeVideoCompressorPlugin.java +66 -67
- package/android/src/main/java/com/atomic/videocompressor/VideoCompressionService.java +8 -9
- package/dist/docs.json +11 -1
- package/dist/esm/definitions.d.ts +5 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +7 -0
- package/dist/esm/web.js +160 -8
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +160 -8
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +160 -10
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeVideoCompressorPlugin/NativeVideoCompressorPlugin.swift +188 -73
- package/package.json +5 -1
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,OAAO,wBAAyB,SAAQ,SAAS;IACrD,KAAK,CAAC,aAAa,CAAC,OAAwB;QAC1C,OAAO,CAAC,IAAI,CAAC,iEAAiE,EAAE,OAAO,CAAC,CAAC;QAEzF,sEAAsE;QACtE,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC;QAEpD,oFAAoF;QACpF,WAAW;QACX,oBAAoB;QACpB,iCAAiC;QACjC,KAAK;IACP,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { NativeVideoCompressorPlugin, CompressOptions, CompressResult } from './definitions';\n\nexport class NativeVideoCompressorWeb extends WebPlugin implements NativeVideoCompressorPlugin {\n async compressVideo(options: CompressOptions): Promise<CompressResult> {\n console.warn('Tính năng nén video bằng GPU không hỗ trợ trên trình duyệt Web.', options);\n\n // Báo lỗi cho chuẩn Capacitor báo rằng nền tảng này không được hỗ trợ\n throw this.unimplemented('Not implemented on web.');\n\n // (Hoặc nếu bạn không muốn văng lỗi trên web, bạn có thể trả về luôn đường dẫn gốc)\n // return {\n // success: false,\n // destPath: options.sourcePath\n // };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { fetchFile, toBlobURL } from '@ffmpeg/util';\n\nimport type { NativeVideoCompressorPlugin, CompressOptions, CompressResult } from './definitions';\n\nexport class NativeVideoCompressorWeb extends WebPlugin implements NativeVideoCompressorPlugin {\n private ffmpeg: FFmpeg | null = null;\n private isLoaded = false;\n\n private async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg) return;\n\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n\n try {\n this.ffmpeg = new FFmpeg();\n\n // Listen to FFmpeg progress and map it to capacitor plugin events\n this.ffmpeg.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n\n this.ffmpeg.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n\n // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n\n console.log('Starting to load FFmpeg core from:', baseURL);\n await this.ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n\n this.isLoaded = true;\n console.log('FFmpeg core loaded successfully');\n } catch (e: any) {\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n }\n\n async initialize(): Promise<{ success: boolean; message?: string }> {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n } catch (e: any) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\n }\n\n async compressVideo(options: CompressOptions): Promise<CompressResult> {\n try {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData: Uint8Array;\n try {\n inputData = await fetchFile(sourcePath);\n } catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\n try {\n await this.ffmpeg.writeFile(inputFileName, inputData);\n } catch (err) {\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n\n // Construct FFmpeg command array\n const args: string[] = ['-i', inputFileName];\n\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n\n // Output\n args.push(outputFileName);\n\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await this.ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\n } catch (err) {\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n\n console.log('[FFmpeg] Reading output file...');\n let data: any;\n try {\n data = await this.ffmpeg.readFile(outputFileName);\n } catch (err) {\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n\n // Free memory\n await this.ffmpeg.deleteFile(inputFileName);\n await this.ffmpeg.deleteFile(outputFileName);\n\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data as any], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n } catch (error: any) {\n console.error('[FFmpeg] Compression process failed:', error);\n throw error;\n }\n }\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -1,21 +1,173 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@capacitor/core');
|
|
4
|
+
var ffmpeg = require('@ffmpeg/ffmpeg');
|
|
5
|
+
var util = require('@ffmpeg/util');
|
|
4
6
|
|
|
5
7
|
const NativeVideoCompressor = core.registerPlugin('NativeVideoCompressor', {
|
|
6
8
|
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.NativeVideoCompressorWeb()),
|
|
7
9
|
});
|
|
8
10
|
|
|
9
11
|
class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.ffmpeg = null;
|
|
15
|
+
this.isLoaded = false;
|
|
16
|
+
}
|
|
17
|
+
async loadFFmpeg() {
|
|
18
|
+
if (this.isLoaded && this.ffmpeg)
|
|
19
|
+
return;
|
|
20
|
+
this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });
|
|
21
|
+
try {
|
|
22
|
+
this.ffmpeg = new ffmpeg.FFmpeg();
|
|
23
|
+
// Listen to FFmpeg progress and map it to capacitor plugin events
|
|
24
|
+
this.ffmpeg.on('progress', ({ progress }) => {
|
|
25
|
+
// progress is a fraction between 0 and 1
|
|
26
|
+
this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });
|
|
27
|
+
});
|
|
28
|
+
this.ffmpeg.on('log', ({ message }) => {
|
|
29
|
+
console.log('[FFmpeg Log]', message);
|
|
30
|
+
});
|
|
31
|
+
// Use standard single-threaded version for best compatibility across browsers
|
|
32
|
+
// without requiring complex SharedArrayBuffer headers configuration
|
|
33
|
+
const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';
|
|
34
|
+
console.log('Starting to load FFmpeg core from:', baseURL);
|
|
35
|
+
await this.ffmpeg.load({
|
|
36
|
+
coreURL: await util.toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
|
|
37
|
+
wasmURL: await util.toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
|
|
38
|
+
});
|
|
39
|
+
this.isLoaded = true;
|
|
40
|
+
console.log('FFmpeg core loaded successfully');
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error('Failed to load FFmpeg:', e);
|
|
44
|
+
throw e;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async initialize() {
|
|
48
|
+
try {
|
|
49
|
+
if (this.isLoaded) {
|
|
50
|
+
return { success: true, message: 'Already initialized' };
|
|
51
|
+
}
|
|
52
|
+
await this.loadFFmpeg();
|
|
53
|
+
return { success: true, message: 'Initialization successful' };
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
return { success: false, message: e.message || 'Initialization failed' };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
10
59
|
async compressVideo(options) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
60
|
+
try {
|
|
61
|
+
if (!this.isLoaded) {
|
|
62
|
+
await this.loadFFmpeg();
|
|
63
|
+
}
|
|
64
|
+
if (!this.ffmpeg) {
|
|
65
|
+
throw new Error('FFmpeg failed to load');
|
|
66
|
+
}
|
|
67
|
+
this.notifyListeners('onProgress', { status: 'started', percent: 0 });
|
|
68
|
+
const { sourcePath, quality = 'MEDIUM' } = options;
|
|
69
|
+
const inputFileName = 'input.mp4';
|
|
70
|
+
const outputFileName = 'output.mp4';
|
|
71
|
+
// Map quality to CRF, Scale, and Audio Bitrate
|
|
72
|
+
let crf = '28';
|
|
73
|
+
let scale = ''; // default keep resolution
|
|
74
|
+
let audioBitrate = '128k';
|
|
75
|
+
switch (quality) {
|
|
76
|
+
case 'VERY_HIGH': // 1080p
|
|
77
|
+
crf = '23';
|
|
78
|
+
scale = '-vf scale=-2:1080';
|
|
79
|
+
audioBitrate = '128k';
|
|
80
|
+
break;
|
|
81
|
+
case 'HIGH': // 720p
|
|
82
|
+
crf = '28';
|
|
83
|
+
scale = '-vf scale=-2:720';
|
|
84
|
+
audioBitrate = '128k';
|
|
85
|
+
break;
|
|
86
|
+
case 'MEDIUM': // 540p
|
|
87
|
+
crf = '30';
|
|
88
|
+
scale = '-vf scale=-2:540';
|
|
89
|
+
audioBitrate = '96k';
|
|
90
|
+
break;
|
|
91
|
+
case 'LOW': // 480p
|
|
92
|
+
crf = '32';
|
|
93
|
+
scale = '-vf scale=-2:480';
|
|
94
|
+
audioBitrate = '96k';
|
|
95
|
+
break;
|
|
96
|
+
case '360P': // 360p
|
|
97
|
+
crf = '28';
|
|
98
|
+
scale = '-vf scale=-2:360';
|
|
99
|
+
audioBitrate = '64k';
|
|
100
|
+
break;
|
|
101
|
+
case 'VERY_LOW': // 240p
|
|
102
|
+
crf = '35';
|
|
103
|
+
scale = '-vf scale=-2:240';
|
|
104
|
+
audioBitrate = '48k';
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
console.log('[FFmpeg] Fetching source file...', sourcePath);
|
|
108
|
+
let inputData;
|
|
109
|
+
try {
|
|
110
|
+
inputData = await util.fetchFile(sourcePath);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
console.error('[FFmpeg] Error fetching source file:', err);
|
|
114
|
+
throw new Error('Could not fetch source video file');
|
|
115
|
+
}
|
|
116
|
+
console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');
|
|
117
|
+
try {
|
|
118
|
+
await this.ffmpeg.writeFile(inputFileName, inputData);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error('[FFmpeg] Error writing to FS:', err);
|
|
122
|
+
throw new Error('Could not write to FFmpeg FS');
|
|
123
|
+
}
|
|
124
|
+
// Construct FFmpeg command array
|
|
125
|
+
const args = ['-i', inputFileName];
|
|
126
|
+
// Video codec and compression parameters (superfast provides much better size than ultrafast)
|
|
127
|
+
args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);
|
|
128
|
+
// Resize if needed (split by space)
|
|
129
|
+
if (scale) {
|
|
130
|
+
args.push(...scale.split(' '));
|
|
131
|
+
}
|
|
132
|
+
// Audio re-encoding to save space
|
|
133
|
+
args.push('-c:a', 'aac', '-b:a', audioBitrate);
|
|
134
|
+
// Output
|
|
135
|
+
args.push(outputFileName);
|
|
136
|
+
console.log('[FFmpeg] Executing command:', args.join(' '));
|
|
137
|
+
try {
|
|
138
|
+
const retCode = await this.ffmpeg.exec(args);
|
|
139
|
+
console.log('[FFmpeg] Exec returned code:', retCode);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
console.error('[FFmpeg] Error executing command:', err);
|
|
143
|
+
throw new Error('FFmpeg execution failed');
|
|
144
|
+
}
|
|
145
|
+
console.log('[FFmpeg] Reading output file...');
|
|
146
|
+
let data;
|
|
147
|
+
try {
|
|
148
|
+
data = await this.ffmpeg.readFile(outputFileName);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.error('[FFmpeg] Error reading output file:', err);
|
|
152
|
+
throw new Error('Could not read FFmpeg output');
|
|
153
|
+
}
|
|
154
|
+
// Free memory
|
|
155
|
+
await this.ffmpeg.deleteFile(inputFileName);
|
|
156
|
+
await this.ffmpeg.deleteFile(outputFileName);
|
|
157
|
+
console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');
|
|
158
|
+
// Create a blob URL for the resulting file
|
|
159
|
+
const blob = new Blob([data], { type: 'video/mp4' });
|
|
160
|
+
const destPath = URL.createObjectURL(blob);
|
|
161
|
+
console.log('[FFmpeg] Compression complete, returning path:', destPath);
|
|
162
|
+
return {
|
|
163
|
+
success: true,
|
|
164
|
+
destPath: destPath,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.error('[FFmpeg] Compression process failed:', error);
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
19
171
|
}
|
|
20
172
|
}
|
|
21
173
|
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst NativeVideoCompressor = registerPlugin('NativeVideoCompressor', {\n web: () => import('./web').then((m) => new m.NativeVideoCompressorWeb()),\n});\nexport * from './definitions';\nexport { NativeVideoCompressor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class NativeVideoCompressorWeb extends WebPlugin {\n async compressVideo(options) {\n console.warn('Tính năng nén video bằng GPU không hỗ trợ trên trình duyệt Web.', options);\n // Báo lỗi cho chuẩn Capacitor báo rằng nền tảng này không được hỗ trợ\n throw this.unimplemented('Not implemented on web.');\n // (Hoặc nếu bạn không muốn văng lỗi trên web, bạn có thể trả về luôn đường dẫn gốc)\n // return {\n // success: false,\n // destPath: options.sourcePath\n // };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,qBAAqB,GAAGA,mBAAc,CAAC,uBAAuB,EAAE;AACtE,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,wBAAwB,EAAE,CAAC;AAC5E,CAAC;;ACFM,MAAM,wBAAwB,SAASC,cAAS,CAAC;AACxD,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;AACjC,QAAQ,OAAO,CAAC,IAAI,CAAC,iEAAiE,EAAE,OAAO,CAAC;AAChG;AACA,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;AAC3D;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst NativeVideoCompressor = registerPlugin('NativeVideoCompressor', {\n web: () => import('./web').then((m) => new m.NativeVideoCompressorWeb()),\n});\nexport * from './definitions';\nexport { NativeVideoCompressor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { fetchFile, toBlobURL } from '@ffmpeg/util';\nexport class NativeVideoCompressorWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.ffmpeg = null;\n this.isLoaded = false;\n }\n async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg)\n return;\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n try {\n this.ffmpeg = new FFmpeg();\n // Listen to FFmpeg progress and map it to capacitor plugin events\n this.ffmpeg.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n this.ffmpeg.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n console.log('Starting to load FFmpeg core from:', baseURL);\n await this.ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n this.isLoaded = true;\n console.log('FFmpeg core loaded successfully');\n }\n catch (e) {\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n }\n async initialize() {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n }\n catch (e) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\n }\n async compressVideo(options) {\n try {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData;\n try {\n inputData = await fetchFile(sourcePath);\n }\n catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\n try {\n await this.ffmpeg.writeFile(inputFileName, inputData);\n }\n catch (err) {\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n // Construct FFmpeg command array\n const args = ['-i', inputFileName];\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n // Output\n args.push(outputFileName);\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await this.ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\n }\n catch (err) {\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n console.log('[FFmpeg] Reading output file...');\n let data;\n try {\n data = await this.ffmpeg.readFile(outputFileName);\n }\n catch (err) {\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n // Free memory\n await this.ffmpeg.deleteFile(inputFileName);\n await this.ffmpeg.deleteFile(outputFileName);\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n }\n catch (error) {\n console.error('[FFmpeg] Compression process failed:', error);\n throw error;\n }\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","FFmpeg","toBlobURL","fetchFile"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var capacitorNativeVideoCompressor = (function (exports, core) {
|
|
1
|
+
var capacitorNativeVideoCompressor = (function (exports, core, ffmpeg, util) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const NativeVideoCompressor = core.registerPlugin('NativeVideoCompressor', {
|
|
@@ -6,15 +6,165 @@ var capacitorNativeVideoCompressor = (function (exports, core) {
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
9
|
+
constructor() {
|
|
10
|
+
super(...arguments);
|
|
11
|
+
this.ffmpeg = null;
|
|
12
|
+
this.isLoaded = false;
|
|
13
|
+
}
|
|
14
|
+
async loadFFmpeg() {
|
|
15
|
+
if (this.isLoaded && this.ffmpeg)
|
|
16
|
+
return;
|
|
17
|
+
this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });
|
|
18
|
+
try {
|
|
19
|
+
this.ffmpeg = new ffmpeg.FFmpeg();
|
|
20
|
+
// Listen to FFmpeg progress and map it to capacitor plugin events
|
|
21
|
+
this.ffmpeg.on('progress', ({ progress }) => {
|
|
22
|
+
// progress is a fraction between 0 and 1
|
|
23
|
+
this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });
|
|
24
|
+
});
|
|
25
|
+
this.ffmpeg.on('log', ({ message }) => {
|
|
26
|
+
console.log('[FFmpeg Log]', message);
|
|
27
|
+
});
|
|
28
|
+
// Use standard single-threaded version for best compatibility across browsers
|
|
29
|
+
// without requiring complex SharedArrayBuffer headers configuration
|
|
30
|
+
const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';
|
|
31
|
+
console.log('Starting to load FFmpeg core from:', baseURL);
|
|
32
|
+
await this.ffmpeg.load({
|
|
33
|
+
coreURL: await util.toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
|
|
34
|
+
wasmURL: await util.toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
|
|
35
|
+
});
|
|
36
|
+
this.isLoaded = true;
|
|
37
|
+
console.log('FFmpeg core loaded successfully');
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
console.error('Failed to load FFmpeg:', e);
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async initialize() {
|
|
45
|
+
try {
|
|
46
|
+
if (this.isLoaded) {
|
|
47
|
+
return { success: true, message: 'Already initialized' };
|
|
48
|
+
}
|
|
49
|
+
await this.loadFFmpeg();
|
|
50
|
+
return { success: true, message: 'Initialization successful' };
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return { success: false, message: e.message || 'Initialization failed' };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
9
56
|
async compressVideo(options) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
try {
|
|
58
|
+
if (!this.isLoaded) {
|
|
59
|
+
await this.loadFFmpeg();
|
|
60
|
+
}
|
|
61
|
+
if (!this.ffmpeg) {
|
|
62
|
+
throw new Error('FFmpeg failed to load');
|
|
63
|
+
}
|
|
64
|
+
this.notifyListeners('onProgress', { status: 'started', percent: 0 });
|
|
65
|
+
const { sourcePath, quality = 'MEDIUM' } = options;
|
|
66
|
+
const inputFileName = 'input.mp4';
|
|
67
|
+
const outputFileName = 'output.mp4';
|
|
68
|
+
// Map quality to CRF, Scale, and Audio Bitrate
|
|
69
|
+
let crf = '28';
|
|
70
|
+
let scale = ''; // default keep resolution
|
|
71
|
+
let audioBitrate = '128k';
|
|
72
|
+
switch (quality) {
|
|
73
|
+
case 'VERY_HIGH': // 1080p
|
|
74
|
+
crf = '23';
|
|
75
|
+
scale = '-vf scale=-2:1080';
|
|
76
|
+
audioBitrate = '128k';
|
|
77
|
+
break;
|
|
78
|
+
case 'HIGH': // 720p
|
|
79
|
+
crf = '28';
|
|
80
|
+
scale = '-vf scale=-2:720';
|
|
81
|
+
audioBitrate = '128k';
|
|
82
|
+
break;
|
|
83
|
+
case 'MEDIUM': // 540p
|
|
84
|
+
crf = '30';
|
|
85
|
+
scale = '-vf scale=-2:540';
|
|
86
|
+
audioBitrate = '96k';
|
|
87
|
+
break;
|
|
88
|
+
case 'LOW': // 480p
|
|
89
|
+
crf = '32';
|
|
90
|
+
scale = '-vf scale=-2:480';
|
|
91
|
+
audioBitrate = '96k';
|
|
92
|
+
break;
|
|
93
|
+
case '360P': // 360p
|
|
94
|
+
crf = '28';
|
|
95
|
+
scale = '-vf scale=-2:360';
|
|
96
|
+
audioBitrate = '64k';
|
|
97
|
+
break;
|
|
98
|
+
case 'VERY_LOW': // 240p
|
|
99
|
+
crf = '35';
|
|
100
|
+
scale = '-vf scale=-2:240';
|
|
101
|
+
audioBitrate = '48k';
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
console.log('[FFmpeg] Fetching source file...', sourcePath);
|
|
105
|
+
let inputData;
|
|
106
|
+
try {
|
|
107
|
+
inputData = await util.fetchFile(sourcePath);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.error('[FFmpeg] Error fetching source file:', err);
|
|
111
|
+
throw new Error('Could not fetch source video file');
|
|
112
|
+
}
|
|
113
|
+
console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');
|
|
114
|
+
try {
|
|
115
|
+
await this.ffmpeg.writeFile(inputFileName, inputData);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
console.error('[FFmpeg] Error writing to FS:', err);
|
|
119
|
+
throw new Error('Could not write to FFmpeg FS');
|
|
120
|
+
}
|
|
121
|
+
// Construct FFmpeg command array
|
|
122
|
+
const args = ['-i', inputFileName];
|
|
123
|
+
// Video codec and compression parameters (superfast provides much better size than ultrafast)
|
|
124
|
+
args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);
|
|
125
|
+
// Resize if needed (split by space)
|
|
126
|
+
if (scale) {
|
|
127
|
+
args.push(...scale.split(' '));
|
|
128
|
+
}
|
|
129
|
+
// Audio re-encoding to save space
|
|
130
|
+
args.push('-c:a', 'aac', '-b:a', audioBitrate);
|
|
131
|
+
// Output
|
|
132
|
+
args.push(outputFileName);
|
|
133
|
+
console.log('[FFmpeg] Executing command:', args.join(' '));
|
|
134
|
+
try {
|
|
135
|
+
const retCode = await this.ffmpeg.exec(args);
|
|
136
|
+
console.log('[FFmpeg] Exec returned code:', retCode);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
console.error('[FFmpeg] Error executing command:', err);
|
|
140
|
+
throw new Error('FFmpeg execution failed');
|
|
141
|
+
}
|
|
142
|
+
console.log('[FFmpeg] Reading output file...');
|
|
143
|
+
let data;
|
|
144
|
+
try {
|
|
145
|
+
data = await this.ffmpeg.readFile(outputFileName);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
console.error('[FFmpeg] Error reading output file:', err);
|
|
149
|
+
throw new Error('Could not read FFmpeg output');
|
|
150
|
+
}
|
|
151
|
+
// Free memory
|
|
152
|
+
await this.ffmpeg.deleteFile(inputFileName);
|
|
153
|
+
await this.ffmpeg.deleteFile(outputFileName);
|
|
154
|
+
console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');
|
|
155
|
+
// Create a blob URL for the resulting file
|
|
156
|
+
const blob = new Blob([data], { type: 'video/mp4' });
|
|
157
|
+
const destPath = URL.createObjectURL(blob);
|
|
158
|
+
console.log('[FFmpeg] Compression complete, returning path:', destPath);
|
|
159
|
+
return {
|
|
160
|
+
success: true,
|
|
161
|
+
destPath: destPath,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error('[FFmpeg] Compression process failed:', error);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
18
168
|
}
|
|
19
169
|
}
|
|
20
170
|
|
|
@@ -27,5 +177,5 @@ var capacitorNativeVideoCompressor = (function (exports, core) {
|
|
|
27
177
|
|
|
28
178
|
return exports;
|
|
29
179
|
|
|
30
|
-
})({}, capacitorExports);
|
|
180
|
+
})({}, capacitorExports, ffmpeg, util);
|
|
31
181
|
//# sourceMappingURL=plugin.js.map
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst NativeVideoCompressor = registerPlugin('NativeVideoCompressor', {\n web: () => import('./web').then((m) => new m.NativeVideoCompressorWeb()),\n});\nexport * from './definitions';\nexport { NativeVideoCompressor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class NativeVideoCompressorWeb extends WebPlugin {\n async compressVideo(options) {\n console.warn('Tính năng nén video bằng GPU không hỗ trợ trên trình duyệt Web.', options);\n // Báo lỗi cho chuẩn Capacitor báo rằng nền tảng này không được hỗ trợ\n throw this.unimplemented('Not implemented on web.');\n // (Hoặc nếu bạn không muốn văng lỗi trên web, bạn có thể trả về luôn đường dẫn gốc)\n // return {\n // success: false,\n // destPath: options.sourcePath\n // };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,qBAAqB,GAAGA,mBAAc,CAAC,uBAAuB,EAAE;IACtE,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,wBAAwB,EAAE,CAAC;IAC5E,CAAC;;ICFM,MAAM,wBAAwB,SAASC,cAAS,CAAC;IACxD,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;IACjC,QAAQ,OAAO,CAAC,IAAI,CAAC,iEAAiE,EAAE,OAAO,CAAC;IAChG;IACA,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D;IACA;IACA;IACA;IACA;IACA,IAAI;IACJ;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst NativeVideoCompressor = registerPlugin('NativeVideoCompressor', {\n web: () => import('./web').then((m) => new m.NativeVideoCompressorWeb()),\n});\nexport * from './definitions';\nexport { NativeVideoCompressor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { fetchFile, toBlobURL } from '@ffmpeg/util';\nexport class NativeVideoCompressorWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.ffmpeg = null;\n this.isLoaded = false;\n }\n async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg)\n return;\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n try {\n this.ffmpeg = new FFmpeg();\n // Listen to FFmpeg progress and map it to capacitor plugin events\n this.ffmpeg.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n this.ffmpeg.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n console.log('Starting to load FFmpeg core from:', baseURL);\n await this.ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n this.isLoaded = true;\n console.log('FFmpeg core loaded successfully');\n }\n catch (e) {\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n }\n async initialize() {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n }\n catch (e) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\n }\n async compressVideo(options) {\n try {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData;\n try {\n inputData = await fetchFile(sourcePath);\n }\n catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\n try {\n await this.ffmpeg.writeFile(inputFileName, inputData);\n }\n catch (err) {\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n // Construct FFmpeg command array\n const args = ['-i', inputFileName];\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n // Output\n args.push(outputFileName);\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await this.ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\n }\n catch (err) {\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n console.log('[FFmpeg] Reading output file...');\n let data;\n try {\n data = await this.ffmpeg.readFile(outputFileName);\n }\n catch (err) {\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n // Free memory\n await this.ffmpeg.deleteFile(inputFileName);\n await this.ffmpeg.deleteFile(outputFileName);\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n }\n catch (error) {\n console.error('[FFmpeg] Compression process failed:', error);\n throw error;\n }\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","FFmpeg","toBlobURL","fetchFile"],"mappings":"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"}
|