node-mac-recorder 2.21.26 → 2.21.27
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/audio_recorder.mm +58 -2
- package/src/camera_recorder.mm +17 -4
package/package.json
CHANGED
package/src/audio_recorder.mm
CHANGED
|
@@ -314,7 +314,7 @@ static dispatch_queue_t g_audioCaptureQueue = nil;
|
|
|
314
314
|
NSLog(@"❌ Audio writer failed to start: %@", self.writer.error);
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
|
-
[self.writer startSessionAtSourceTime:
|
|
317
|
+
[self.writer startSessionAtSourceTime:kCMTimeZero];
|
|
318
318
|
self.writerStarted = YES;
|
|
319
319
|
self.startTime = timestamp;
|
|
320
320
|
}
|
|
@@ -323,9 +323,65 @@ static dispatch_queue_t g_audioCaptureQueue = nil;
|
|
|
323
323
|
return;
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
if (
|
|
326
|
+
if (CMTIME_IS_INVALID(self.startTime)) {
|
|
327
|
+
self.startTime = timestamp;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
CMSampleBufferRef bufferToAppend = sampleBuffer;
|
|
331
|
+
CMItemCount timingEntryCount = 0;
|
|
332
|
+
OSStatus timingStatus = CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, 0, NULL, &timingEntryCount);
|
|
333
|
+
CMSampleTimingInfo *timingInfo = NULL;
|
|
334
|
+
|
|
335
|
+
if (timingStatus == noErr && timingEntryCount > 0) {
|
|
336
|
+
timingInfo = (CMSampleTimingInfo *)malloc(sizeof(CMSampleTimingInfo) * timingEntryCount);
|
|
337
|
+
if (timingInfo) {
|
|
338
|
+
timingStatus = CMSampleBufferGetSampleTimingInfoArray(sampleBuffer, timingEntryCount, timingInfo, &timingEntryCount);
|
|
339
|
+
|
|
340
|
+
if (timingStatus == noErr) {
|
|
341
|
+
for (CMItemCount i = 0; i < timingEntryCount; ++i) {
|
|
342
|
+
// Shift audio timestamps to begin at t=0 so they align with camera capture
|
|
343
|
+
if (CMTIME_IS_VALID(timingInfo[i].presentationTimeStamp)) {
|
|
344
|
+
CMTime adjustedPTS = CMTimeSubtract(timingInfo[i].presentationTimeStamp, self.startTime);
|
|
345
|
+
if (CMTIME_COMPARE_INLINE(adjustedPTS, <, kCMTimeZero)) {
|
|
346
|
+
adjustedPTS = kCMTimeZero;
|
|
347
|
+
}
|
|
348
|
+
timingInfo[i].presentationTimeStamp = adjustedPTS;
|
|
349
|
+
} else {
|
|
350
|
+
timingInfo[i].presentationTimeStamp = kCMTimeZero;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (CMTIME_IS_VALID(timingInfo[i].decodeTimeStamp)) {
|
|
354
|
+
CMTime adjustedDTS = CMTimeSubtract(timingInfo[i].decodeTimeStamp, self.startTime);
|
|
355
|
+
if (CMTIME_COMPARE_INLINE(adjustedDTS, <, kCMTimeZero)) {
|
|
356
|
+
adjustedDTS = kCMTimeZero;
|
|
357
|
+
}
|
|
358
|
+
timingInfo[i].decodeTimeStamp = adjustedDTS;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
CMSampleBufferRef adjustedBuffer = NULL;
|
|
363
|
+
timingStatus = CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault,
|
|
364
|
+
sampleBuffer,
|
|
365
|
+
timingEntryCount,
|
|
366
|
+
timingInfo,
|
|
367
|
+
&adjustedBuffer);
|
|
368
|
+
if (timingStatus == noErr && adjustedBuffer) {
|
|
369
|
+
bufferToAppend = adjustedBuffer;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
free(timingInfo);
|
|
374
|
+
timingInfo = NULL;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (![self.writerInput appendSampleBuffer:bufferToAppend]) {
|
|
327
379
|
NSLog(@"⚠️ Failed appending audio buffer: %@", self.writer.error);
|
|
328
380
|
}
|
|
381
|
+
|
|
382
|
+
if (bufferToAppend != sampleBuffer) {
|
|
383
|
+
CFRelease(bufferToAppend);
|
|
384
|
+
}
|
|
329
385
|
}
|
|
330
386
|
|
|
331
387
|
@end
|
package/src/camera_recorder.mm
CHANGED
|
@@ -837,10 +837,10 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
|
|
|
837
837
|
if (!self.writerStarted) {
|
|
838
838
|
if (self.assetWriter.status == AVAssetWriterStatusUnknown) {
|
|
839
839
|
if ([self.assetWriter startWriting]) {
|
|
840
|
-
[self.assetWriter startSessionAtSourceTime:
|
|
840
|
+
[self.assetWriter startSessionAtSourceTime:kCMTimeZero];
|
|
841
841
|
self.writerStarted = YES;
|
|
842
842
|
self.firstSampleTime = timestamp;
|
|
843
|
-
MRLog(@"✅ Camera writer started");
|
|
843
|
+
MRLog(@"✅ Camera writer started (zero-based timeline)");
|
|
844
844
|
} else {
|
|
845
845
|
MRLog(@"❌ CameraRecorder: Failed to start asset writer: %@", self.assetWriter.error);
|
|
846
846
|
self.isRecording = NO;
|
|
@@ -862,13 +862,26 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
|
|
|
862
862
|
return;
|
|
863
863
|
}
|
|
864
864
|
|
|
865
|
+
if (CMTIME_IS_INVALID(self.firstSampleTime)) {
|
|
866
|
+
self.firstSampleTime = timestamp;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
CMTime relativeTimestamp = timestamp;
|
|
870
|
+
if (CMTIME_IS_VALID(self.firstSampleTime)) {
|
|
871
|
+
// Align camera frames to a zero-based timeline so multi-track compositions stay in sync
|
|
872
|
+
relativeTimestamp = CMTimeSubtract(timestamp, self.firstSampleTime);
|
|
873
|
+
if (CMTIME_COMPARE_INLINE(relativeTimestamp, <, kCMTimeZero)) {
|
|
874
|
+
relativeTimestamp = kCMTimeZero;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
865
878
|
CVPixelBufferRetain(pixelBuffer);
|
|
866
|
-
BOOL appended = [self.pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:
|
|
879
|
+
BOOL appended = [self.pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:relativeTimestamp];
|
|
867
880
|
CVPixelBufferRelease(pixelBuffer);
|
|
868
881
|
|
|
869
882
|
if (!appended) {
|
|
870
883
|
MRLog(@"⚠️ CameraRecorder: Failed to append camera frame at time %.2f (status %ld)",
|
|
871
|
-
CMTimeGetSeconds(
|
|
884
|
+
CMTimeGetSeconds(relativeTimestamp), (long)self.assetWriter.status);
|
|
872
885
|
if (self.assetWriter.status == AVAssetWriterStatusFailed) {
|
|
873
886
|
MRLog(@"❌ CameraRecorder writer failure: %@", self.assetWriter.error);
|
|
874
887
|
self.isRecording = NO;
|