clean-up; temporary solution for waiting until audio assetWriter is ready before appending sample buffers

preferencesAboutTextFull
Craig Watson 9 years ago
parent 2b75543ee3
commit fc554baecd
  1. 18
      src/podcast/quicktime/UBQuickTimeFile.h
  2. 72
      src/podcast/quicktime/UBQuickTimeFile.mm

@ -101,24 +101,12 @@ class UBQuickTimeFile : public QThread
bool beginSession(); bool beginSession();
void setLastErrorMessage(const QString& error); void setLastErrorMessage(const QString& error);
void appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp); void appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp);
bool flushPendingFrames();
volatile bool mShouldStopCompression;
volatile bool mCompressionSessionRunning;
volatile int mPendingFrames;
QString mSpatialQuality;
int mFramesPerSecond;
QSize mFrameSize; QSize mFrameSize;
QString mVideoFileName; QString mVideoFileName;
bool mRecordAudio;
AssetWriterPTR mVideoWriter; AssetWriterPTR mVideoWriter;
AssetWriterInputPTR mVideoWriterInput; AssetWriterInputPTR mVideoWriterInput;
@ -132,6 +120,12 @@ class UBQuickTimeFile : public QThread
CMAudioFormatDescriptionRef mAudioFormatDescription; CMAudioFormatDescriptionRef mAudioFormatDescription;
long mTimeScale; long mTimeScale;
bool mRecordAudio;
volatile bool mShouldStopCompression;
volatile bool mCompressionSessionRunning;
QString mLastErrorMessage; QString mLastErrorMessage;

@ -41,18 +41,15 @@ QQueue<UBQuickTimeFile::VideoFrame> UBQuickTimeFile::frameQueue;
QMutex UBQuickTimeFile::frameQueueMutex; QMutex UBQuickTimeFile::frameQueueMutex;
QWaitCondition UBQuickTimeFile::frameBufferNotEmpty; QWaitCondition UBQuickTimeFile::frameBufferNotEmpty;
UBQuickTimeFile::UBQuickTimeFile(QObject * pParent) UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
: QThread(pParent) : QThread(pParent)
, mVideoWriter(0) , mVideoWriter(0)
, mVideoWriterInput(0) , mVideoWriterInput(0)
, mAdaptor(0) , mAdaptor(0)
, mFramesPerSecond(-1)
, mTimeScale(1000) , mTimeScale(1000)
, mRecordAudio(true) , mRecordAudio(true)
, mShouldStopCompression(false) , mShouldStopCompression(false)
, mCompressionSessionRunning(false) , mCompressionSessionRunning(false)
, mPendingFrames(0)
{ {
} }
@ -67,10 +64,8 @@ bool UBQuickTimeFile::init(const QString& pVideoFileName, const QString& pProfil
, const QSize& pFrameSize, bool pRecordAudio, const QString& audioRecordingDevice) , const QSize& pFrameSize, bool pRecordAudio, const QString& audioRecordingDevice)
{ {
mFrameSize = pFrameSize; mFrameSize = pFrameSize;
mFramesPerSecond = pFramesPerSecond;
mVideoFileName = pVideoFileName; mVideoFileName = pVideoFileName;
mRecordAudio = pRecordAudio; mRecordAudio = pRecordAudio;
mSpatialQuality = pProfileData;
if (mRecordAudio) if (mRecordAudio)
mAudioRecordingDeviceName = audioRecordingDevice; mAudioRecordingDeviceName = audioRecordingDevice;
@ -88,7 +83,6 @@ bool UBQuickTimeFile::init(const QString& pVideoFileName, const QString& pProfil
void UBQuickTimeFile::run() void UBQuickTimeFile::run()
{ {
mShouldStopCompression = false; mShouldStopCompression = false;
mPendingFrames = 0;
if (!beginSession()) if (!beginSession())
return; return;
@ -96,7 +90,8 @@ void UBQuickTimeFile::run()
mCompressionSessionRunning = true; mCompressionSessionRunning = true;
emit compressionSessionStarted(); emit compressionSessionStarted();
do { do {
// Video
frameQueueMutex.lock(); frameQueueMutex.lock();
frameBufferNotEmpty.wait(&UBQuickTimeFile::frameQueueMutex); frameBufferNotEmpty.wait(&UBQuickTimeFile::frameQueueMutex);
@ -118,6 +113,7 @@ void UBQuickTimeFile::run()
} }
else else
frameQueueMutex.unlock(); frameQueueMutex.unlock();
} while(!mShouldStopCompression); } while(!mShouldStopCompression);
endSession(); endSession();
@ -127,7 +123,7 @@ void UBQuickTimeFile::run()
/** /**
* \brief Begin the recording session; initialize the audio/video writer * \brief Begin the recording session; initialize the audio/video writer
* \return true if the session was initialized successfully * \return true if the session was initialized successfully
* *
* This function initializes the AVAssetWriter and associated video and audio inputs. * This function initializes the AVAssetWriter and associated video and audio inputs.
* Video is encoded as H264; audio is encoded as AAC. * Video is encoded as H264; audio is encoded as AAC.
*/ */
@ -141,7 +137,7 @@ bool UBQuickTimeFile::beginSession()
qDebug() << "Podcast video URL invalid; not recording"; qDebug() << "Podcast video URL invalid; not recording";
return false; return false;
} }
// Create and check the assetWriter // Create and check the assetWriter
mVideoWriter = [[AVAssetWriter assetWriterWithURL:outputUrl mVideoWriter = [[AVAssetWriter assetWriterWithURL:outputUrl
fileType:AVFileTypeQuickTimeMovie fileType:AVFileTypeQuickTimeMovie
@ -149,7 +145,7 @@ bool UBQuickTimeFile::beginSession()
NSCParameterAssert(mVideoWriter); NSCParameterAssert(mVideoWriter);
mVideoWriter.movieTimeScale = mTimeScale; mVideoWriter.movieTimeScale = mTimeScale;
// Video // Video
@ -158,7 +154,7 @@ bool UBQuickTimeFile::beginSession()
int frameWidth = mFrameSize.width(); int frameWidth = mFrameSize.width();
int frameHeight = mFrameSize.height(); int frameHeight = mFrameSize.height();
// Create the input and check it // Create the input and check it
NSDictionary * videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: NSDictionary * videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey, AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:frameWidth], AVVideoWidthKey, [NSNumber numberWithInt:frameWidth], AVVideoWidthKey,
@ -167,7 +163,7 @@ bool UBQuickTimeFile::beginSession()
mVideoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo mVideoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoSettings] retain]; outputSettings:videoSettings] retain];
NSCParameterAssert(mVideoWriterInput); NSCParameterAssert(mVideoWriterInput);
@ -193,7 +189,7 @@ bool UBQuickTimeFile::beginSession()
if(mRecordAudio) { if(mRecordAudio) {
mWaveRecorder = new UBAudioQueueRecorder(); mWaveRecorder = new UBAudioQueueRecorder();
// Get the audio format description from mWaveRecorder // Get the audio format description from mWaveRecorder
CMAudioFormatDescriptionCreate(kCFAllocatorDefault, mWaveRecorder->audioFormat(), CMAudioFormatDescriptionCreate(kCFAllocatorDefault, mWaveRecorder->audioFormat(),
0, NULL, 0, NULL, NULL, 0, NULL, 0, NULL, NULL,
@ -213,16 +209,16 @@ bool UBQuickTimeFile::beginSession()
} }
// Audio is mono, and compressed to AAC at 128kbps // Audio is mono, and compressed to AAC at 128kbps
AudioChannelLayout audioChannelLayout = { AudioChannelLayout audioChannelLayout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_Mono, .mChannelLayoutTag = kAudioChannelLayoutTag_Mono,
.mChannelBitmap = 0, .mChannelBitmap = 0,
.mNumberChannelDescriptions = 0 .mNumberChannelDescriptions = 0
}; };
NSData *channelLayoutAsData = [NSData dataWithBytes:&audioChannelLayout NSData *channelLayoutAsData = [NSData dataWithBytes:&audioChannelLayout
length:offsetof(AudioChannelLayout, mChannelDescriptions)]; length:offsetof(AudioChannelLayout, mChannelDescriptions)];
NSDictionary * compressionAudioSettings = @{ NSDictionary * compressionAudioSettings = @{
AVFormatIDKey : [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC], AVFormatIDKey : [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC],
AVEncoderBitRateKey : [NSNumber numberWithInteger:128000], AVEncoderBitRateKey : [NSNumber numberWithInteger:128000],
@ -231,12 +227,12 @@ bool UBQuickTimeFile::beginSession()
AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:1] AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:1]
}; };
mAudioWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio mAudioWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:compressionAudioSettings] retain]; outputSettings:compressionAudioSettings] retain];
NSCParameterAssert([mVideoWriter canAddInput:mAudioWriterInput]); NSCParameterAssert([mVideoWriter canAddInput:mAudioWriterInput]);
[mVideoWriter addInput:mAudioWriterInput]; [mVideoWriter addInput:mAudioWriterInput];
qDebug() << "audio writer input created and added"; qDebug() << "audio writer input created and added";
} }
@ -247,7 +243,7 @@ bool UBQuickTimeFile::beginSession()
mStartTime = CFAbsoluteTimeGetCurrent(); // used for audio timestamp calculation mStartTime = CFAbsoluteTimeGetCurrent(); // used for audio timestamp calculation
return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting; return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting;
} }
@ -257,7 +253,7 @@ bool UBQuickTimeFile::beginSession()
void UBQuickTimeFile::endSession() void UBQuickTimeFile::endSession()
{ {
[mVideoWriterInput markAsFinished]; [mVideoWriterInput markAsFinished];
bool success = [mVideoWriter finishWriting]; [mVideoWriter finishWritingWithCompletionHandler:^{}];
[mAdaptor release]; [mAdaptor release];
[mVideoWriterInput release]; [mVideoWriterInput release];
@ -309,12 +305,12 @@ CVPixelBufferRef UBQuickTimeFile::newPixelBuffer()
*/ */
void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp) void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp)
{ {
CMTime t = CMTimeMake((msTimeStamp * mTimeScale / 1000.0), mTimeScale); CMTime t = CMTimeMake((msTimeStamp * mTimeScale / 1000.0), mTimeScale);
bool added = [mAdaptor appendPixelBuffer: pixelBuffer bool added = [mAdaptor appendPixelBuffer: pixelBuffer
withPresentationTime: t]; withPresentationTime: t];
if (!added) if (!added)
setLastErrorMessage(QString("Could not encode frame at time %1").arg(msTimeStamp)); setLastErrorMessage(QString("Could not encode frame at time %1").arg(msTimeStamp));
@ -333,10 +329,10 @@ void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTime
* (implemented in the UBAudioQueueRecorder class) and the recording, handled * (implemented in the UBAudioQueueRecorder class) and the recording, handled
* by the AVAssetWriterInput instance mAudioWriterInput. * by the AVAssetWriterInput instance mAudioWriterInput.
*/ */
void UBQuickTimeFile::appendAudioBuffer(void* pBuffer, void UBQuickTimeFile::appendAudioBuffer(void* pBuffer,
long pLength) long pLength)
{ {
if(!mRecordAudio) if(!mRecordAudio)
return; return;
@ -368,24 +364,34 @@ void UBQuickTimeFile::appendAudioBuffer(void* pBuffer,
true, true,
NULL, NULL,
NULL, NULL,
mAudioFormatDescription, mAudioFormatDescription,
nSamples, nSamples,
timeStamp, timeStamp,
NULL, NULL,
&sampleBuffer); &sampleBuffer);
// Add the audio sample to the asset writer input // Wait until the AssetWriterInput is ready, but no more than 100ms
if ([mAudioWriterInput isReadyForMoreMediaData]) // (bit of a duct tape solution; cleaner solution would be to use a QQueue,
// similar to the VideoWriter)
int waitTime = 0;
while(![mAudioWriterInput isReadyForMoreMediaData] && waitTime < 100) {
waitTime += 10;
usleep(10000);
}
if ([mAudioWriterInput isReadyForMoreMediaData]) {
if(![mAudioWriterInput appendSampleBuffer:sampleBuffer]) if(![mAudioWriterInput appendSampleBuffer:sampleBuffer])
setLastErrorMessage(QString("Failed to append sample buffer to audio input")); setLastErrorMessage(QString("Failed to append sample buffer to audio input"));
}
else else
setLastErrorMessage(QString("Audio Writer not ready; sample dropped")); setLastErrorMessage(QString("AudioWriterInput not ready. Buffer dropped."));
CFRelease(sampleBuffer); CFRelease(sampleBuffer);
CFRelease(blockBuffer); CFRelease(blockBuffer);
// The audioQueueBuffers are all freed when UBAudioQueueRecorder::close() is called // The audioQueueBuffers are all freed when UBAudioQueueRecorder::close() is called
} }

Loading…
Cancel
Save