Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
OpenBoard
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lifo
Nicolas Ollinger
OpenBoard
Commits
e3d81650
Commit
e3d81650
authored
Nov 26, 2015
by
agriche
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' of
https://github.com/DIP-SEM/OpenBoard
into dev
parents
2c83c5e4
b40f52c4
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
218 additions
and
55 deletions
+218
-55
UBAudioQueueRecorder.cpp
src/podcast/quicktime/UBAudioQueueRecorder.cpp
+25
-16
UBAudioQueueRecorder.h
src/podcast/quicktime/UBAudioQueueRecorder.h
+14
-8
UBQuickTimeFile.h
src/podcast/quicktime/UBQuickTimeFile.h
+25
-14
UBQuickTimeFile.mm
src/podcast/quicktime/UBQuickTimeFile.mm
+154
-17
No files found.
src/podcast/quicktime/UBAudioQueueRecorder.cpp
View file @
e3d81650
...
@@ -37,15 +37,22 @@ UBAudioQueueRecorder::UBAudioQueueRecorder(QObject* pParent)
...
@@ -37,15 +37,22 @@ UBAudioQueueRecorder::UBAudioQueueRecorder(QObject* pParent)
,
mIsRecording
(
false
)
,
mIsRecording
(
false
)
,
mBufferLengthInMs
(
500
)
,
mBufferLengthInMs
(
500
)
{
{
int
sampleSize
=
sizeof
(
float
);
// TODO: check if this is system/ microphone-dependant
sAudioFormat
.
mSampleRate
=
44100.0
;
sAudioFormat
.
mSampleRate
=
44100.0
;
sAudioFormat
.
mFormatID
=
kAudioFormatMPEG4AAC
;
sAudioFormat
.
mFormatID
=
kAudioFormatLinearPCM
;
sAudioFormat
.
mChannelsPerFrame
=
2
;
sAudioFormat
.
mChannelsPerFrame
=
1
;
sAudioFormat
.
mBytesPerFrame
=
0
;
sAudioFormat
.
mBytesPerFrame
=
sampleSize
;
sAudioFormat
.
mBitsPerChannel
=
0
;
sAudioFormat
.
mBitsPerChannel
=
8
*
sampleSize
;
sAudioFormat
.
mBytesPerPacket
=
0
;
sAudioFormat
.
mBytesPerPacket
=
sampleSize
;
sAudioFormat
.
mFramesPerPacket
=
0
;
sAudioFormat
.
mFramesPerPacket
=
1
;
sAudioFormat
.
mFormatFlags
=
0
;
sAudioFormat
.
mFormatFlags
=
kAudioFormatFlagIsFloat
|
kAudioFormatFlagsNativeEndian
|
kAudioFormatFlagIsPacked
;
}
}
...
@@ -250,7 +257,7 @@ bool UBAudioQueueRecorder::init(const QString& waveInDeviceName)
...
@@ -250,7 +257,7 @@ bool UBAudioQueueRecorder::init(const QString& waveInDeviceName)
int
nbBuffers
=
6
;
int
nbBuffers
=
6
;
mSampleBufferSize
=
sAudioFormat
.
mSampleRate
*
sAudioFormat
.
mChannelsPerFrame
mSampleBufferSize
=
sAudioFormat
.
mSampleRate
*
sAudioFormat
.
mChannelsPerFrame
*
2
*
mBufferLengthInMs
/
1000
;
// 44.1 Khz * stereo * 16bit * buffer length
*
sAudioFormat
.
mChannelsPerFrame
*
mBufferLengthInMs
/
1000
;
// 44.1 Khz * stereo * 16bit * buffer length
for
(
int
i
=
0
;
i
<
nbBuffers
;
i
++
)
for
(
int
i
=
0
;
i
<
nbBuffers
;
i
++
)
{
{
...
@@ -333,9 +340,12 @@ bool UBAudioQueueRecorder::close()
...
@@ -333,9 +340,12 @@ bool UBAudioQueueRecorder::close()
}
}
void
UBAudioQueueRecorder
::
audioQueueInputCallback
(
void
*
inUserData
,
AudioQueueRef
inAQ
,
void
UBAudioQueueRecorder
::
audioQueueInputCallback
(
void
*
inUserData
,
AudioQueueBufferRef
inBuffer
,
const
AudioTimeStamp
*
inStartTime
,
AudioQueueRef
inAQ
,
UInt32
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
)
AudioQueueBufferRef
inBuffer
,
const
AudioTimeStamp
*
inStartTime
,
UInt32
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
)
{
{
Q_UNUSED
(
inAQ
);
Q_UNUSED
(
inAQ
);
Q_UNUSED
(
inStartTime
)
Q_UNUSED
(
inStartTime
)
...
@@ -356,12 +366,11 @@ void UBAudioQueueRecorder::audioQueueInputCallback (void *inUserData, AudioQueue
...
@@ -356,12 +366,11 @@ void UBAudioQueueRecorder::audioQueueInputCallback (void *inUserData, AudioQueue
void
UBAudioQueueRecorder
::
emitNewWaveBuffer
(
AudioQueueBufferRef
pBuffer
,
void
UBAudioQueueRecorder
::
emitNewWaveBuffer
(
AudioQueueBufferRef
pBuffer
,
int
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
)
int
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
)
{
{
AudioStreamPacketDescription
*
tmpPackages
=
(
AudioStreamPacketDescription
*
)
malloc
(
inNumberPacketDescriptions
*
sizeof
(
AudioStreamPacketDescription
));
memcpy
(
tmpPackages
,
inPacketDescs
,
inNumberPacketDescriptions
*
sizeof
(
AudioStreamPacketDescription
));
emit
newWaveBuffer
(
pBuffer
->
mAudioData
,
pBuffer
->
mAudioDataByteSize
,
inNumberPacketDescriptions
,
tmpPackages
);
emit
newWaveBuffer
(
pBuffer
->
mAudioData
,
pBuffer
->
mAudioDataByteSize
);
qreal
level
=
0
;
qreal
level
=
0
;
UInt32
size
;
UInt32
size
;
...
...
src/podcast/quicktime/UBAudioQueueRecorder.h
View file @
e3d81650
...
@@ -57,23 +57,29 @@ class UBAudioQueueRecorder : public QObject
...
@@ -57,23 +57,29 @@ class UBAudioQueueRecorder : public QObject
return
mLastErrorMessage
;
return
mLastErrorMessage
;
}
}
static
AudioStreamBasicDescription
audioFormat
()
AudioStreamBasicDescription
*
audioFormat
()
{
{
return
sAudioFormat
;
return
&
sAudioFormat
;
}
}
signals
:
signals
:
void
newWaveBuffer
(
void
*
pBuffer
,
long
pLength
,
int
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
);
void
newWaveBuffer
(
void
*
pBuffer
,
long
pLength
);
void
audioLevelChanged
(
quint8
level
);
void
audioLevelChanged
(
quint8
level
);
private
:
private
:
static
void
audioQueueInputCallback
(
void
*
inUserData
,
AudioQueueRef
inAQ
,
static
void
audioQueueInputCallback
(
void
*
inUserData
,
AudioQueueBufferRef
inBuffer
,
const
AudioTimeStamp
*
inStartTime
,
AudioQueueRef
inAQ
,
UInt32
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
);
AudioQueueBufferRef
inBuffer
,
const
AudioTimeStamp
*
inStartTime
,
void
emitNewWaveBuffer
(
AudioQueueBufferRef
pBuffer
,
int
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
);
UInt32
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
);
void
emitNewWaveBuffer
(
AudioQueueBufferRef
pBuffer
,
int
inNumberPacketDescriptions
,
const
AudioStreamPacketDescription
*
inPacketDescs
);
void
emitAudioLevelChanged
(
quint8
level
);
void
emitAudioLevelChanged
(
quint8
level
);
...
...
src/podcast/quicktime/UBQuickTimeFile.h
View file @
e3d81650
...
@@ -31,6 +31,7 @@
...
@@ -31,6 +31,7 @@
#include <QtCore>
#include <QtCore>
#include <CoreVideo/CoreVideo.h>
#include <CoreVideo/CoreVideo.h>
#include <CoreMedia/CoreMedia.h>
#include "UBAudioQueueRecorder.h"
#include "UBAudioQueueRecorder.h"
...
@@ -92,40 +93,50 @@ class UBQuickTimeFile : public QThread
...
@@ -92,40 +93,50 @@ class UBQuickTimeFile : public QThread
protected
:
protected
:
void
run
();
void
run
();
private
slots
:
void
appendAudioBuffer
(
void
*
pBuffer
,
long
pLength
);
private
:
private
:
bool
beginSession
();
bool
beginSession
();
void
appendVideoFrame
(
CVPixelBufferRef
pixelBuffer
,
long
msTimeStamp
);
void
setLastErrorMessage
(
const
QString
&
error
);
void
setLastErrorMessage
(
const
QString
&
error
);
void
appendVideoFrame
(
CVPixelBufferRef
pixelBuffer
,
long
msTimeStamp
);
bool
flushPendingFrames
();
bool
flushPendingFrames
();
volatile
CVPixelBufferPoolRef
mCVPixelBufferPool
;
volatile
bool
mShouldStopCompression
;
volatile
bool
mCompressionSessionRunning
;
volatile
int
mPendingFrames
;
QString
mSpatialQuality
;
int
mFramesPerSecond
;
int
mFramesPerSecond
;
QSize
mFrameSize
;
QSize
mFrameSize
;
QString
mVideoFileName
;
QString
mVideoFileName
;
long
mTimeScale
;
bool
mRecordAudio
;
bool
mRecordAudio
;
QString
mLastErrorMessage
;
QString
mSpatialQuality
;
AssetWriterPTR
mVideoWriter
;
volatile
bool
mShouldStopCompression
;
AssetWriterInputPTR
mVideoWriterInput
;
volatile
bool
mCompressionSessionRunning
;
AssetWriterInputAdaptorPTR
mAdaptor
;
AssetWriterInputPTR
mAudioWriterInput
;
QPointer
<
UBAudioQueueRecorder
>
mWaveRecorder
;
CFAbsoluteTime
mStartTime
;
CMAudioFormatDescriptionRef
mAudioFormatDescription
;
long
mTimeScale
;
QString
mLastErrorMessage
;
QString
mAudioRecordingDeviceName
;
QString
mAudioRecordingDeviceName
;
volatile
int
mPendingFrames
;
AssetWriterPTR
mVideoWriter
;
AssetWriterInputPTR
mVideoWriterInput
;
AssetWriterInputAdaptorPTR
mAdaptor
;
};
};
#endif
/* UBQUICKTIMEFILE_H_ */
#endif
/* UBQUICKTIMEFILE_H_ */
src/podcast/quicktime/UBQuickTimeFile.mm
View file @
e3d81650
...
@@ -47,21 +47,20 @@ UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
...
@@ -47,21 +47,20 @@ UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
, mVideoWriter(0)
, mVideoWriter(0)
, mVideoWriterInput(0)
, mVideoWriterInput(0)
, mAdaptor(0)
, mAdaptor(0)
, mCVPixelBufferPool(0)
, mFramesPerSecond(-1)
, mFramesPerSecond(-1)
, mTimeScale(100)
, mTimeScale(100
0
)
, mRecordAudio(true)
, mRecordAudio(true)
, mShouldStopCompression(false)
, mShouldStopCompression(false)
, mCompressionSessionRunning(false)
, mCompressionSessionRunning(false)
, mPendingFrames(0)
, mPendingFrames(0)
{
{
// NOOP
}
}
UBQuickTimeFile::~UBQuickTimeFile()
UBQuickTimeFile::~UBQuickTimeFile()
{
{
// NOOP
// destruction of mWaveRecorder is handled by endSession()
}
}
bool UBQuickTimeFile::init(const QString& pVideoFileName, const QString& pProfileData, int pFramesPerSecond
bool UBQuickTimeFile::init(const QString& pVideoFileName, const QString& pProfileData, int pFramesPerSecond
...
@@ -126,7 +125,11 @@ void UBQuickTimeFile::run()
...
@@ -126,7 +125,11 @@ void UBQuickTimeFile::run()
}
}
/**
/**
* \brief Initialize the AVAssetWriter, which handles writing the media to file
* \brief Begin the recording session; initialize the audio/video writer
* \return true if the session was initialized successfully
*
* This function initializes the AVAssetWriter and associated video and audio inputs.
* Video is encoded as H264; audio is encoded as AAC.
*/
*/
bool UBQuickTimeFile::beginSession()
bool UBQuickTimeFile::beginSession()
{
{
...
@@ -147,6 +150,11 @@ bool UBQuickTimeFile::beginSession()
...
@@ -147,6 +150,11 @@ bool UBQuickTimeFile::beginSession()
mVideoWriter.movieTimeScale = mTimeScale;
mVideoWriter.movieTimeScale = mTimeScale;
// Video
//
int frameWidth = mFrameSize.width();
int frameWidth = mFrameSize.width();
int frameHeight = mFrameSize.height();
int frameHeight = mFrameSize.height();
...
@@ -175,18 +183,71 @@ bool UBQuickTimeFile::beginSession()
...
@@ -175,18 +183,71 @@ bool UBQuickTimeFile::beginSession()
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:mVideoWriterInput
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:mVideoWriterInput
sourcePixelBufferAttributes:pixelBufSettings] retain];
sourcePixelBufferAttributes:pixelBufSettings] retain];
// Add the input(s) to the assetWriter
NSCParameterAssert([mVideoWriter canAddInput:mVideoWriterInput]);
NSCParameterAssert([mVideoWriter canAddInput:mVideoWriterInput]);
[mVideoWriter addInput:mVideoWriterInput];
[mVideoWriter addInput:mVideoWriterInput];
// begin the writing session
// Audio
//
if(mRecordAudio) {
mWaveRecorder = new UBAudioQueueRecorder();
// Get the audio format description from mWaveRecorder
CMAudioFormatDescriptionCreate(kCFAllocatorDefault, mWaveRecorder->audioFormat(),
0, NULL, 0, NULL, NULL,
&mAudioFormatDescription);
if(mWaveRecorder->init(mAudioRecordingDeviceName)) {
connect(mWaveRecorder, &UBAudioQueueRecorder::newWaveBuffer,
this, &UBQuickTimeFile::appendAudioBuffer);
connect(mWaveRecorder, SIGNAL(audioLevelChanged(quint8)),
this, SIGNAL(audioLevelChanged(quint8)));
}
else {
setLastErrorMessage(mWaveRecorder->lastErrorMessage());
mWaveRecorder->deleteLater();
mRecordAudio = false;
}
// Audio is mono, and compressed to AAC at 128kbps
AudioChannelLayout audioChannelLayout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_Mono,
.mChannelBitmap = 0,
.mNumberChannelDescriptions = 0
};
NSData *channelLayoutAsData = [NSData dataWithBytes:&audioChannelLayout
length:offsetof(AudioChannelLayout, mChannelDescriptions)];
NSDictionary * compressionAudioSettings = @{
AVFormatIDKey : [NSNumber numberWithUnsignedInt:kAudioFormatMPEG4AAC],
AVEncoderBitRateKey : [NSNumber numberWithInteger:128000],
AVSampleRateKey : [NSNumber numberWithInteger:44100],
AVChannelLayoutKey : channelLayoutAsData,
AVNumberOfChannelsKey : [NSNumber numberWithUnsignedInteger:1]
};
mAudioWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:compressionAudioSettings] retain];
NSCParameterAssert([mVideoWriter canAddInput:mAudioWriterInput]);
[mVideoWriter addInput:mAudioWriterInput];
qDebug() << "audio writer input created and added";
}
// Begin the writing session
bool canStartWriting = [mVideoWriter startWriting];
bool canStartWriting = [mVideoWriter startWriting];
[mVideoWriter startSessionAtSourceTime:CMTimeMake(0, mTimeScale)];
[mVideoWriter startSessionAtSourceTime:CMTimeMake(0, mTimeScale)];
// return true if everything was created and started successfully
mStartTime = CFAbsoluteTimeGetCurrent(); // used for audio timestamp calculation
return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting;
return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting;
}
}
...
@@ -201,10 +262,18 @@ void UBQuickTimeFile::endSession()
...
@@ -201,10 +262,18 @@ void UBQuickTimeFile::endSession()
[mAdaptor release];
[mAdaptor release];
[mVideoWriterInput release];
[mVideoWriterInput release];
[mVideoWriter release];
[mVideoWriter release];
[mAudioWriterInput release];
mAdaptor = nil;
mAdaptor = nil;
mVideoWriterInput = nil;
mVideoWriterInput = nil;
mVideoWriter = nil;
mVideoWriter = nil;
mAudioWriterInput = nil;
if (mWaveRecorder) {
mWaveRecorder->close();
mWaveRecorder->deleteLater();
}
}
}
/**
/**
...
@@ -235,11 +304,11 @@ CVPixelBufferRef UBQuickTimeFile::newPixelBuffer()
...
@@ -235,11 +304,11 @@ CVPixelBufferRef UBQuickTimeFile::newPixelBuffer()
/**
/**
* \brief Add a frame to the pixel buffer adaptor
* \brief Add a frame to the pixel buffer adaptor
* \param pixelBuffer The CVPixelBufferRef (video frame) to add to the movie
* \param msTimeStamp Timestamp, in milliseconds, of the frame
*/
*/
void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp)
void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp)
{
{
//qDebug() << "adding video frame at time: " << 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
...
@@ -252,11 +321,79 @@ void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTime
...
@@ -252,11 +321,79 @@ void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTime
CVPixelBufferRelease(pixelBuffer);
CVPixelBufferRelease(pixelBuffer);
}
}
/**
* \brief Append an AudioQueue Buffer to the audio AVAssetWriterInput
* \param pBuffer The AudioQueueBufferRef to add. Must be uncompressed (LPCM).
* \param pLength The length of the buffer, in Bytes
*
* This function serves as an interface between the low-level audio stream
* (implemented in the UBAudioQueueRecorder class) and the recording, handled
* by the AVAssetWriterInput instance mAudioWriterInput.
*/
void UBQuickTimeFile::appendAudioBuffer(void* pBuffer,
long pLength)
{
if(!mRecordAudio)
return;
// CMSampleBuffers require a CMBlockBuffer to hold the media data; we
// create a blockBuffer here from the AudioQueueBuffer's data.
CMBlockBufferRef blockBuffer;
CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
pBuffer,
pLength,
kCFAllocatorNull,
NULL,
0,
pLength,
kCMBlockBufferAssureMemoryNowFlag,
&blockBuffer);
// Timestamp of current sample
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsedTime = currentTime - mStartTime;
CMTime timeStamp = CMTimeMake(elapsedTime * mTimeScale, mTimeScale);
// Number of samples in the buffer
long nSamples = pLength / mWaveRecorder->audioFormat()->mBytesPerFrame;
CMSampleBufferRef sampleBuffer;
CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault,
blockBuffer,
true,
NULL,
NULL,
mAudioFormatDescription,
nSamples,
timeStamp,
NULL,
&sampleBuffer);
// Add the audio sample to the asset writer input
if ([mAudioWriterInput isReadyForMoreMediaData])
if(![mAudioWriterInput appendSampleBuffer:sampleBuffer])
setLastErrorMessage(QString("Failed to append sample buffer to audio input"));
else
setLastErrorMessage(QString("Audio Writer not ready; sample dropped"));
CFRelease(sampleBuffer);
CFRelease(blockBuffer);
// The audioQueueBuffers are all freed when UBAudioQueueRecorder::close() is called
}
/**
* \brief Print an error message to the terminal, and store it
*/
void UBQuickTimeFile::setLastErrorMessage(const QString& error)
void UBQuickTimeFile::setLastErrorMessage(const QString& error)
{
{
mLastErrorMessage = error;
mLastErrorMessage = error;
qWarning() << "UBQuickTimeFile error" << error;
qWarning() << "UBQuickTimeFile error" << error;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment