new recording idea

This commit is contained in:
Naser Elziadna
2022-02-10 12:01:34 +02:00
parent 12279ba55d
commit 9af09cdf45
7 changed files with 156 additions and 54 deletions

View File

@@ -80,12 +80,7 @@ class _HomePageState extends State<HomePage> {
@override
void initState() {
drawController = context.read<DoddlerBloc>().drawController;
timer = Timer.periodic(Duration(milliseconds: 34), (Timer t) {
context
.read<RecorderBloc>()
.add(TakeSnapshotEvent(globalKey: Doddler.globalKey));
print("TakeSnapshotEvent");
});
context.read<RecorderBloc>().add(StartRecordingEvent());
super.initState();
}

View File

@@ -1,7 +1,13 @@
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'frame.dart';
import 'package:image/image.dart' as image;
class RecorderController {
final GlobalKey? globalKey;
@@ -11,6 +17,44 @@ class RecorderController {
this.frames,
});
Future<Uint8List> convertImageToUint8List(ui.Image image) async {
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
return pngBytes;
}
Future<List<int>?> export() async {
List<RawFrame> bytes = [];
for (final frame in frames!) {
final i = await frame.frame!.toByteData(format: ui.ImageByteFormat.png);
if (i != null) {
bytes.add(RawFrame(32, i));
} else {
print('Skipped frame while enconding');
}
}
final result = compute(_export, bytes);
frames!.clear();
return result;
}
static Future<List<int>?> _export(List<RawFrame> frames) async {
final animation = image.Animation();
animation.backgroundColor = Colors.transparent.value;
for (final frame in frames) {
final iAsBytes = frame.image.buffer.asUint8List();
final decodedImage = image.decodePng(iAsBytes);
if (decodedImage == null) {
print('Skipped frame while enconding');
continue;
}
decodedImage.duration = frame.durationInMillis;
animation.addFrame(decodedImage);
}
return image.encodeGifAnimation(animation);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
@@ -37,3 +81,10 @@ class RecorderController {
);
}
}
class RawFrame {
RawFrame(this.durationInMillis, this.image);
final int durationInMillis;
final ByteData image;
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
@@ -22,53 +23,75 @@ class MovieTimePageState extends State<MovieTimePage> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => RecorderBloc(),
child: Scaffold(
body: Center(
child: BlocBuilder<RecorderBloc, RecorderState>(
builder: (context, state) {
if (state is NextFrameState) {
return FutureBuilder(
future: convertImageToUint8List(state.frame!.frame!),
builder: (BuildContext context,
AsyncSnapshot<Uint8List> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: Text('Please wait its loading...'));
} else {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
context.read<RecorderBloc>().add(
CallNextFrameEvent(index: state.frame!.index! + 1));
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
context.read<RecorderBloc>().add(SaveGifEvent());
},
icon: Icon(Icons.not_started))
],
),
body: Center(
child: BlocBuilder<RecorderBloc, RecorderState>(
builder: (context, state) {
if (state is NextFrameState) {
Future.delayed(Duration(seconds: 1));
return Center(
child: Image.memory(
snapshot.data!,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.6,
),
);
} // snapshot.data :- get your object which is pass from your downloadData() function
}
},
);
} else if (state is InitialRecorderState) {
context.read<RecorderBloc>().add(CallNextFrameEvent(index: 0));
return const CircularProgressIndicator();
}
return const CircularProgressIndicator(
color: Colors.red,
return FutureBuilder(
future: convertImageToUint8List(state.frame!.frame!),
builder:
(BuildContext context, AsyncSnapshot<Uint8List> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: Text('Please wait its loading...'));
} else {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
return Center(
child: Image.memory(
snapshot.data!,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.6,
),
);
} // snapshot.data :- get your object which is pass from your downloadData() function
}
},
);
},
),
} else if (state is InitialRecorderState) {
// context.read<RecorderBloc>().add(CallNextFrameEvent(index: 0));
return const CircularProgressIndicator();
} else if (state is MessageState) {
return LinearProgressIndicator(
semanticsLabel: state.message,
);
} else if (state is ShowGifState) {
return Center(
child: Container(
color: Colors.black,
child: Image.memory(
Uint8List.fromList(state.gif!),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.6,
),
),
);
}
return const CircularProgressIndicator(
color: Colors.red,
);
},
),
),
);
}
Future<Uint8List> convertImageToUint8List(ui.Image image) {
return Future.value();
Future<Uint8List> convertImageToUint8List(ui.Image image) async {
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
return pngBytes;
}
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:bloc/bloc.dart';
@@ -7,6 +8,7 @@ import 'package:flutter/rendering.dart';
import 'package:doddle/models/recorder_controller.dart';
import '../doddler.dart';
import 'recorder_event.dart';
import 'recorder_state.dart';
@@ -14,18 +16,34 @@ class RecorderBloc extends Bloc<RecorderEvent, RecorderState> {
RecorderController? recorderController =
RecorderController(frames: [], globalKey: GlobalKey());
int index = 0;
bool isRecording = false;
int recordingIndex = 0;
Timer? timer;
RecorderBloc({this.recorderController}) : super(InitialRecorderState());
@override
Stream<RecorderState> mapEventToState(RecorderEvent event) async* {
if (event is TakeSnapshotEvent) {
final image = await canvasToImage(event.globalKey!);
final frames = recorderController!.frames;
frames!.add(Frame(frame: image, index: index++));
recorderController = recorderController!.copyWith(frames: frames);
} else if (event is CallNextFrameEvent) {
yield NextFrameState(frame: recorderController!.frames![event.index]);
if (!isRecording) {
timer =
Timer.periodic(const Duration(milliseconds: 34), (Timer t) async {
final frames = recorderController!.frames;
final image = await canvasToImage(event.globalKey!);
frames!.add(Frame(frame: image, index: index++));
recorderController = recorderController!.copyWith(frames: frames);
});
}
} else if (event is SaveGifEvent) {
add(StopRecordingEvent());
yield MessageState("Waiting ... ");
final gif = await recorderController!.export();
yield ShowGifState(gif: gif);
} else if (event is StartRecordingEvent) {
add(TakeSnapshotEvent(globalKey: Doddler.globalKey));
} else if (event is StopRecordingEvent) {
timer!.cancel();
}
}

View File

@@ -16,6 +16,8 @@ class PlayVideoEvent extends RecorderEvent {}
class StopVideoEvent extends RecorderEvent {}
class SaveGifEvent extends RecorderEvent {}
class CallNextFrameEvent extends RecorderEvent {
final int index;
CallNextFrameEvent({

View File

@@ -10,3 +10,16 @@ class NextFrameState extends RecorderState {
this.frame,
});
}
class MessageState extends RecorderState {
final String message;
MessageState(this.message);
}
class ShowGifState extends RecorderState {
final gif;
ShowGifState({
this.gif,
});
}

View File

@@ -102,7 +102,7 @@ class _ToolsWidgetState extends State<ToolsWidget> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const MovieTimePage()),
builder: (context) => MovieTimePage()),
);
},
),