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
17a08d35
Commit
17a08d35
authored
Mar 23, 2016
by
Craig Watson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Clean-up of OS X podcast recording; fixed frames sometimes carrying over from one video to the next
parent
5a51dbd9
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
79 additions
and
66 deletions
+79
-66
UBQuickTimeFile.h
src/podcast/quicktime/UBQuickTimeFile.h
+40
-33
UBQuickTimeFile.mm
src/podcast/quicktime/UBQuickTimeFile.mm
+32
-20
UBQuickTimeVideoEncoder.cpp
src/podcast/quicktime/UBQuickTimeVideoEncoder.cpp
+7
-13
No files found.
src/podcast/quicktime/UBQuickTimeFile.h
View file @
17a08d35
...
@@ -59,6 +59,14 @@ class UBQuickTimeFile : public QThread
...
@@ -59,6 +59,14 @@ class UBQuickTimeFile : public QThread
Q_OBJECT
;
Q_OBJECT
;
public
:
public
:
struct
VideoFrame
{
CVPixelBufferRef
buffer
;
long
timestamp
;
};
static
QWaitCondition
frameBufferNotEmpty
;
UBQuickTimeFile
(
QObject
*
pParent
=
0
);
UBQuickTimeFile
(
QObject
*
pParent
=
0
);
virtual
~
UBQuickTimeFile
();
virtual
~
UBQuickTimeFile
();
...
@@ -69,23 +77,11 @@ class UBQuickTimeFile : public QThread
...
@@ -69,23 +77,11 @@ class UBQuickTimeFile : public QThread
void
stop
();
void
stop
();
CVPixelBufferRef
newPixelBuffer
();
CVPixelBufferRef
newPixelBuffer
();
void
enqueueVideoFrame
(
VideoFrame
frame
);
bool
isCompressionSessionRunning
()
{
return
mCompressionSessionRunning
;
}
bool
isCompressionSessionRunning
()
{
return
mCompressionSessionRunning
;
}
QString
lastErrorMessage
()
const
{
return
mLastErrorMessage
;
}
QString
lastErrorMessage
()
const
{
return
mLastErrorMessage
;
}
void
endSession
();
struct
VideoFrame
{
CVPixelBufferRef
buffer
;
long
timestamp
;
};
static
QQueue
<
VideoFrame
>
frameQueue
;
static
QMutex
frameQueueMutex
;
static
QWaitCondition
frameBufferNotEmpty
;
signals
:
signals
:
void
audioLevelChanged
(
quint8
level
);
void
audioLevelChanged
(
quint8
level
);
void
compressionSessionStarted
();
void
compressionSessionStarted
();
...
@@ -98,44 +94,55 @@ class UBQuickTimeFile : public QThread
...
@@ -98,44 +94,55 @@ class UBQuickTimeFile : public QThread
void
enqueueAudioBuffer
(
void
*
pBuffer
,
long
pLength
);
void
enqueueAudioBuffer
(
void
*
pBuffer
,
long
pLength
);
private
:
private
:
QString
mLastErrorMessage
;
bool
beginSession
();
// Format information
void
setLastErrorMessage
(
const
QString
&
error
);
void
appendVideoFrame
(
CVPixelBufferRef
pixelBuffer
,
long
msTimeStamp
);
bool
appendSampleBuffer
(
CMSampleBufferRef
sampleBuffer
);
QSize
mFrameSize
;
QString
mVideoFileName
;
QString
mVideoFileName
;
QSize
mFrameSize
;
long
mTimeScale
;
bool
mRecordAudio
;
QString
mAudioRecordingDeviceName
;
// Video/audio encoders and associated objects
AssetWriterPTR
mVideoWriter
;
AssetWriterPTR
mVideoWriter
;
AssetWriterInputPTR
mVideoWriterInput
;
AssetWriterInputPTR
mVideoWriterInput
;
AssetWriterInputAdaptorPTR
mAdaptor
;
AssetWriterInputAdaptorPTR
mAdaptor
;
AssetWriterInputPTR
mAudioWriterInput
;
AssetWriterInputPTR
mAudioWriterInput
;
// Audio recorder
QPointer
<
UBAudioQueueRecorder
>
mWaveRecorder
;
QPointer
<
UBAudioQueueRecorder
>
mWaveRecorder
;
CMAudioFormatDescriptionRef
mAudioFormatDescription
;
// Variables used during encoding
CFAbsoluteTime
mStartTime
;
CFAbsoluteTime
mStartTime
;
CMTime
mLastFrameTimestamp
;
CMAudioFormatDescriptionRef
mAudioFormatDescription
;
long
mTimeScale
;
bool
mRecordAudio
;
volatile
bool
mShouldStopCompression
;
volatile
bool
mShouldStopCompression
;
volatile
bool
mCompressionSessionRunning
;
volatile
bool
mCompressionSessionRunning
;
QString
mLastErrorMessage
;
// Dispatch queues to handle passing data to the A/V encoders
QString
mAudioRecordingDeviceName
;
dispatch_queue_t
mVideoDispatchQueue
;
dispatch_queue_t
mVideoDispatchQueue
;
dispatch_queue_t
mAudioDispatchQueue
;
dispatch_queue_t
mAudioDispatchQueue
;
static
QQueue
<
CMSampleBufferRef
>
audioQueue
;
// Queues for frames and audio buffers to be encoded
static
QMutex
audioQueueMutex
;
QQueue
<
VideoFrame
>
frameQueue
;
QQueue
<
CMSampleBufferRef
>
audioQueue
;
QMutex
frameQueueMutex
;
QMutex
audioQueueMutex
;
QMutex
audioWriterMutex
;
// Private functions
void
setLastErrorMessage
(
const
QString
&
error
);
bool
beginSession
();
void
endSession
();
void
appendFrameToVideo
(
CVPixelBufferRef
pixelBuffer
,
long
msTimeStamp
);
bool
appendSampleBuffer
(
CMSampleBufferRef
sampleBuffer
);
static
QMutex
audioWriterMutex
;
};
};
#endif
/* UBQUICKTIMEFILE_H_ */
#endif
/* UBQUICKTIMEFILE_H_ */
src/podcast/quicktime/UBQuickTimeFile.mm
View file @
17a08d35
...
@@ -37,15 +37,8 @@
...
@@ -37,15 +37,8 @@
#include "core/memcheck.h"
#include "core/memcheck.h"
QQueue<UBQuickTimeFile::VideoFrame> UBQuickTimeFile::frameQueue;
QMutex UBQuickTimeFile::frameQueueMutex;
QWaitCondition UBQuickTimeFile::frameBufferNotEmpty;
QWaitCondition UBQuickTimeFile::frameBufferNotEmpty;
QQueue<CMSampleBufferRef> UBQuickTimeFile::audioQueue;
QMutex UBQuickTimeFile::audioQueueMutex;
QMutex UBQuickTimeFile::audioWriterMutex;
UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
: QThread(pParent)
: QThread(pParent)
, mVideoWriter(0)
, mVideoWriter(0)
...
@@ -65,13 +58,14 @@ UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
...
@@ -65,13 +58,14 @@ UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
UBQuickTimeFile::~UBQuickTimeFile()
UBQuickTimeFile::~UBQuickTimeFile()
{
{
// 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
, const QSize& pFrameSize, bool pRecordAudio, const QString& audioRecordingDevice)
, const QSize& pFrameSize, bool pRecordAudio, const QString& audioRecordingDevice)
{
{
Q_UNUSED(pProfileData);
Q_UNUSED(pFramesPerSecond);
mFrameSize = pFrameSize;
mFrameSize = pFrameSize;
mVideoFileName = pVideoFileName;
mVideoFileName = pVideoFileName;
mRecordAudio = pRecordAudio;
mRecordAudio = pRecordAudio;
...
@@ -109,8 +103,9 @@ void UBQuickTimeFile::run()
...
@@ -109,8 +103,9 @@ void UBQuickTimeFile::run()
!frameQueue.isEmpty() &&
!frameQueue.isEmpty() &&
[mVideoWriterInput isReadyForMoreMediaData])
[mVideoWriterInput isReadyForMoreMediaData])
{
{
// in this case the last few frames may be dropped if the queue isn't empty...
VideoFrame frame = frameQueue.dequeue();
VideoFrame frame = frameQueue.dequeue();
append
VideoFrame
(frame.buffer, frame.timestamp);
append
FrameToVideo
(frame.buffer, frame.timestamp);
}
}
frameQueueMutex.unlock();
frameQueueMutex.unlock();
...
@@ -174,7 +169,6 @@ bool UBQuickTimeFile::beginSession()
...
@@ -174,7 +169,6 @@ bool UBQuickTimeFile::beginSession()
[NSNumber numberWithInt:frameHeight], AVVideoHeightKey,
[NSNumber numberWithInt:frameHeight], AVVideoHeightKey,
nil];
nil];
mVideoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
mVideoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoSettings] retain];
outputSettings:videoSettings] retain];
NSCParameterAssert(mVideoWriterInput);
NSCParameterAssert(mVideoWriterInput);
...
@@ -253,7 +247,7 @@ bool UBQuickTimeFile::beginSession()
...
@@ -253,7 +247,7 @@ bool UBQuickTimeFile::beginSession()
[mVideoWriter startSessionAtSourceTime:CMTimeMake(0, mTimeScale)];
[mVideoWriter startSessionAtSourceTime:CMTimeMake(0, mTimeScale)];
mStartTime = CFAbsoluteTimeGetCurrent(); // used for audio timestamp calculation
mStartTime = CFAbsoluteTimeGetCurrent(); // used for audio timestamp calculation
mLastFrameTimestamp = CMTimeMake(0, mTimeScale);
return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting;
return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting;
}
}
...
@@ -267,6 +261,8 @@ void UBQuickTimeFile::endSession()
...
@@ -267,6 +261,8 @@ void UBQuickTimeFile::endSession()
[mVideoWriterInput markAsFinished];
[mVideoWriterInput markAsFinished];
if (mAudioWriterInput != 0)
[mAudioWriterInput markAsFinished];
[mVideoWriter finishWritingWithCompletionHandler:^{
[mVideoWriter finishWritingWithCompletionHandler:^{
[mAdaptor release];
[mAdaptor release];
...
@@ -319,32 +315,48 @@ CVPixelBufferRef UBQuickTimeFile::newPixelBuffer()
...
@@ -319,32 +315,48 @@ CVPixelBufferRef UBQuickTimeFile::newPixelBuffer()
{
{
CVPixelBufferRef pixelBuffer = 0;
CVPixelBufferRef pixelBuffer = 0;
if(CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, mAdaptor.pixelBufferPool, &pixelBuffer) != kCVReturnSuccess)
CVReturn result = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, mAdaptor.pixelBufferPool, &pixelBuffer);
{
setLastErrorMessage("Could not retrieve CV buffer from pool");
if (result != kCVReturnSuccess) {
setLastErrorMessage("Could not retrieve CV buffer from pool (error " + QString::number(result) + ")");
return 0;
return 0;
}
}
return pixelBuffer;
return pixelBuffer;
}
}
void UBQuickTimeFile::enqueueVideoFrame(VideoFrame frame)
{
frameQueueMutex.lock();
frameQueue.enqueue(frame);
frameQueueMutex.unlock();
}
/**
/**
* \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 pixelBuffer The CVPixelBufferRef (video frame) to add to the movie
* \param msTimeStamp Timestamp, in milliseconds, of the frame
* \param msTimeStamp Timestamp, in milliseconds, of the frame
*/
*/
void UBQuickTimeFile::append
VideoFrame
(CVPixelBufferRef pixelBuffer, long msTimeStamp)
void UBQuickTimeFile::append
FrameToVideo
(CVPixelBufferRef pixelBuffer, long msTimeStamp)
{
{
//qDebug() << "appending video frame";
//qDebug() << "appending video frame";
CMTime t = CMTimeMake((msTimeStamp * mTimeScale / 1000.0), mTimeScale);
CMTime t = CMTimeMake((msTimeStamp * mTimeScale / 1000.0), mTimeScale);
bool added = [mAdaptor appendPixelBuffer: pixelBuffer
// The timestamp must be both valid and larger than the previous frame's timestamp
withPresentationTime: t];
if (CMTIME_IS_VALID(t) && CMTimeCompare(t, mLastFrameTimestamp) == 1) {
if (!added)
bool added = [mAdaptor appendPixelBuffer: pixelBuffer
setLastErrorMessage(QString("Could not encode frame at time %1").arg(msTimeStamp));
withPresentationTime: t];
if (!added)
setLastErrorMessage(QString("Could not encode frame at time %1").arg(msTimeStamp));
mLastFrameTimestamp = t;
}
else {
qDebug() << "Frame dropped; timestamp was smaller or equal to previous frame's timestamp of: "
<< mLastFrameTimestamp.value << "/" << mLastFrameTimestamp.timescale;
}
CVPixelBufferRelease(pixelBuffer);
CVPixelBufferRelease(pixelBuffer);
}
}
...
...
src/podcast/quicktime/UBQuickTimeVideoEncoder.cpp
View file @
17a08d35
...
@@ -97,15 +97,12 @@ void UBQuickTimeVideoEncoder::compressionFinished()
...
@@ -97,15 +97,12 @@ void UBQuickTimeVideoEncoder::compressionFinished()
void
UBQuickTimeVideoEncoder
::
newPixmap
(
const
QImage
&
pImage
,
long
timestamp
)
void
UBQuickTimeVideoEncoder
::
newPixmap
(
const
QImage
&
pImage
,
long
timestamp
)
{
{
//qDebug() << "New Frame at ms" << timestamp;
//qDebug() << "New Frame at ms" << timestamp;
if
(
mQuickTimeCompressionSession
.
isCompressionSessionRunning
())
if
(
mQuickTimeCompressionSession
.
isCompressionSessionRunning
())
{
{
if
(
mPendingImageFrames
.
length
()
>
0
)
{
if
(
mPendingImageFrames
.
length
()
>
0
)
foreach
(
ImageFrame
frame
,
mPendingImageFrames
)
{
{
encodeFrame
(
frame
.
image
,
frame
.
timestamp
);
foreach
(
ImageFrame
frame
,
mPendingImageFrames
)
{
encodeFrame
(
frame
.
image
,
frame
.
timestamp
);
}
}
mPendingImageFrames
.
clear
();
mPendingImageFrames
.
clear
();
...
@@ -115,8 +112,7 @@ void UBQuickTimeVideoEncoder::newPixmap(const QImage& pImage, long timestamp)
...
@@ -115,8 +112,7 @@ void UBQuickTimeVideoEncoder::newPixmap(const QImage& pImage, long timestamp)
UBQuickTimeFile
::
frameBufferNotEmpty
.
wakeAll
();
UBQuickTimeFile
::
frameBufferNotEmpty
.
wakeAll
();
}
}
else
else
{
{
qDebug
()
<<
"queuing frame, compressor not ready"
;
qDebug
()
<<
"queuing frame, compressor not ready"
;
ImageFrame
frame
;
ImageFrame
frame
;
...
@@ -172,9 +168,7 @@ void UBQuickTimeVideoEncoder::encodeFrame(const QImage& pImage, long timestamp)
...
@@ -172,9 +168,7 @@ void UBQuickTimeVideoEncoder::encodeFrame(const QImage& pImage, long timestamp)
videoFrame
.
buffer
=
pixelBuffer
;
videoFrame
.
buffer
=
pixelBuffer
;
videoFrame
.
timestamp
=
timestamp
;
videoFrame
.
timestamp
=
timestamp
;
UBQuickTimeFile
::
frameQueueMutex
.
lock
();
mQuickTimeCompressionSession
.
enqueueVideoFrame
(
videoFrame
);
UBQuickTimeFile
::
frameQueue
.
enqueue
(
videoFrame
);
UBQuickTimeFile
::
frameQueueMutex
.
unlock
();
}
}
...
...
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