node-mac-recorder 2.21.29 → 2.21.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/camera_recorder.mm +53 -27
package/package.json
CHANGED
package/src/camera_recorder.mm
CHANGED
|
@@ -724,27 +724,43 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
|
|
|
724
724
|
MRLog(@"🛑 CameraRecorder: Stopping session (external device safe)...");
|
|
725
725
|
|
|
726
726
|
self.isShuttingDown = YES;
|
|
727
|
-
self.isRecording = NO;
|
|
728
|
-
|
|
729
|
-
// Stop delegate FIRST to prevent new frames
|
|
730
|
-
[self.videoOutput setSampleBufferDelegate:nil queue:nil];
|
|
731
727
|
|
|
732
|
-
// Stop session on background thread to avoid blocking
|
|
728
|
+
// Stop session on background thread to avoid blocking, but wait briefly so we capture trailing frames.
|
|
733
729
|
AVCaptureSession *sessionToStop = self.session;
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
730
|
+
|
|
731
|
+
dispatch_group_t sessionStopGroup = NULL;
|
|
732
|
+
if (sessionToStop) {
|
|
733
|
+
sessionStopGroup = dispatch_group_create();
|
|
734
|
+
dispatch_group_enter(sessionStopGroup);
|
|
735
|
+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
736
|
+
@autoreleasepool {
|
|
737
|
+
@try {
|
|
738
|
+
if ([sessionToStop isRunning]) {
|
|
739
|
+
MRLog(@"🛑 Stopping AVCaptureSession (camera)...");
|
|
740
|
+
[sessionToStop stopRunning];
|
|
741
|
+
MRLog(@"✅ AVCaptureSession stopped (camera)");
|
|
742
|
+
}
|
|
743
|
+
} @catch (NSException *exception) {
|
|
744
|
+
MRLog(@"⚠️ CameraRecorder: Exception while stopping session: %@", exception.reason);
|
|
741
745
|
}
|
|
742
|
-
|
|
743
|
-
MRLog(@"⚠️ CameraRecorder: Exception while stopping session: %@", exception.reason);
|
|
746
|
+
dispatch_group_leave(sessionStopGroup);
|
|
744
747
|
}
|
|
745
|
-
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (sessionStopGroup) {
|
|
752
|
+
dispatch_time_t sessionTimeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
|
|
753
|
+
long waitResult = dispatch_group_wait(sessionStopGroup, sessionTimeout);
|
|
754
|
+
if (waitResult != 0) {
|
|
755
|
+
MRLog(@"⚠️ CameraRecorder: AVCaptureSession stop timed out after 2s (continuing shutdown)");
|
|
746
756
|
}
|
|
747
|
-
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Now detach delegate to prevent further callbacks
|
|
760
|
+
if (self.videoOutput) {
|
|
761
|
+
[self.videoOutput setSampleBufferDelegate:nil queue:nil];
|
|
762
|
+
}
|
|
763
|
+
self.isRecording = NO;
|
|
748
764
|
|
|
749
765
|
// CRITICAL FIX: Check if assetWriter exists before trying to finish it
|
|
750
766
|
// If no frames were captured, assetWriter will be nil
|
|
@@ -754,31 +770,41 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
|
|
|
754
770
|
return YES; // Success - nothing to finish
|
|
755
771
|
}
|
|
756
772
|
|
|
757
|
-
|
|
758
|
-
|
|
773
|
+
AVAssetWriter *writer = self.assetWriter;
|
|
774
|
+
AVAssetWriterInput *writerInput = self.assetWriterInput;
|
|
775
|
+
|
|
776
|
+
if (writerInput) {
|
|
777
|
+
[writerInput markAsFinished];
|
|
759
778
|
}
|
|
760
779
|
|
|
761
780
|
__block BOOL finished = NO;
|
|
762
781
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
763
782
|
|
|
764
|
-
[
|
|
783
|
+
[writer finishWritingWithCompletionHandler:^{
|
|
765
784
|
finished = YES;
|
|
766
785
|
dispatch_semaphore_signal(semaphore);
|
|
767
786
|
}];
|
|
768
787
|
|
|
769
|
-
//
|
|
770
|
-
|
|
788
|
+
// Allow generous flush time so final frames are preserved.
|
|
789
|
+
const int64_t primaryWaitSeconds = 3;
|
|
790
|
+
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(primaryWaitSeconds * NSEC_PER_SEC));
|
|
771
791
|
long result = dispatch_semaphore_wait(semaphore, timeout);
|
|
772
792
|
|
|
773
793
|
if (result != 0 || !finished) {
|
|
774
|
-
MRLog(@"⚠️ CameraRecorder:
|
|
775
|
-
|
|
776
|
-
|
|
794
|
+
MRLog(@"⚠️ CameraRecorder: Writer still finishing after %ds – waiting longer", (int)primaryWaitSeconds);
|
|
795
|
+
const int64_t extendedWaitSeconds = 5;
|
|
796
|
+
dispatch_time_t extendedTimeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(extendedWaitSeconds * NSEC_PER_SEC));
|
|
797
|
+
result = dispatch_semaphore_wait(semaphore, extendedTimeout);
|
|
777
798
|
}
|
|
778
799
|
|
|
779
|
-
|
|
800
|
+
if (result != 0 || !finished) {
|
|
801
|
+
MRLog(@"⚠️ CameraRecorder: Writer did not finish after extended wait – forcing cancel");
|
|
802
|
+
[writer cancelWriting];
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
BOOL success = (writer.status == AVAssetWriterStatusCompleted);
|
|
780
806
|
if (!success) {
|
|
781
|
-
MRLog(@"⚠️ CameraRecorder: Writer finished with status %ld error %@", (long)
|
|
807
|
+
MRLog(@"⚠️ CameraRecorder: Writer finished with status %ld error %@", (long)writer.status, writer.error);
|
|
782
808
|
} else {
|
|
783
809
|
MRLog(@"✅ CameraRecorder writer finished successfully");
|
|
784
810
|
}
|
|
@@ -791,7 +817,7 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
|
|
|
791
817
|
- (void)captureOutput:(AVCaptureOutput *)output
|
|
792
818
|
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
|
793
819
|
fromConnection:(AVCaptureConnection *)connection {
|
|
794
|
-
if (!self.isRecording
|
|
820
|
+
if (!self.isRecording) {
|
|
795
821
|
return;
|
|
796
822
|
}
|
|
797
823
|
|