MediaRecorder stopRecorder() returns empty Blob after first use
[WebKit-https.git] / Source / WebCore / platform / mediarecorder / cocoa / AudioSampleBufferCompressor.mm
1 /*
2  * Copyright (C) 2020 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "AudioSampleBufferCompressor.h"
27
28 #if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
29
30 #include "Logging.h"
31 #include <AudioToolbox/AudioCodec.h>
32 #include <AudioToolbox/AudioConverter.h>
33 #include <AudioToolbox/AudioFormat.h>
34 #include <Foundation/Foundation.h>
35
36 #import <pal/cf/AudioToolboxSoftLink.h>
37
38 #define LOW_WATER_TIME_IN_SECONDS 0.1
39
40 namespace WebCore {
41
42 using namespace PAL;
43
44 std::unique_ptr<AudioSampleBufferCompressor> AudioSampleBufferCompressor::create(CMBufferQueueTriggerCallback callback, void* callbackObject)
45 {
46     auto compressor = std::unique_ptr<AudioSampleBufferCompressor>(new AudioSampleBufferCompressor());
47     if (!compressor->initialize(callback, callbackObject))
48         return nullptr;
49     return compressor;
50 }
51
52 AudioSampleBufferCompressor::AudioSampleBufferCompressor()
53     : m_serialDispatchQueue { dispatch_queue_create("com.apple.AudioSampleBufferCompressor", DISPATCH_QUEUE_SERIAL) }
54     , m_lowWaterTime { CMTimeMakeWithSeconds(LOW_WATER_TIME_IN_SECONDS, 1000) }
55 {
56 }
57
58 AudioSampleBufferCompressor::~AudioSampleBufferCompressor()
59 {
60     dispatch_release(m_serialDispatchQueue);
61     if (m_converter) {
62         AudioConverterDispose(m_converter);
63         m_converter = nullptr;
64     }
65 }
66
67 bool AudioSampleBufferCompressor::initialize(CMBufferQueueTriggerCallback callback, void* callbackObject)
68 {
69     CMBufferQueueRef inputBufferQueue;
70     if (auto error = CMBufferQueueCreate(kCFAllocatorDefault, 0, CMBufferQueueGetCallbacksForUnsortedSampleBuffers(), &inputBufferQueue)) {
71         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBufferQueueCreate for m_inputBufferQueue failed with %d", error);
72         return false;
73     }
74     m_inputBufferQueue = adoptCF(inputBufferQueue);
75
76     CMBufferQueueRef outputBufferQueue;
77     if (auto error = CMBufferQueueCreate(kCFAllocatorDefault, 0, CMBufferQueueGetCallbacksForUnsortedSampleBuffers(), &outputBufferQueue)) {
78         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBufferQueueCreate for m_outputBufferQueue failed with %d", error);
79         return false;
80     }
81     m_outputBufferQueue = adoptCF(outputBufferQueue);
82     CMBufferQueueInstallTrigger(m_outputBufferQueue.get(), callback, callbackObject, kCMBufferQueueTrigger_WhenDataBecomesReady, kCMTimeZero, NULL);
83
84     m_isEncoding = true;
85     return true;
86 }
87
88 void AudioSampleBufferCompressor::finish()
89 {
90     dispatch_sync(m_serialDispatchQueue, ^{
91         processSampleBuffersUntilLowWaterTime(kCMTimeInvalid);
92         auto error = CMBufferQueueMarkEndOfData(m_outputBufferQueue.get());
93         RELEASE_LOG_ERROR_IF(error, MediaStream, "AudioSampleBufferCompressor CMBufferQueueMarkEndOfData failed %d", error);
94         m_isEncoding = false;
95     });
96 }
97
98 bool AudioSampleBufferCompressor::initAudioConverterForSourceFormatDescription(CMFormatDescriptionRef formatDescription, AudioFormatID outputFormatID)
99 {
100     const auto *audioFormatListItem = CMAudioFormatDescriptionGetRichestDecodableFormat(formatDescription);
101     m_sourceFormat = audioFormatListItem->mASBD;
102
103     memset(&m_destinationFormat, 0, sizeof(AudioStreamBasicDescription));
104     m_destinationFormat.mFormatID = outputFormatID;
105     m_destinationFormat.mSampleRate = m_sourceFormat.mSampleRate;
106     m_destinationFormat.mChannelsPerFrame = m_sourceFormat.mChannelsPerFrame;
107
108     UInt32 size = sizeof(m_destinationFormat);
109     if (auto error = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &m_destinationFormat)) {
110         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor AudioFormatGetProperty failed with %d", error);
111         return false;
112     }
113
114     AudioConverterRef converter;
115     if (auto error = AudioConverterNew(&m_sourceFormat, &m_destinationFormat, &converter)) {
116         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor AudioConverterNew failed with %d", error);
117         return false;
118     }
119     m_converter = converter;
120
121     size_t cookieSize = 0;
122     const void *cookie = CMAudioFormatDescriptionGetMagicCookie(formatDescription, &cookieSize);
123     if (cookieSize) {
124         if (auto error = AudioConverterSetProperty(m_converter, kAudioConverterDecompressionMagicCookie, (UInt32)cookieSize, cookie)) {
125             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioConverterDecompressionMagicCookie failed with %d", error);
126             return false;
127         }
128     }
129
130     size = sizeof(m_sourceFormat);
131     if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterCurrentInputStreamDescription, &size, &m_sourceFormat)) {
132         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterCurrentInputStreamDescription failed with %d", error);
133         return false;
134     }
135
136     if (!m_sourceFormat.mBytesPerPacket) {
137         RELEASE_LOG_ERROR(MediaStream, "mBytesPerPacket should not be zero");
138         return false;
139     }
140
141     size = sizeof(m_destinationFormat);
142     if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterCurrentOutputStreamDescription, &size, &m_destinationFormat)) {
143         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterCurrentOutputStreamDescription failed with %d", error);
144         return false;
145     }
146
147     if (m_destinationFormat.mFormatID == kAudioFormatMPEG4AAC) {
148         // FIXME: Set outputBitRate according MediaRecorderOptions.audioBitsPerSecond.
149         UInt32 outputBitRate = 64000;
150         if (m_destinationFormat.mSampleRate >= 44100)
151             outputBitRate = 192000;
152         else if (m_destinationFormat.mSampleRate < 22000)
153             outputBitRate = 32000;
154
155         size = sizeof(outputBitRate);
156         if (auto error = AudioConverterSetProperty(m_converter, kAudioConverterEncodeBitRate, size, &outputBitRate)) {
157             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioConverterEncodeBitRate failed with %d", error);
158             return false;
159         }
160     }
161
162     if (!m_destinationFormat.mBytesPerPacket) {
163         // If the destination format is VBR, we need to get max size per packet from the converter.
164         size = sizeof(m_maxOutputPacketSize);
165
166         if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterPropertyMaximumOutputPacketSize, &size, &m_maxOutputPacketSize)) {
167             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterPropertyMaximumOutputPacketSize failed with %d", error);
168             return false;
169         }
170     }
171
172     auto destinationBufferSize = computeBufferSizeForAudioFormat(m_destinationFormat, m_maxOutputPacketSize, LOW_WATER_TIME_IN_SECONDS);
173     if (m_destinationBuffer.size() < destinationBufferSize)
174         m_destinationBuffer.resize(destinationBufferSize);
175     if (!m_destinationFormat.mBytesPerPacket)
176         m_destinationPacketDescriptions.resize(m_destinationBuffer.capacity() / m_maxOutputPacketSize);
177
178     return true;
179 }
180
181 size_t AudioSampleBufferCompressor::computeBufferSizeForAudioFormat(AudioStreamBasicDescription format, UInt32 maxOutputPacketSize, Float32 duration)
182 {
183     UInt32 numPackets = (format.mSampleRate * duration) / format.mFramesPerPacket;
184     UInt32 outputPacketSize = format.mBytesPerPacket ? format.mBytesPerPacket : maxOutputPacketSize;
185     UInt32 bufferSize = numPackets * outputPacketSize;
186
187     return bufferSize;
188 }
189
190 void AudioSampleBufferCompressor::attachPrimingTrimsIfNeeded(CMSampleBufferRef buffer)
191 {
192     if (CMTIME_IS_INVALID(m_remainingPrimeDuration)) {
193         AudioConverterPrimeInfo primeInfo { 0, 0 };
194         UInt32 size = sizeof(primeInfo);
195
196         if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterPrimeInfo, &size, &primeInfo)) {
197             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterPrimeInfo failed with %d", error);
198             return;
199         }
200
201         m_remainingPrimeDuration = CMTimeMake(primeInfo.leadingFrames, m_destinationFormat.mSampleRate);
202     }
203
204     if (CMTIME_COMPARE_INLINE(kCMTimeZero, <, m_remainingPrimeDuration)) {
205         CMTime sampleDuration = CMSampleBufferGetDuration(buffer);
206         CMTime trimDuration = CMTimeMinimum(sampleDuration, m_remainingPrimeDuration);
207         CFDictionaryRef trimAtStartDict = CMTimeCopyAsDictionary(trimDuration, kCFAllocatorDefault);
208         CMSetAttachment(buffer, kCMSampleBufferAttachmentKey_TrimDurationAtStart, trimAtStartDict, kCMAttachmentMode_ShouldPropagate);
209         CFRelease(trimAtStartDict);
210         m_remainingPrimeDuration = CMTimeSubtract(m_remainingPrimeDuration, trimDuration);
211     }
212 }
213
214 RetainPtr<NSNumber> AudioSampleBufferCompressor::gradualDecoderRefreshCount()
215 {
216     UInt32 delaySize = sizeof(uint32_t);
217     uint32_t originalDelayMode = 0;
218     if (auto error = AudioConverterGetProperty(m_converter, kAudioCodecPropertyDelayMode, &delaySize, &originalDelayMode)) {
219         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioCodecPropertyDelayMode failed with %d", error);
220         return nil;
221     }
222
223     uint32_t optimalDelayMode = kAudioCodecDelayMode_Optimal;
224     if (auto error = AudioConverterSetProperty(m_converter, kAudioCodecPropertyDelayMode, delaySize, &optimalDelayMode)) {
225         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioCodecPropertyDelayMode failed with %d", error);
226         return nil;
227     }
228
229     UInt32 primeSize = sizeof(AudioCodecPrimeInfo);
230     AudioCodecPrimeInfo primeInfo { 0, 0 };
231     if (auto error = AudioConverterGetProperty(m_converter, kAudioCodecPropertyPrimeInfo, &primeSize, &primeInfo)) {
232         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioCodecPropertyPrimeInfo failed with %d", error);
233         return nil;
234     }
235
236     if (auto error = AudioConverterSetProperty(m_converter, kAudioCodecPropertyDelayMode, delaySize, &originalDelayMode)) {
237         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor setting kAudioCodecPropertyDelayMode failed with %d", error);
238         return nil;
239     }
240     return adoptNS([NSNumber numberWithInt:(primeInfo.leadingFrames / m_destinationFormat.mFramesPerPacket)]);
241 }
242
243 CMSampleBufferRef AudioSampleBufferCompressor::sampleBufferWithNumPackets(UInt32 numPackets, AudioBufferList fillBufferList)
244 {
245     Vector<char> cookie;
246     if (!m_destinationFormatDescription) {
247         UInt32 cookieSize = 0;
248
249         auto error = AudioConverterGetPropertyInfo(m_converter, kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
250         if ((error == noErr) && !!cookieSize) {
251             cookie.resize(cookieSize);
252
253             if (auto error = AudioConverterGetProperty(m_converter, kAudioConverterCompressionMagicCookie, &cookieSize, cookie.data())) {
254                 RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor getting kAudioConverterCompressionMagicCookie failed with %d", error);
255                 return nil;
256             }
257         }
258
259         CMFormatDescriptionRef destinationFormatDescription;
260         if (auto error = CMAudioFormatDescriptionCreate(kCFAllocatorDefault, &m_destinationFormat, 0, NULL, cookieSize, cookie.data(), NULL, &destinationFormatDescription)) {
261             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMAudioFormatDescriptionCreate failed with %d", error);
262             return nil;
263         }
264         m_destinationFormatDescription = adoptCF(destinationFormatDescription);
265         m_gdrCountNum = gradualDecoderRefreshCount();
266     }
267
268     char *data = static_cast<char*>(fillBufferList.mBuffers[0].mData);
269     size_t dataSize = fillBufferList.mBuffers[0].mDataByteSize;
270
271     CMBlockBufferRef blockBuffer;
272     if (auto error = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, dataSize, kCFAllocatorDefault, NULL, 0, dataSize, kCMBlockBufferAssureMemoryNowFlag, &blockBuffer)) {
273         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBlockBufferCreateWithMemoryBlock failed with %d", error);
274         return nil;
275     }
276     auto buffer = adoptCF(blockBuffer);
277
278     if (auto error = CMBlockBufferReplaceDataBytes(data, buffer.get(), 0, dataSize)) {
279         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBlockBufferReplaceDataBytes failed with %d", error);
280         return nil;
281     }
282
283     CMSampleBufferRef sampleBuffer;
284     auto error = CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, buffer.get(), true, NULL, NULL, m_destinationFormatDescription.get(), numPackets, m_currentNativePresentationTimeStamp, m_destinationPacketDescriptions.data(), &sampleBuffer);
285     if (error) {
286         RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMAudioSampleBufferCreateWithPacketDescriptions failed with %d", error);
287         return nil;
288     }
289
290     if ([m_gdrCountNum intValue])
291         CMSetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_GradualDecoderRefresh, (__bridge CFTypeRef)m_gdrCountNum.get(), kCMAttachmentMode_ShouldPropagate);
292
293     return sampleBuffer;
294 }
295
296 OSStatus AudioSampleBufferCompressor::audioConverterComplexInputDataProc(AudioConverterRef, UInt32 *numOutputPacketsPtr, AudioBufferList *bufferList, AudioStreamPacketDescription **packetDescriptionOut, void *audioSampleBufferCompressor)
297 {
298     auto *compressor = static_cast<AudioSampleBufferCompressor*>(audioSampleBufferCompressor);
299     return compressor->provideSourceDataNumOutputPackets(numOutputPacketsPtr, bufferList, packetDescriptionOut);
300 }
301
302 OSStatus AudioSampleBufferCompressor::provideSourceDataNumOutputPackets(UInt32* numOutputPacketsPtr, AudioBufferList* audioBufferList, AudioStreamPacketDescription** packetDescriptionOut)
303 {
304     if (packetDescriptionOut)
305         *packetDescriptionOut = NULL;
306
307     const UInt32 numPacketsToCopy = *numOutputPacketsPtr;
308     size_t numBytesToCopy = (numPacketsToCopy * m_sourceFormat.mBytesPerPacket);
309
310     if (audioBufferList->mNumberBuffers == 1) {
311         size_t currentOffsetInSourceBuffer = 0;
312
313         if (m_sourceBuffer.size() < numBytesToCopy)
314             m_sourceBuffer.resize(numBytesToCopy);
315
316         while (numBytesToCopy) {
317             if (m_sampleBlockBufferSize <= m_currentOffsetInSampleBlockBuffer) {
318                 if (m_sampleBlockBuffer) {
319                     m_sampleBlockBuffer = nullptr;
320                     m_sampleBlockBufferSize = 0;
321                 }
322
323                 if (CMBufferQueueIsEmpty(m_inputBufferQueue.get()))
324                     break;
325
326                 auto sampleBuffer = adoptCF((CMSampleBufferRef)(const_cast<void*>(CMBufferQueueDequeueAndRetain(m_inputBufferQueue.get()))));
327                 m_sampleBlockBuffer = adoptCF((CMBlockBufferRef)(const_cast<void*>(CFRetain(CMSampleBufferGetDataBuffer(sampleBuffer.get())))));
328                 if (!m_sampleBlockBuffer) {
329                     RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMSampleBufferGetDataBuffer failed");
330                     m_sampleBlockBufferSize = 0;
331                     m_currentOffsetInSampleBlockBuffer = 0;
332                     continue;
333                 }
334                 m_sampleBlockBufferSize = CMBlockBufferGetDataLength(m_sampleBlockBuffer.get());
335                 m_currentOffsetInSampleBlockBuffer = 0;
336             }
337
338             if (m_sampleBlockBuffer) {
339                 size_t numBytesToCopyFromSampleBbuf = std::min(numBytesToCopy, (m_sampleBlockBufferSize - m_currentOffsetInSampleBlockBuffer));
340                 if (auto error = CMBlockBufferCopyDataBytes(m_sampleBlockBuffer.get(), m_currentOffsetInSampleBlockBuffer, numBytesToCopyFromSampleBbuf, (m_sourceBuffer.data() + currentOffsetInSourceBuffer))) {
341                     RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBlockBufferCopyDataBytes failed with %d", error);
342                     return error;
343                 }
344                 numBytesToCopy -= numBytesToCopyFromSampleBbuf;
345                 currentOffsetInSourceBuffer += numBytesToCopyFromSampleBbuf;
346                 m_currentOffsetInSampleBlockBuffer += numBytesToCopyFromSampleBbuf;
347             }
348         }
349         audioBufferList->mBuffers[0].mData = m_sourceBuffer.data();
350         audioBufferList->mBuffers[0].mDataByteSize = currentOffsetInSourceBuffer;
351         audioBufferList->mBuffers[0].mNumberChannels = m_sourceFormat.mChannelsPerFrame;
352
353         *numOutputPacketsPtr = (audioBufferList->mBuffers[0].mDataByteSize / m_sourceFormat.mBytesPerPacket);
354         return noErr;
355     }
356
357     ASSERT(audioBufferList->mNumberBuffers == 2);
358
359     // FIXME: Support interleaved data by uninterleaving m_sourceBuffer if needed.
360     ASSERT(m_sourceFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
361
362     if (m_sourceBuffer.size() < 2 * numBytesToCopy)
363         m_sourceBuffer.resize(2 * numBytesToCopy);
364     auto* firstChannel = m_sourceBuffer.data();
365     auto* secondChannel = m_sourceBuffer.data() + numBytesToCopy;
366
367     size_t currentOffsetInSourceBuffer = 0;
368     while (numBytesToCopy) {
369         if (m_sampleBlockBufferSize <= m_currentOffsetInSampleBlockBuffer) {
370             if (m_sampleBlockBuffer) {
371                 m_sampleBlockBuffer = nullptr;
372                 m_sampleBlockBufferSize = 0;
373             }
374
375             if (CMBufferQueueIsEmpty(m_inputBufferQueue.get()))
376                 break;
377
378             auto sampleBuffer = adoptCF((CMSampleBufferRef)(const_cast<void*>(CMBufferQueueDequeueAndRetain(m_inputBufferQueue.get()))));
379             m_sampleBlockBuffer = adoptCF((CMBlockBufferRef)(const_cast<void*>(CFRetain(CMSampleBufferGetDataBuffer(sampleBuffer.get())))));
380             if (!m_sampleBlockBuffer) {
381                 RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMSampleBufferGetDataBuffer failed");
382                 m_sampleBlockBufferSize = 0;
383                 m_currentOffsetInSampleBlockBuffer = 0;
384                 continue;
385             }
386             m_sampleBlockBufferSize = CMBlockBufferGetDataLength(m_sampleBlockBuffer.get()) / 2;
387             m_currentOffsetInSampleBlockBuffer = 0;
388         }
389
390         if (m_sampleBlockBuffer) {
391             size_t numBytesToCopyFromSampleBbuf = std::min(numBytesToCopy, (m_sampleBlockBufferSize - m_currentOffsetInSampleBlockBuffer));
392             if (auto error = CMBlockBufferCopyDataBytes(m_sampleBlockBuffer.get(), m_currentOffsetInSampleBlockBuffer, numBytesToCopyFromSampleBbuf, (firstChannel + currentOffsetInSourceBuffer))) {
393                 RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBlockBufferCopyDataBytes first channel failed with %d", error);
394                 return error;
395             }
396             if (auto error = CMBlockBufferCopyDataBytes(m_sampleBlockBuffer.get(), m_currentOffsetInSampleBlockBuffer + m_sampleBlockBufferSize, numBytesToCopyFromSampleBbuf, (secondChannel + currentOffsetInSourceBuffer))) {
397                 RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBlockBufferCopyDataBytes second channel failed with %d", error);
398                 return error;
399             }
400             numBytesToCopy -= numBytesToCopyFromSampleBbuf;
401             currentOffsetInSourceBuffer += numBytesToCopyFromSampleBbuf;
402             m_currentOffsetInSampleBlockBuffer += numBytesToCopyFromSampleBbuf;
403         }
404     }
405
406     audioBufferList->mBuffers[0].mData = firstChannel;
407     audioBufferList->mBuffers[0].mDataByteSize = currentOffsetInSourceBuffer;
408     audioBufferList->mBuffers[0].mNumberChannels = 1;
409
410     audioBufferList->mBuffers[1].mData = secondChannel;
411     audioBufferList->mBuffers[1].mDataByteSize = currentOffsetInSourceBuffer;
412     audioBufferList->mBuffers[1].mNumberChannels = 1;
413
414     *numOutputPacketsPtr = (audioBufferList->mBuffers[0].mDataByteSize / m_sourceFormat.mBytesPerPacket);
415     return noErr;
416 }
417
418 void AudioSampleBufferCompressor::processSampleBuffersUntilLowWaterTime(CMTime lowWaterTime)
419 {
420     if (!m_converter) {
421         if (CMBufferQueueIsEmpty(m_inputBufferQueue.get()))
422             return;
423
424         auto buffer = (CMSampleBufferRef)(const_cast<void*>(CMBufferQueueGetHead(m_inputBufferQueue.get())));
425         ASSERT(buffer);
426
427         m_currentNativePresentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(buffer);
428         m_currentOutputPresentationTimeStamp = CMSampleBufferGetOutputPresentationTimeStamp(buffer);
429
430         auto formatDescription = CMSampleBufferGetFormatDescription(buffer);
431         if (!initAudioConverterForSourceFormatDescription(formatDescription, m_outputCodecType))
432             return;
433     }
434
435     while (CMTIME_IS_INVALID(lowWaterTime) || CMTIME_COMPARE_INLINE(lowWaterTime, <, CMBufferQueueGetDuration(m_inputBufferQueue.get()))) {
436         AudioBufferList fillBufferList;
437
438         fillBufferList.mNumberBuffers = 1;
439         fillBufferList.mBuffers[0].mNumberChannels = m_destinationFormat.mChannelsPerFrame;
440         fillBufferList.mBuffers[0].mDataByteSize = (UInt32)m_destinationBuffer.capacity();
441         fillBufferList.mBuffers[0].mData = m_destinationBuffer.data();
442
443         UInt32 outputPacketSize = m_destinationFormat.mBytesPerPacket ? m_destinationFormat.mBytesPerPacket : m_maxOutputPacketSize;
444         UInt32 numOutputPackets = (UInt32)m_destinationBuffer.capacity() / outputPacketSize;
445
446         auto error = AudioConverterFillComplexBuffer(m_converter, audioConverterComplexInputDataProc, this, &numOutputPackets, &fillBufferList, m_destinationPacketDescriptions.data());
447         if (error) {
448             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor AudioConverterFillComplexBuffer failed with %d", error);
449             return;
450         }
451
452         if (!numOutputPackets)
453             break;
454
455         auto buffer = sampleBufferWithNumPackets(numOutputPackets, fillBufferList);
456
457         attachPrimingTrimsIfNeeded(buffer);
458
459         error = CMSampleBufferSetOutputPresentationTimeStamp(buffer, m_currentOutputPresentationTimeStamp);
460         if (error) {
461             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMSampleBufferSetOutputPresentationTimeStamp failed with %d", error);
462             return;
463         }
464
465         CMTime nativeDuration = CMSampleBufferGetDuration(buffer);
466         m_currentNativePresentationTimeStamp = CMTimeAdd(m_currentNativePresentationTimeStamp, nativeDuration);
467
468         CMTime outputDuration = CMSampleBufferGetOutputDuration(buffer);
469         m_currentOutputPresentationTimeStamp = CMTimeAdd(m_currentOutputPresentationTimeStamp, outputDuration);
470
471         error = CMBufferQueueEnqueue(m_outputBufferQueue.get(), buffer);
472         if (error) {
473             RELEASE_LOG_ERROR(MediaStream, "AudioSampleBufferCompressor CMBufferQueueEnqueue failed with %d", error);
474             return;
475         }
476     }
477 }
478
479 void AudioSampleBufferCompressor::processSampleBuffer(CMSampleBufferRef buffer)
480 {
481     auto error = CMBufferQueueEnqueue(m_inputBufferQueue.get(), buffer);
482     RELEASE_LOG_ERROR_IF(error, MediaStream, "AudioSampleBufferCompressor CMBufferQueueEnqueue failed with %d", error);
483
484     processSampleBuffersUntilLowWaterTime(m_lowWaterTime);
485 }
486
487 void AudioSampleBufferCompressor::addSampleBuffer(CMSampleBufferRef buffer)
488 {
489     dispatch_sync(m_serialDispatchQueue, ^{
490         if (m_isEncoding)
491             processSampleBuffer(buffer);
492     });
493 }
494
495 CMSampleBufferRef AudioSampleBufferCompressor::getOutputSampleBuffer()
496 {
497     return (CMSampleBufferRef)(const_cast<void*>(CMBufferQueueGetHead(m_outputBufferQueue.get())));
498 }
499
500 RetainPtr<CMSampleBufferRef> AudioSampleBufferCompressor::takeOutputSampleBuffer()
501 {
502     return adoptCF((CMSampleBufferRef)(const_cast<void*>(CMBufferQueueDequeueAndRetain(m_outputBufferQueue.get()))));
503 }
504
505 }
506
507 #endif // ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)