@@ -2,10 +2,6 @@ import 'dart:async';
2
2
import 'dart:convert' ;
3
3
import 'dart:io' ;
4
4
5
- import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart' ;
6
- import 'package:ffmpeg_kit_flutter_full_gpl/ffprobe_kit.dart' ;
7
- import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart' ;
8
- import 'package:ffmpeg_kit_flutter_full_gpl/session_state.dart' ;
9
5
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart' ;
10
6
import 'package:azlistview/azlistview.dart' ;
11
7
import 'package:collection/collection.dart' ;
@@ -169,110 +165,6 @@ class IMUtils {
169
165
return mediaInfo;
170
166
}
171
167
172
- /// Extracts a thumbnail (PNG image) from the first frame of a video file.
173
- ///
174
- /// Returns the generated thumbnail file. If extraction fails, throws an exception.
175
- static Future <File > getVideoThumbnail (File file) async {
176
- final path = file.path;
177
- final fileName = path.split ('/' ).last.split ('.' ).first; // Get base name without extension
178
- final tempDir = await createTempDir (dir: 'video' );
179
- final targetPath = '$tempDir /$fileName .png' ;
180
-
181
- // FFmpeg command to extract the first frame at 0 seconds
182
- final ffmpegCommand = '-i "$path " -ss 0 -vframes 1 -q:v 15 -y "$targetPath "' ;
183
- final session = await FFmpegKit .execute (ffmpegCommand);
184
-
185
- final state = await session.getState ();
186
- final returnCode = await session.getReturnCode ();
187
-
188
- if (state == SessionState .failed || ! ReturnCode .isSuccess (returnCode)) {
189
- Logger ().printError (info: 'Failed to generate thumbnail: $ffmpegCommand ' );
190
- throw Exception ('Thumbnail extraction failed for $path ' );
191
- }
192
-
193
- session.cancel ();
194
- final thumbnail = File (targetPath);
195
-
196
- // Verify the thumbnail file exists
197
- if (! thumbnail.existsSync ()) {
198
- Logger ().printError (info: 'Thumbnail file not found at $targetPath ' );
199
- throw Exception ('Thumbnail file was not created' );
200
- }
201
-
202
- return thumbnail;
203
- }
204
-
205
- /// compress video
206
- /// Compresses a video file and returns the resulting file.
207
- /// Aims for speed priority, moderate quality, and minimal file size while keeping resolution unchanged.
208
- static Future <File ?> compressVideoAndGetFile (File file) async {
209
- final path = file.path;
210
- final fileName = path.split ('/' ).last;
211
- final tempDir = await createTempDir (dir: 'video' );
212
- final targetPath = '$tempDir /$fileName ' ;
213
-
214
- // Get media information using FFprobe
215
- final mediaInfo = await FFprobeKit .getMediaInformation (path);
216
- final streams = mediaInfo.getMediaInformation ()? .getStreams () ?? [];
217
- final fileSize = int .tryParse (mediaInfo.getMediaInformation ()? .getSize () ?? '0' ) ?? 0 ;
218
- mediaInfo.cancel ();
219
-
220
- // Check video and audio codecs
221
- final isH264265 =
222
- streams.any ((s) => s.getCodec ()? .contains ('h264' ) == true || s.getCodec ()? .contains ('hevc' ) == true );
223
- final audioStream = streams.firstWhereOrNull ((s) => s.getType ()? .contains ('audio' ) == true );
224
- final isAAC = audioStream? .getCodec ()? .toLowerCase ().contains ('aac' ) == true ;
225
-
226
- // If target file exists and is H.264, return it directly (assuming no further processing needed)
227
- if (File (targetPath).existsSync () && isH264265) {
228
- return File (targetPath);
229
- }
230
-
231
- // Default FFmpeg command for compression
232
- String ffmpegCommand = '-i "$path " -preset fast -crf 28 -threads 4 -c:a aac -b:a 96k -c:v libx264 -y "$targetPath "' ;
233
-
234
- if (isH264265) {
235
- if (isAAC) {
236
- const sizeThreshold = 50 * 1024 * 1024 ; // 50MB threshold
237
- if (fileSize > sizeThreshold) {
238
- // Compress video if size exceeds threshold
239
- final compressed = await VideoCompress .compressVideo (
240
- path,
241
- quality: VideoQuality .Res1280x720Quality , // Moderate quality for smaller file size
242
- );
243
- Logger .print ('Compressed video size: ${compressed ?.file ?.lengthSync ()} bytes' );
244
-
245
- return compressed? .file ?? file; // Return compressed file or original on failure
246
- } else {
247
- // Copy file directly if below threshold
248
- file.copySync (targetPath);
249
-
250
- return File (targetPath);
251
- }
252
- } else {
253
- // Only convert audio to AAC, copy video stream for speed
254
- ffmpegCommand = '-i "$path " -c:v copy -c:a aac -b:a 96k -threads 4 -y "$targetPath "' ;
255
- }
256
- }
257
- // Execute FFmpeg command
258
- Logger .print ('executing FFmpeg command: $ffmpegCommand ' );
259
- final session = await FFmpegKit .execute (ffmpegCommand);
260
- final state = await session.getState ();
261
- final returnCode = await session.getReturnCode ();
262
-
263
- if (state == SessionState .failed || ! ReturnCode .isSuccess (returnCode)) {
264
- // Log error and fallback to copying original file
265
- Logger ().printError (info: "FFmpeg failed: $ffmpegCommand " );
266
- file.copySync (targetPath);
267
-
268
- return File (targetPath);
269
- }
270
-
271
- session.cancel ();
272
-
273
- return File (targetPath);
274
- }
275
-
276
168
static Future <File ?> compressImageAndGetFile (File file, {int quality = 80 }) async {
277
169
var path = file.path;
278
170
var name = path.substring (path.lastIndexOf ("/" ) + 1 ).toLowerCase ();
0 commit comments