video_editor icon indicating copy to clipboard operation
video_editor copied to clipboard

CropGridViewer.preview cutting video frames from edges vertically

Open joshua750 opened this issue 11 months ago • 0 comments

CropGridViewer.preview cutting video frames from edges vertically but if i use CropGridViewer.edit it working fine please.

here is my code

import 'dart:io';

import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart'; import 'package:ffmpeg_kit_flutter_full_gpl/log.dart'; import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart'; import 'package:ffmpeg_kit_flutter_full_gpl/statistics.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:intl/intl.dart'; import 'package:media_scanner/media_scanner.dart'; import 'package:path_provider/path_provider.dart'; import 'package:video_editor/video_editor.dart'; import 'package:video_player/video_player.dart'; import 'package:videocutter/Tab/TabBarViewWidget.dart'; import 'package:videocutter/Trim/video_trim.dart';

class Trim extends StatefulWidget { const Trim({Key? key, required this.file}) : super(key: key);

final File file;

@override State<Trim> createState() => _TrimState(); }

class _TrimState extends State<Trim> { late final VideoEditorController _controller; late VideoPlayerController _videoPlayerController;

int duration = 0;

String formatter(Duration duration) => [ duration.inMinutes.remainder(60).toString().padLeft(2, '0'), duration.inSeconds.remainder(60).toString().padLeft(2, '0') ].join(":");

final double height = 60; ValueNotifier progressNotifier = ValueNotifier(0);

@override void initState() { super.initState(); videoPlayerController = VideoPlayerController.file(widget.file) ..initialize().then(() { setState(() { duration = _videoPlayerController.value.duration.inSeconds; }); });

_controller = VideoEditorController.file(
  File(widget.file.path),
  minDuration: const Duration(seconds: 10),
  maxDuration: Duration(
      seconds: 60), // Default value, can be updated later
);

_controller.initialize(aspectRatio: 16 / 9).then((_) {
  setState(() {
    // Update maxDuration after _controller has been initialized
    _controller.maxDuration =
        Duration(seconds: duration > 0 ? duration : 60);
  });
}).catchError((error) {
  // handle minimum duration bigger than video duration error
  Navigator.pop(context);
});

}

@override void dispose() { _controller.dispose(); _videoPlayerController.dispose(); super.dispose(); }

/// Basic export video function /// Basic export video function

bool istrim = true; int progress = 0; String? fileName;

Future trimVideos() async {

try {

  Directory mainFolder = Directory('${(await getApplicationDocumentsDirectory()).path}/trim/cut/');
  final String outputPath= '${mainFolder.path}/$fileName.mp4';

  //final String outputPath = '/storage/emulated/0/Download/trim/cut/$fileName.mp4';
  final String inputPath = widget.file.path;

  final Duration start = _controller.startTrim;
  final Duration end = _controller.endTrim;

  final command =
      '-ss ${formatter(start)} -t ${formatter(end - start)} -noaccurate_seek -i "$inputPath" -codec copy -avoid_negative_ts 1 "$outputPath"';

  int totalVideoDuration = (end - start).inMilliseconds;
  int totalProgress = 0;

  // Execute FFmpeg command
  await FFmpegKit.executeAsync(command, (session) async {
    final returnCode = await session.getReturnCode();

    print('The command is executed: $command');

    if (ReturnCode.isSuccess(returnCode)) {
       print('working fine $returnCode');
       progressNotifier.value = 100;
       Navigator.pushReplacement(
         context,
         MaterialPageRoute(builder: (context) => TabBarViewWidget(initialIndex: 0, showAppBar: true)),
       );
    }

  }, (Log log) {
    // Log callback
  }, (Statistics statistics) {
    if (statistics == null) {
      return;
    }
    if (statistics.getTime() > 0) {
      // Calculate the percentage of completion
      totalProgress = (statistics.getTime() * 100) ~/ totalVideoDuration;
      progress = totalProgress;
      print('Progress: $progress%');

      progressNotifier.value=progress;


    }

  });
  // MediaScanner.loadMedia(path: outputPath);
  print('Execution completed successfully');
  // Navigator.push(
  //   context,
  //   MaterialPageRoute(
  //     builder: (context) => TabBarViewWidget(initialIndex: 1),
  //   ),
  // );




} catch (e) {
  print('Error trimming video: $e');
}

}

Future _showFileNameDialog(BuildContext context) async { TextEditingController fileNameController = TextEditingController();

return showDialog<String>(
  context: context,
  builder: (BuildContext context) {
    return AlertDialog(
      title: Text('Enter  Name'),
      content: TextField(
        controller: fileNameController,
        decoration: InputDecoration(hintText: '${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}'),
      ),
      actions: <Widget>[
        TextButton(
          onPressed: () async {
            Navigator.pop(context, fileNameController.text.trim());
            if(fileNameController.text==null||fileNameController.text.isEmpty){
              fileName= DateFormat('yyyyMMdd_HHmmss').format(DateTime.now());
              print('The file name is $fileName');
            }else{
              fileName=fileNameController.text.trim();
              print('The file name is $fileName');
            }
          trimVideos();
          },
          child: Text('OK'),
        ),
        TextButton(
          onPressed: () {
            Navigator.pop(context, null); // Cancel
          },
          child: Text('Cancel'),
        ),
      ],
    );
  },
);

}

@override
Widget build(BuildContext context) {
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);
  return Scaffold(
    appBar: AppBar(
      actions: [
        Padding(
          padding: EdgeInsets.only(right: 30),

        )
      ],
      centerTitle: true,
      title: Text('Trim video'),
    ),
    body: FutureBuilder(
      future: _controller.initialize(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return _buildTrimWidget();
        } else {
          return Center(child:
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              CircularProgressIndicator(),

            ],

          ));
        }
      },
    ),
  );
}


Widget _buildTrimWidget() {
  if (_controller.initialized) {
    return Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Expanded(
        child: Stack(
          alignment: Alignment.center,
          children: [
            Container(
              color: Colors.black,
              width: MediaQuery.of(context).size.width,
              height: MediaQuery.of(context).size.height, // Adjust height as desired
              child: CropGridViewer.edit(
                controller: _controller,

              ),
            ),
            AnimatedBuilder(
              animation: _controller.video,
              builder: (_, __) =>
                  AnimatedOpacity(
                    opacity: _controller.isPlaying ? 0 : 1,
                    duration: kThemeAnimationDuration,
                    child: GestureDetector(
                      onTap: _controller.video.play,
                      child: Container(
                        width: 40,
                        height: 40,
                        decoration: const BoxDecoration(
                          color: Colors.white,
                          shape: BoxShape.circle,
                        ),
                        child: const Icon(
                          Icons.play_arrow,
                          color: Colors.black,
                        ),
                      ),
                    ),
                  ),
            ),
          ],
        ),
      ),
      ElevatedButton(
          onPressed: (){
            _showFileNameDialog(context);
          },
          child: Text('Trim Now')
      ),
      ValueListenableBuilder<int>(
          valueListenable: progressNotifier,
          builder: (context, value, child) {
            return value < 100
                ? Column(
              children: [
                Text('Progress: $value%'),
                LinearProgressIndicator(
                  value: value / 100,
                  minHeight: 10,
                  backgroundColor: Colors.grey,
                  valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
                ),
              ],
            )
                : Container(); // Empty container when progress is finished
          }),

      ..._trimSlider(),
    ],
  );
  } else {
    return const Center(child: CircularProgressIndicator());
  }
}

List<Widget> _trimSlider() {
  return [
    AnimatedBuilder(
      animation: Listenable.merge([
        _controller,
        _controller.video,
      ]),
      builder: (_, __) {
        final int duration = _controller.videoDuration.inSeconds;
        final double pos = _controller.trimPosition * duration;

        return Padding(
          padding: EdgeInsets.symmetric(horizontal: height / 4),
          child: Row(
            children: [
              Text('${formatter(Duration(seconds: pos.toInt()))}'),
              const Expanded(child: SizedBox()),
              AnimatedOpacity(
                opacity: _controller.isTrimming ? 1 : 0,
                duration: kThemeAnimationDuration,
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(formatter(_controller.startTrim)),
                    const SizedBox(width: 10),
                    Text(formatter(_controller.endTrim)),
                  ],
                ),
              ),
            ],
          ),
        );
      },
    ),
    Container(
      width: MediaQuery
          .of(context)
          .size
          .width,
      margin: EdgeInsets.symmetric(vertical: height / 4),
      child: TrimSlider(
        controller: _controller,
        height: height,
        horizontalMargin: height / 4,
        child: TrimTimeline(
          controller: _controller,
          padding: const EdgeInsets.only(top: 10),
        ),
      ),
    ),
  ];
}

}

joshua750 avatar Mar 13 '24 08:03 joshua750