fix video compression issues #25

This commit is contained in:
otsmr 2025-04-29 14:48:48 +02:00
parent 0f7b1755ba
commit 06715e8ac9
5 changed files with 74 additions and 52 deletions

View file

@ -181,7 +181,7 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
await writeMediaFile(media.messageId, "mp4", splited[1]); await writeMediaFile(media.messageId, "mp4", splited[1]);
} }
await writeMediaFile(media.messageId, "image", imageBytes); await writeMediaFile(media.messageId, "png", imageBytes);
} catch (e) { } catch (e) {
Logger("media_received.dart").info("Decryption error: $e"); Logger("media_received.dart").info("Decryption error: $e");
await twonlyDatabase.messagesDao.updateMessageByMessageId( await twonlyDatabase.messagesDao.updateMessageByMessageId(
@ -207,7 +207,7 @@ Future<client.Response> handleDownloadData(DownloadData data) async {
} }
Future<Uint8List?> getImageBytes(int mediaId) async { Future<Uint8List?> getImageBytes(int mediaId) async {
return await readMediaFile(mediaId, "image"); return await readMediaFile(mediaId, "png");
} }
Future<File?> getVideoPath(int mediaId) async { Future<File?> getVideoPath(int mediaId) async {

View file

@ -17,6 +17,7 @@ import 'package:twonly/src/model/protobuf/api/server_to_client.pb.dart';
import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/api/api.dart';
import 'package:twonly/src/providers/api/api_utils.dart'; import 'package:twonly/src/providers/api/api_utils.dart';
import 'package:twonly/src/services/notification_service.dart'; import 'package:twonly/src/services/notification_service.dart';
import 'package:video_compress/video_compress.dart';
Future sendMediaFile( Future sendMediaFile(
List<int> userIds, List<int> userIds,
@ -43,9 +44,9 @@ Future sendMediaFile(
if (mediaUploadId != null) { if (mediaUploadId != null) {
if (videoFilePath != null) { if (videoFilePath != null) {
String basePath = await getMediaFilePath(mediaUploadId, "send"); String basePath = await getMediaFilePath(mediaUploadId, "send");
await File(videoFilePath.path).rename("$basePath.video"); await File(videoFilePath.path).rename("$basePath.mp4");
} }
await writeMediaFile(mediaUploadId, "image", imageBytes); await writeMediaFile(mediaUploadId, "png", imageBytes);
await handleSingleMediaFile(mediaUploadId); await handleSingleMediaFile(mediaUploadId);
} }
} }
@ -165,7 +166,7 @@ Future handleAddToMessageDb(MediaUpload media) async {
} }
Future handleCompressionState(MediaUpload media) async { Future handleCompressionState(MediaUpload media) async {
Uint8List imageBytes = await readMediaFile(media, "image"); Uint8List imageBytes = await readMediaFile(media, "png");
try { try {
Uint8List imageBytesCompressed = Uint8List imageBytesCompressed =
@ -184,54 +185,56 @@ Future handleCompressionState(MediaUpload media) async {
); );
} }
await writeMediaFile( await writeMediaFile(
media.mediaUploadId, "image.compressed", imageBytesCompressed); media.mediaUploadId, "compressed.png", imageBytesCompressed);
} catch (e) { } catch (e) {
Logger("media_send.dart").shout("$e"); Logger("media_send.dart").shout("$e");
// as a fall back use the original image // as a fall back use the original image
await writeMediaFile(media.mediaUploadId, "image.compressed", imageBytes); await writeMediaFile(media.mediaUploadId, "compressed.png", imageBytes);
} }
if (media.metadata.isVideo) { if (media.metadata.isVideo) {
String basePath = await getMediaFilePath(media.mediaUploadId, "send"); String basePath = await getMediaFilePath(media.mediaUploadId, "send");
File videoOriginalFile = File("$basePath.video"); File videoOriginalFile = File("$basePath.mp4");
File videoCompressedFile = File("$basePath.video.compressed"); File videoCompressedFile = File("$basePath.compressed.mp4");
// MediaInfo? mediaInfo; MediaInfo? mediaInfo;
try { try {
// mediaInfo = await VideoCompress.compressVideo( mediaInfo = await VideoCompress.compressVideo(
// videoOriginalFile.path, videoOriginalFile.path,
// quality: VideoQuality.Res1280x720Quality, quality: VideoQuality.Res1280x720Quality,
// deleteOrigin: false, deleteOrigin: false,
// includeAudio: media.metadata.videoWithAudio, includeAudio:
// ); true, // https://github.com/jonataslaw/VideoCompress/issues/184
);
// if (mediaInfo!.filesize! >= 20 * 1000 * 1000) { if (mediaInfo!.filesize! >= 20 * 1000 * 1000) {
// // if the media file is over 20MB compress it with low quality // if the media file is over 20MB compress it with low quality
// mediaInfo = await VideoCompress.compressVideo( mediaInfo = await VideoCompress.compressVideo(
// videoOriginalFile.path, videoOriginalFile.path,
// quality: VideoQuality.Res960x540Quality, quality: VideoQuality.Res960x540Quality,
// deleteOrigin: false, deleteOrigin: false,
// includeAudio: media.metadata.videoWithAudio, includeAudio: media.metadata.videoWithAudio,
// ); );
// } }
} catch (e) { } catch (e) {
Logger("media_send.dart").shout("Video compression: $e"); Logger("media_send.dart").shout("Video compression: $e");
} }
// if (mediaInfo == null) { if (mediaInfo == null) {
Logger("media_send.dart").shout("Error compressing video.");
// as a fall back use the non compressed version // as a fall back use the non compressed version
await videoOriginalFile.copy(videoCompressedFile.path); await videoOriginalFile.copy(videoCompressedFile.path);
await videoOriginalFile.delete(); await videoOriginalFile.delete();
// } else { } else {
// Logger("media_send.dart").shout("Error compressing video."); await mediaInfo.file!.copy(videoCompressedFile.path);
// mediaInfo!.file!.rename(videoCompressedFile.path); await mediaInfo.file!.delete();
// } }
} }
// delete non compressed media files // delete non compressed media files
await deleteMediaFile(media, "image"); await deleteMediaFile(media, "png");
await deleteMediaFile(media, "video"); await deleteMediaFile(media, "mp4");
await twonlyDatabase.mediaUploadsDao.updateMediaUpload( await twonlyDatabase.mediaUploadsDao.updateMediaUpload(
media.mediaUploadId, media.mediaUploadId,
@ -246,10 +249,10 @@ Future handleCompressionState(MediaUpload media) async {
Future handleEncryptionState(MediaUpload media) async { Future handleEncryptionState(MediaUpload media) async {
var state = MediaEncryptionData(); var state = MediaEncryptionData();
Uint8List dataToEncrypt = await readMediaFile(media, "image.compressed"); Uint8List dataToEncrypt = await readMediaFile(media, "compressed.png");
if (media.metadata.isVideo) { if (media.metadata.isVideo) {
Uint8List compressedVideo = await readMediaFile(media, "video.compressed"); Uint8List compressedVideo = await readMediaFile(media, "compressed.mp4");
dataToEncrypt = combineUint8Lists(dataToEncrypt, compressedVideo); dataToEncrypt = combineUint8Lists(dataToEncrypt, compressedVideo);
} }

View file

@ -41,6 +41,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
bool useHighQuality = false; bool useHighQuality = false;
bool isVideoRecording = false; bool isVideoRecording = false;
bool hasAudioPermission = true; bool hasAudioPermission = true;
bool videoWithAudio = true;
DateTime? videoRecordingStarted; DateTime? videoRecordingStarted;
Timer? videoRecordingTimer; Timer? videoRecordingTimer;
DateTime currentTime = DateTime.now(); DateTime currentTime = DateTime.now();
@ -105,7 +106,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
controller = CameraController( controller = CameraController(
gCameras[sCameraId], gCameras[sCameraId],
ResolutionPreset.high, ResolutionPreset.high,
enableAudio: await Permission.microphone.isGranted, enableAudio: await Permission.microphone.isGranted && videoWithAudio,
); );
controller?.initialize().then((_) async { controller?.initialize().then((_) async {
if (!mounted) { if (!mounted) {
@ -508,7 +509,23 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
tooltipText: tooltipText:
"Allow microphone access for video recording.", "Allow microphone access for video recording.",
onPressed: requestMicrophonePermission, onPressed: requestMicrophonePermission,
) ),
if (hasAudioPermission)
ActionButton(
(videoWithAudio)
? Icons.volume_up_rounded
: Icons.volume_off_rounded,
tooltipText: "Record video with audio.",
color: (videoWithAudio)
? Colors.white
: Colors.white.withAlpha(160),
onPressed: () async {
setState(() {
videoWithAudio = !videoWithAudio;
});
selectCamera(cameraId);
},
),
], ],
), ),
), ),

View file

@ -196,18 +196,19 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
}, },
), ),
), ),
if (widget.videoFilePath != null) const SizedBox(height: 8), // https://github.com/jonataslaw/VideoCompress/issues/184
if (widget.videoFilePath != null) // if (widget.videoFilePath != null) const SizedBox(height: 8),
ActionButton( // if (widget.videoFilePath != null)
(videoWithAudio) ? Icons.volume_up_rounded : Icons.volume_off_rounded, // ActionButton(
tooltipText: context.lang.protectAsARealTwonly, // (videoWithAudio) ? Icons.volume_up_rounded : Icons.volume_off_rounded,
color: Colors.white, // tooltipText: context.lang.protectAsARealTwonly,
onPressed: () async { // color: Colors.white,
setState(() { // onPressed: () async {
videoWithAudio = !videoWithAudio; // setState(() {
}); // videoWithAudio = !videoWithAudio;
}, // });
), // },
// ),
const SizedBox(height: 8), const SizedBox(height: 8),
ActionButton( ActionButton(
FontAwesomeIcons.shieldHeart, FontAwesomeIcons.shieldHeart,
@ -287,6 +288,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
isRealTwonly: _isRealTwonly, isRealTwonly: _isRealTwonly,
maxShowTime: maxShowTime, maxShowTime: maxShowTime,
preselectedUser: widget.sendTo, preselectedUser: widget.sendTo,
videoFilePath: widget.videoFilePath,
), ),
), ),
); );

View file

@ -23,7 +23,7 @@ class ShareImageView extends StatefulWidget {
required this.isRealTwonly, required this.isRealTwonly,
required this.maxShowTime, required this.maxShowTime,
this.preselectedUser, this.preselectedUser,
this.videoFilePath, required this.videoFilePath,
this.enableVideoAudio}); this.enableVideoAudio});
final Future<Uint8List?> imageBytesFuture; final Future<Uint8List?> imageBytesFuture;
final bool isRealTwonly; final bool isRealTwonly;