Привет, я пытаюсь загрузить видео из своего приложения, когда я впервые запускаю приложение и выполняю загрузку, все в порядке, но при второй загрузке выдается ошибка
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
Плохое состояние: поток уже прослушивался прослушивателем прогресса сжатия. ⇐ Android
Форум для тех, кто программирует под Android
1736336168
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)),
)
],
),
)
],
),
);
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79338426/bad-state-stream-has-already-been-listened-to-compression-progress-listener[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия