13#include <QtCore/qmetaobject.h>
14#include <QtCore/qatomic.h>
22 if (!service || !service->session())
50- (
void)updateDuration:(CMTime)newTimeStamp;
51- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset;
77 bool m_writeFirstAudioBuffer;
80 CMTime m_lastTimeStamp;
81 CMTime m_lastVideoTimestamp;
82 CMTime m_lastAudioTimestamp;
86 NSDictionary *m_audioSettings;
87 NSDictionary *m_videoSettings;
89 AVFAtomicInt64 m_durationInMs;
96 if (self = [super
init]) {
98 m_setStartTime =
true;
100 m_startTime = kCMTimeInvalid;
101 m_lastTimeStamp = kCMTimeInvalid;
102 m_lastAudioTimestamp = kCMTimeInvalid;
103 m_lastVideoTimestamp = kCMTimeInvalid;
104 m_timeOffset = kCMTimeInvalid;
105 m_adjustTime =
false;
106 m_durationInMs.storeRelaxed(0);
107 m_audioSettings = nil;
108 m_videoSettings = nil;
109 m_writeFirstAudioBuffer =
false;
115- (bool)setupWithFileURL:(NSURL *)fileURL
117 audioSettings:(NSDictionary *)audioSettings
118 videoSettings:(NSDictionary *)videoSettings
120 transform:(CGAffineTransform)transform
130 m_audioSettings = audioSettings;
131 m_videoSettings = videoSettings;
135 m_writerQueue.
reset(dispatch_queue_create(
"asset-writer-queue", DISPATCH_QUEUE_SERIAL));
136 if (!m_writerQueue) {
141 m_videoQueue.
reset();
143 m_videoQueue.
reset(dispatch_queue_create(
"video-output-queue", DISPATCH_QUEUE_SERIAL));
148 dispatch_set_target_queue(m_videoQueue, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0));
151 m_audioQueue.
reset();
153 m_audioQueue.
reset(dispatch_queue_create(
"audio-output-queue", DISPATCH_QUEUE_SERIAL));
163 m_assetWriter.reset([[AVAssetWriter alloc] initWithURL:fileURL
166 if (!m_assetWriter) {
172 m_writeFirstAudioBuffer =
true;
175 m_assetWriter.reset();
179 if (m_cameraWriterInput)
180 m_cameraWriterInput.data().transform =
transform;
182 [
self setMetaData:fileType];
188- (
void)setMetaData:(AVFileType)fileType
197 m_setStartTime =
true;
201 [m_assetWriter startWriting];
203 if (!session.running)
204 [session startRunning];
212 if ([m_assetWriter status] != AVAssetWriterStatusWriting
213 && [m_assetWriter status] != AVAssetWriterStatusFailed)
225 dispatch_sync(m_writerQueue, ^{});
229 dispatch_sync(m_videoQueue, ^{});
232 [m_assetWriter finishWritingWithCompletionHandler:^{
235 if (m_state.loadAcquire() == WriterStateAborted)
238 AVCaptureSession *session = m_service->session()->captureSession();
240 [session stopRunning];
241 QMetaObject::invokeMethod(m_delegate, "assetWriterFinished", Qt::QueuedConnection);
261 dispatch_sync(m_writerQueue, ^{});
265 dispatch_sync(m_videoQueue, ^{});
269 [m_assetWriter finishWritingWithCompletionHandler:^{
277 if ([m_assetWriter status] != AVAssetWriterStatusWriting)
288 if ([m_assetWriter status] != AVAssetWriterStatusWriting)
294- (
void)setStartTimeFrom:(CMSampleBufferRef)sampleBuffer
305 m_durationInMs.storeRelease(0);
306 m_startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
307 m_lastTimeStamp = m_startTime;
308 [m_assetWriter startSessionAtSourceTime:m_startTime];
309 m_setStartTime =
false;
312- (CMSampleBufferRef)adjustTime:(CMSampleBufferRef)sample by:(CMTime)offset
315 CMSampleBufferGetSampleTimingInfoArray(sample, 0, nil, &
count);
316 CMSampleTimingInfo* timingInfo = (CMSampleTimingInfo*) malloc(
sizeof(CMSampleTimingInfo) *
count);
317 CMSampleBufferGetSampleTimingInfoArray(sample,
count, timingInfo, &
count);
318 for (CMItemCount
i = 0;
i <
count;
i++)
320 timingInfo[i].decodeTimeStamp = CMTimeSubtract(timingInfo[
i].decodeTimeStamp,
offset);
321 timingInfo[i].presentationTimeStamp = CMTimeSubtract(timingInfo[
i].presentationTimeStamp,
offset);
323 CMSampleBufferRef updatedBuffer;
324 CMSampleBufferCreateCopyWithNewTiming(kCFAllocatorDefault, sample,
count, timingInfo, &updatedBuffer);
326 return updatedBuffer;
329- (
void)writeVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
336 [
self setStartTimeFrom:sampleBuffer];
338 if (m_cameraWriterInput.data().readyForMoreMediaData) {
339 [
self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
340 [m_cameraWriterInput appendSampleBuffer:sampleBuffer];
345- (
void)writeAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
352 [
self setStartTimeFrom:sampleBuffer];
354 if (m_audioWriterInput.data().readyForMoreMediaData) {
355 [
self updateDuration:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
356 [m_audioWriterInput appendSampleBuffer:sampleBuffer];
361- (
void)captureOutput:(AVCaptureOutput *)captureOutput
362 didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
363 fromConnection:(AVCaptureConnection *)connection
371 if ([m_assetWriter status] != AVAssetWriterStatusWriting) {
372 if ([m_assetWriter status] == AVAssetWriterStatusFailed) {
373 NSError *
error = [m_assetWriter error];
374 NSString *failureReason =
error.localizedFailureReason;
375 NSString *suggestion =
error.localizedRecoverySuggestion;
376 NSString *
errorString = suggestion ? [failureReason stringByAppendingString:suggestion] : failureReason;
384 if (!CMSampleBufferDataIsReady(sampleBuffer)) {
389 CFRetain(sampleBuffer);
391 bool isVideoBuffer =
true;
397 NSObject<AVCaptureVideoDataOutputSampleBufferDelegate> *vfDelegate =
400 AVCaptureOutput *
output = nil;
402 [vfDelegate captureOutput:output didOutputSampleBuffer:sampleBuffer fromConnection:connection];
407 NSObject<AVCaptureAudioDataOutputSampleBufferDelegate> *audioPreviewDelegate =
409 if (audioPreviewDelegate) {
410 AVCaptureOutput *
output = nil;
412 [audioPreviewDelegate captureOutput:output didOutputSampleBuffer:sampleBuffer fromConnection:connection];
418 CFRelease(sampleBuffer);
423 CMTime currentTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
424 CMTime lastTimestamp = isVideoBuffer ? m_lastVideoTimestamp : m_lastAudioTimestamp;
426 if (!CMTIME_IS_INVALID(lastTimestamp)) {
427 if (!CMTIME_IS_INVALID(m_timeOffset))
428 currentTimestamp = CMTimeSubtract(currentTimestamp, m_timeOffset);
430 CMTime pauseDuration = CMTimeSubtract(currentTimestamp, lastTimestamp);
432 if (m_timeOffset.value == 0)
433 m_timeOffset = pauseDuration;
435 m_timeOffset = CMTimeAdd(m_timeOffset, pauseDuration);
437 m_lastVideoTimestamp = kCMTimeInvalid;
438 m_adjustTime =
false;
441 if (m_timeOffset.value > 0) {
442 CFRelease(sampleBuffer);
443 sampleBuffer = [
self adjustTime:sampleBuffer by:m_timeOffset];
446 CMTime currentTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
447 CMTime currentDuration = CMSampleBufferGetDuration(sampleBuffer);
448 if (currentDuration.value > 0)
449 currentTimestamp = CMTimeAdd(currentTimestamp, currentDuration);
453 m_lastVideoTimestamp = currentTimestamp;
454 dispatch_async(m_writerQueue, ^{
455 [
self writeVideoSampleBuffer:sampleBuffer];
456 m_writeFirstAudioBuffer =
true;
457 CFRelease(sampleBuffer);
459 }
else if (m_writeFirstAudioBuffer) {
460 m_lastAudioTimestamp = currentTimestamp;
461 dispatch_async(m_writerQueue, ^{
462 [
self writeAudioSampleBuffer:sampleBuffer];
463 CFRelease(sampleBuffer);
475 m_cameraWriterInput.reset();
479 m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
480 outputSettings:m_videoSettings
481 sourceFormatHint:session->videoCaptureDevice().activeFormat.formatDescription]);
483 if (m_cameraWriterInput && [m_assetWriter canAddInput:m_cameraWriterInput]) {
484 [m_assetWriter addInput:m_cameraWriterInput];
487 m_cameraWriterInput.reset();
491 m_cameraWriterInput.data().expectsMediaDataInRealTime = YES;
494 m_audioWriterInput.reset();
496 m_audioWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio
497 outputSettings:m_audioSettings]);
498 if (!m_audioWriterInput) {
501 if (!m_cameraWriterInput)
503 }
else if ([m_assetWriter canAddInput:m_audioWriterInput]) {
504 [m_assetWriter addInput:m_audioWriterInput];
505 m_audioWriterInput.data().expectsMediaDataInRealTime = YES;
508 m_audioWriterInput.reset();
509 if (!m_cameraWriterInput)
525 [session->videoOutput()->videoDataOutput() setSampleBufferDelegate:self queue:m_videoQueue];
530 [session->audioOutput() setSampleBufferDelegate:self queue:m_audioQueue];
534- (
void)updateDuration:(CMTime)newTimeStamp
536 Q_ASSERT(CMTimeCompare(m_startTime, kCMTimeInvalid));
537 Q_ASSERT(CMTimeCompare(m_lastTimeStamp, kCMTimeInvalid));
538 if (CMTimeCompare(newTimeStamp, m_lastTimeStamp) > 0) {
540 const CMTime duration = CMTimeSubtract(newTimeStamp, m_startTime);
541 if (!CMTimeCompare(duration, kCMTimeInvalid))
544 m_durationInMs.storeRelease(CMTimeGetSeconds(duration) * 1000);
545 m_lastTimeStamp = newTimeStamp;
553 return m_durationInMs.loadAcquire();
DarwinBluetooth::DeviceInquiryDelegate * m_delegate
AVCaptureVideoDataOutput * videoDataOutput() const
AVFCaptureFramesDelegate * captureDelegate() const
AVFCameraSession * session() const
AVCaptureDeviceInput * videoInput() const
AVCaptureSession * captureSession() const
AVCaptureDevice * videoCaptureDevice() const
AVFCameraRenderer * videoOutput() const
AVCaptureDeviceInput * audioInput() const
AVFAudioPreviewDelegate * audioPreviewDelegate() const
AVCaptureAudioDataOutput * audioOutput() const
void reset(dispatch_queue_t q=nullptr)
T fetchAndStoreRelease(T newValue) noexcept
void storeRelaxed(T newValue) noexcept
T loadAcquire() const noexcept
void storeRelease(T newValue) noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
bool qt_capture_session_isValid(AVFCameraService *service)
Q_CORE_EXPORT QtJniTypes::Service service()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError * error
DBusConnection * connection
#define qCDebug(category,...)
#define Q_ARG(Type, data)
GLenum GLenum GLsizei count
GLenum GLuint GLintptr offset
GLuint GLenum GLenum transform
static FileType fileType(const QFileInfo &fi)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
QT_BEGIN_NAMESPACE typedef uchar * output