Плохое состояние: поток уже прослушивался прослушивателем прогресса сжатия.Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Плохое состояние: поток уже прослушивался прослушивателем прогресса сжатия.

Сообщение Anonymous »

Привет, я пытаюсь загрузить видео из своего приложения, когда я впервые запускаю приложение и выполняю загрузку, все в порядке, но при второй загрузке выдается ошибка
Bad state: Stream has already been listed to

Оригинальная проблема:
Ошибка возникает во время сжатия видео, когда поток, контролирующий ход сжатия, вызывает проблемы. У меня есть функция _compressVideo, которая отвечает за сжатие видео с помощью библиотеки VideoCompress. Он подписывается на обновления хода сжатия через поток, но когда процесс сжатия видео инициируется несколько раз, появляется ошибка «Плохое состояние: поток уже прослушивался».
Обзор кода :
В моем UploadVideoController я отменяю все предыдущие подписки на сжатие, создаю новый StreamController для обработки обновлений хода выполнения и слушаю этот поток в функции _compressVideo. Однако ошибка по-прежнему возникает, указывая на то, что поток прослушивается более одного раза.
Проблема:
Несмотря на то, что я закрываю предыдущий контроллер потока и отменяю подписку, поток из VideoCompress.compressProgress$ может все еще содержать прослушиватель или он не очищается должным образом между последовательными задачами сжатия. Это вызывает ошибку при повторной попытке прослушать поток.
Как обеспечить правильную очистку потока и предотвратить эту ошибку при нескольких попытках сжатия?
это мой видеоконтроллер для загрузки
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:get/get.dart';
import 'package:video_compress/video_compress.dart';
import 'package:viwaad_1/constants.dart';
import 'package:viwaad_1/models/videos.dart';

class UploadVideoContoller extends GetxController {
var compressionProgress = 0.0.obs; // Progress observable
var uploadProgress = 0.0.obs;

// StreamController and StreamSubscription to manage compression progress
StreamController? _progressController;
StreamSubscription? _progressSubscription;

// Method to handle video compression
Future _compressVideo(String videoPath) async {
try {
// Clean up any previous compression progress
await _cancelPreviousCompressionProgress();

// Set logging level (optional)
VideoCompress.setLogLevel(0);
compressionProgress.value = 0.0;

// Create a new StreamController for the new compression task
_progressController = StreamController();

// Start video compression
final compressTask = VideoCompress.compressVideo(
videoPath,
quality: VideoQuality.MediumQuality,
deleteOrigin: false,
);

// Listen for progress updates
VideoCompress.compressProgress$.subscribe((progress) {
// Only feed progress to controller if it's valid
if (_progressController != null && !_progressController!.isClosed) {
_progressController!.add(progress / 100); // Normalize progress to 0-1 range
}
});

// Listen to the progress updates stream
_progressSubscription = _progressController!.stream.listen((progress) {
compressionProgress.value = progress; // Update progress observable
});

// Wait for the compression task to complete
final compressedVideo = await compressTask;

// Close the progress controller stream
await _progressController!.close();
return compressedVideo?.file;
} catch (e) {
// Handle any errors that occur during compression
Get.snackbar(
'Compression Error',
'An error occurred during video compression: $e',
snackPosition: SnackPosition.TOP,
duration: Duration(seconds: 3),
);
return null;
}
}

// Method to clean up previous progress stream and subscription
Future _cancelPreviousCompressionProgress() async {
if (_progressSubscription != null) {
await _progressSubscription!.cancel();
_progressSubscription = null;
}
if (_progressController != null) {
await _progressController!.close();
_progressController = null;
}
}

// Main method to upload video and associated metadata
Future uploadVideo(String songName, String caption, String videoPath) async {
try {
// Clean up any previous progress before starting new upload
await _cancelPreviousCompressionProgress();
String videoUrl = await _uploadVideoToStorage("Video $len", videoPath);
String profilePhoto = (userDoc.data() as Map)['profilePhoto'];

// Create video metadata object
Video video = Video(
uid: uid,
id: "Video $len",
likes: [],
commentCount: 0,
shareCount: 0,
songName: songName,
caption: caption,
videoUrl: videoUrl,
thumbnail: thumbnail,
profilePhoto: profilePhoto,
);
} catch (e) {
//error display
}
}

@override
void onClose() {
// Ensure that previous compression progress is cleaned up when the controller is disposed
_cancelPreviousCompressionProgress();
super.onClose();
}
}


а это экран загрузки
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:video_player/video_player.dart';
import 'package:viwaad_1/controllers/upload_video_contoller.dart';
import 'package:viwaad_1/views/widgets/text_input_field.dart';

class ConfirmScreen extends StatefulWidget {
final File videoFile;
final String videoPath;

const ConfirmScreen({Key? key, required this.videoFile, required this.videoPath}) : super(key: key);

@override
_ConfirmScreenState createState() => _ConfirmScreenState();
}

class _ConfirmScreenState extends State {
late VideoPlayerController _controller;
final TextEditingController _songController = TextEditingController();
final TextEditingController _captionController = TextEditingController();
final UploadVideoContoller _uploadVideoContoller = Get.put(UploadVideoContoller(), permanent: false);
bool _isUploading = false;

@override
void initState() {
super.initState();
// Initialize VideoPlayerController
_controller = VideoPlayerController.file(widget.videoFile)
..initialize().then((_) {
setState(() {
_controller.play();
_controller.setVolume(1);
_controller.setLooping(true);
});
});
}

@override
void dispose() {
_controller.dispose(); // Dispose the video controller
_uploadVideoContoller.onClose(); // Ensure the controller handles cleanup
super.dispose();
}

void startUpload() {
setState(() {
_isUploading = true;
_uploadVideoContoller.compressionProgress.value = 0.0;
_uploadVideoContoller.uploadProgress.value = 0.0;
});

_uploadVideoContoller.uploadVideo(
_songController.text,
_captionController.text,
widget.videoPath,
).then((_) {
setState(() {
_isUploading = false;
});
// Navigate back after Snackbar duration
Future.delayed(const Duration(seconds: 1), () {
if (mounted) {
Navigator.of(context).pop();
}
});
}).catchError((e) {
setState(() {
_isUploading = false;
});

Get.snackbar(
'Upload Failed',
'Error: $e',
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
);
});
}

это предыдущий экран. Я не знаю, есть ли здесь проблема, но я просто предоставляю дополнительную информацию
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:viwaad_1/views/screens/cofirm_image_screen.dart';
import 'package:viwaad_1/views/screens/confirm_screen.dart';
import 'package:viwaad_1/views/widgets/icons_add_screen.dart';

class AddScreen extends StatelessWidget {
const AddScreen({super.key});

pickVideo(ImageSource src, BuildContext context) async {
final video = await ImagePicker().pickVideo(source: src);
if (video != null) {
// Close the dialog first before navigating to the next screen
Navigator.of(context).pop();

// Navigate to the confirm screen after picking the video
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ConfirmScreen(
videoFile: File(video.path),
videoPath: video.path,
),
));
}
}

showVideoOptionsDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) => SimpleDialog(
children: [
SimpleDialogOption(
onPressed: () => pickVideo(ImageSource.gallery, context),
child: Row(
children: const [
Icon(Icons.image),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Gallery', style: TextStyle(fontSize: 20)),
)
],
),
),
SimpleDialogOption(
onPressed: () => pickVideo(ImageSource.camera, context),
child: Row(
children: const [
Icon(Icons.camera_alt),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Camera', style: TextStyle(fontSize: 20)),
)
],
),
),
SimpleDialogOption(
onPressed: () => Navigator.of(context).pop(),
child: Row(
children: const [
Icon(Icons.cancel),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Cancel', style: TextStyle(fontSize: 20)),
)
],
),
)
],
),
);
}


Подробнее здесь: https://stackoverflow.com/questions/793 ... s-listener
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»