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

[Bug] iOS VideoWriter release throw exception #245

Closed
ykwang opened this issue Sep 19, 2024 · 12 comments · Fixed by #262
Closed

[Bug] iOS VideoWriter release throw exception #245

ykwang opened this issue Sep 19, 2024 · 12 comments · Fixed by #262
Labels
bug Something isn't working

Comments

@ykwang
Copy link

ykwang commented Sep 19, 2024

Describe the bug
use VideoWriter write a video, before the end, release the VideoWriter, throw a exception;
And the output video is 758K, but it cannot opened.

[Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSAutoreleasePool retain]: Cannot retain an autorelease pool'
*** First throw call stack:"]

To Reproduce

cv.Mat image = cv.imread(imagePath);
cv.VideoWriter writer = cv.VideoWriter.empty();
writer.open(outVideo, "H264",30, (width, height));
if (!writer.isOpened) {
      print("Could not open the output video file $filename for write");
      return false;
 }
    for (int i = 0; i <=30; i++) {
writer .write(image )
}
writer .release();

Expected behavior

Desktop (please complete the following information):

  • OS: ios 17.6

Smartphone (please complete the following information):

  • Device: iphone 12
@ykwang ykwang added the bug Something isn't working label Sep 19, 2024
@ykwang
Copy link
Author

ykwang commented Sep 19, 2024

Use version 1.2.4 and 1.1.0 also has this problem

@rainyl
Copy link
Owner

rainyl commented Sep 19, 2024

@ykwang replace writer.release(); with writer.dispose(); and try again.

I didn't test it, please tell me whether it works, if not, I will test it further.

@ykwang
Copy link
Author

ykwang commented Sep 19, 2024

Use writer.dispose(); it also does not work.

@rainyl
Copy link
Owner

rainyl commented Sep 19, 2024

okay, I am not an apple user so it's not convenient to reproduce it, will test it when I have some time.

@ykwang
Copy link
Author

ykwang commented Sep 19, 2024

Thank You, But I need to solve this problem as soon as possible. Do you have some method I can try to solve it.

@rainyl
Copy link
Owner

rainyl commented Sep 19, 2024

Thank You, But I need to solve this problem as soon as possible. Do you have some method I can try to solve it.

So far, I have no clues, do you have more detailed logs?

@ykwang
Copy link
Author

ykwang commented Sep 20, 2024

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSAutoreleasePool retain]: Cannot retain an autorelease pool'
*** First throw call stack:
(0x189334f20 0x1811d72b8 0x18881f238 0x1e5d7cd3c 0x1e5d7d458 0x1e5d7d3ec 0x1e5d7d8e8 0x1e5d7d7f8 0x1e5d7d444 0x1e5d7d3ec 0x1882a4a2c 0x1882a48fc 0x1882a4534 0x19aca26dc 0x11301df1c 0x11302e264 0x113021b38 0x11301a5dc 0x1125f0094 0x119407e58 0x14cefa7ac 0x14cefa1bc 0x14cefa018 0x14cedc42c 0x14cef9ee4 0x14cedb3f4 0x1286a8ff0 0x1286a8e4c 0x1286a9bd4 0x1286a97dc 0x14cebe5b0 0x1286be08c 0x1286bda98 0x1286bd86c 0x1286bcc40 0x1286bae78 0x1286bab44 0x11f44201c 0x11f441418 0x119403834 0x116cd3db0 0x116cf36f0 0x116d1fd4c 0x1170af414 0x116ac5de8 0x116c5e848 0x11698fd34 0x116993e50 0x189368a2c 0x1893686d0 0x18936822c 0x189304888 0x189303cd8 0x116993f3c 0x116992fe0 0x116992cf0 0x1e5d8606c 0x1e5d810d8)
libc++abi: terminating due to uncaught exception of type NSException

@ykwang
Copy link
Author

ykwang commented Sep 20, 2024

I can not get the exception from the opencv_dart

@ykwang
Copy link
Author

ykwang commented Sep 20, 2024

When I first install apk, run it. the log is before. and I run the app second again, log is the below.
LOG:[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ...
mMovieWriter.status: 3. Error: Cannot Save

@rainyl
Copy link
Owner

rainyl commented Sep 20, 2024

@ykwang sorry for the late reply.

I have tried to reproduce it but it worked fine in my test.

0. environments

  • flutter 3.24.3
  • iPhone 15 Simulator, ios 17.2
  • opencv_dart 1.2.4

1. create the test project

flutter create --template=app --platforms=android,ios,macos,windows,linux opencv_dart_issue245
cd opencv_dart_issue245
flutter pub add opencv_dart file_picker path

2. edit lib/main.dart and replace the content with the following code

import 'dart:typed_data';

import 'package:file_picker/file_picker.dart';
import 'package:opencv_dart/opencv_dart.dart' as cv;
import 'package:path/path.dart' as p;
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // TRY THIS: Try running your application with "flutter run". You'll see
        // the application has a purple toolbar. Then, without quitting the app,
        // try changing the seedColor in the colorScheme below to Colors.green
        // and then invoke "hot reload" (save your changes or press the "hot
        // reload" button in a Flutter-supported IDE, or press "r" if you used
        // the command line to start the app).
        //
        // Notice that the counter didn't reset back to zero; the application
        // state is not lost during the reload. To reset the state, use hot
        // restart instead.
        //
        // This works for code too, not just values: Most code changes can be
        // tested with just a hot reload.
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Video IO demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int width = -1;
  int height = -1;
  double fps = -1;
  String backend = "unknown";
  String? src;
  String? dst;
  final vc = cv.VideoCapture.empty();
  final vw = cv.VideoWriter.empty();

  Uint8List? _wroteFrame;

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // TRY THIS: Try changing the color here to a specific color (to
        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
        // change color while the other colors stay the same.
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            ElevatedButton(
                onPressed: () async {
                  FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.video);
                  if (result != null) {
                    final file = result.files.single;
                    final path = file.path;
                    if (path != null) {
                      debugPrint("selected file: $path");
                      await vc.openAsync(path);
                      setState(() {
                        src = path;
                        dst = p.join("/data/user/0/com.example.videoio/cache", "output.avi");
                        width = vc.get(cv.CAP_PROP_FRAME_WIDTH).toInt();
                        height = vc.get(cv.CAP_PROP_FRAME_HEIGHT).toInt();
                        fps = vc.get(cv.CAP_PROP_FPS);
                        backend = vc.getBackendName();
                      });
                    }
                  }
                },
                child: const Text("Select a video")),
            Text("width: $width, height: $height, fps: $fps, backend: $backend"),
            Text(
              "src: $src",
              overflow: TextOverflow.ellipsis,
            ),
            Text(
              "dst: $dst",
              overflow: TextOverflow.ellipsis,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                    onPressed: () async {
                      final result =
                          await FilePicker.platform.saveFile(fileName: "output.avi", bytes: Uint8List(0));
                      if (result != null) {
                        setState(() {
                          dst = result;
                        });
                      }
                    },
                    child: const Text("Save to")),
                ElevatedButton(
                  onPressed: () async {
                    // we will write the first frame to the `dst` file and read it back
                    vw.open(dst!, "MJPG", fps, (width, height), apiPreference: cv.CAP_OPENCV_MJPEG);
                    assert(vw.isOpened);
                    final (success, frame) = await vc.readAsync();
                    debugPrint("Frame: ${frame.shape}");
                    if (success) {
                       vw.write(frame);
                      debugPrint("Write frame finished");
                    } else {
                      debugPrint("failed to read frame");
                    }
                    vw.release();

                    // read it back
                    final vc1 = cv.VideoCapture.fromFile(dst!);
                    final (s, f) = await vc1.readAsync();
                    vc1.dispose();

                    if (s) {
                      final (s1, bytes) = await cv.imencodeAsync(".png", f);

                      f.dispose();

                      if (s1) {
                        setState(() {
                          _wroteFrame = bytes;
                        });
                      } else {
                        debugPrint("encode failed");
                      }
                    } else {
                      debugPrint("failed to read frame from $dst");
                    }
                  },
                  child: const Text("Start"),
                ),
              ],
            ),
            _wroteFrame == null ? Placeholder() : Image.memory(_wroteFrame!),
          ],
        ),
      ),
    );
  }
}

3. run and result screenshot

flutter run -d "iphone"

image

Please check whether the above example can help you, if you still have problems, please give me a reproducible example.

@rainyl
Copy link
Owner

rainyl commented Sep 20, 2024

Note: I tried vw.open(dst!, "H264", fps, (width, height), apiPreference: cv.CAP_AVFOUNDATION); but it didn't work, so I changed it to vw.open(dst!, "MJPG", fps, (width, height), apiPreference: cv.CAP_OPENCV_MJPEG); according to:

@ykwang
Copy link
Author

ykwang commented Sep 20, 2024

Yes,and I should set the suffix of file to “.avi”, then use vw.open(dst!, "MJPG", fps, (width, height), apiPreference: cv.CAP_OPENCV_MJPEG); this works .
Can not set suffix to ".mp4".
Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants