Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CropGridViewer.preview cutting video frames from edges vertically #182

Open
joshua750 opened this issue Mar 13, 2024 · 0 comments
Open

CropGridViewer.preview cutting video frames from edges vertically #182

joshua750 opened this issue Mar 13, 2024 · 0 comments

Comments

@joshua750
Copy link

joshua750 commented Mar 13, 2024

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 createState() => _TrimState();
}

class _TrimState extends State {
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),
        ),
      ),
    ),
  ];
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant