Commit ab4ae943 authored by agriche's avatar agriche

Merge branch 'dev' of https://github.com/DIP-SEM/OpenBoard into dev

parents 7ad77bb4 642d1546
......@@ -130,13 +130,13 @@ macx {
LIBS += -framework Foundation
LIBS += -framework Cocoa
LIBS += -framework Carbon
LIBS += -framework AVFoundation
LIBS += -framework CoreMedia
LIBS += -lcrypto
CONFIG(release, debug|release):CONFIG += x86_64
CONFIG(debug, debug|release):CONFIG += x86_64
# TODO Craig: switch to 64bit
QMAKE_MAC_SDK = macosx
QMAKE_MACOSX_DEPLOYMENT_TARGET = "10.10"
......
......@@ -59,9 +59,9 @@
#ifdef Q_OS_WIN
#include "windowsmedia/UBWindowsMediaVideoEncoder.h"
#include "windowsmedia/UBWaveRecorder.h"
//#elif defined(Q_OS_OSX)
// #include "quicktime/UBQuickTimeVideoEncoder.h"
// #include "quicktime/UBAudioQueueRecorder.h"
#elif defined(Q_OS_OSX)
#include "quicktime/UBQuickTimeVideoEncoder.h"
#include "quicktime/UBAudioQueueRecorder.h"
#endif
#include "core/memcheck.h"
......@@ -305,8 +305,8 @@ void UBPodcastController::start()
#ifdef Q_OS_WIN
mVideoEncoder = new UBWindowsMediaVideoEncoder(this); //deleted on stop
//#elif defined(Q_OS_OSX)
// mVideoEncoder = new UBQuickTimeVideoEncoder(this); //deleted on stop
#elif defined(Q_OS_OSX)
mVideoEncoder = new UBQuickTimeVideoEncoder(this); //deleted on stop
#endif
if (mVideoEncoder)
......@@ -795,8 +795,8 @@ QStringList UBPodcastController::audioRecordingDevices()
#ifdef Q_OS_WIN
devices = UBWaveRecorder::waveInDevices();
//#elif defined(Q_OS_OSX)
// devices = UBAudioQueueRecorder::waveInDevices();
#elif defined(Q_OS_OSX)
devices = UBAudioQueueRecorder::waveInDevices();
#endif
return devices;
......
HEADERS += src/podcast/UBPodcastController.h \
src/podcast/UBAbstractVideoEncoder.h \
src/podcast/UBPodcastRecordingPalette.h \
src/podcast/youtube/UBYouTubePublisher.h \
src/podcast/intranet/UBIntranetPodcastPublisher.h
SOURCES += src/podcast/UBPodcastController.cpp \
src/podcast/UBAbstractVideoEncoder.cpp \
src/podcast/UBPodcastRecordingPalette.cpp \
src/podcast/youtube/UBYouTubePublisher.cpp \
src/podcast/intranet/UBIntranetPodcastPublisher.cpp
win32 {
SOURCES += src/podcast/windowsmedia/UBWindowsMediaVideoEncoder.cpp \
src/podcast/windowsmedia/UBWindowsMediaFile.cpp \
src/podcast/windowsmedia/UBWaveRecorder.cpp
HEADERS += src/podcast/windowsmedia/UBWindowsMediaVideoEncoder.h \
src/podcast/windowsmedia/UBWindowsMediaFile.h \
src/podcast/windowsmedia/UBWaveRecorder.h
}
#macx {
# SOURCES += src/podcast/quicktime/UBQuickTimeVideoEncoder.cpp \
# src/podcast/quicktime/UBQuickTimeFile.cpp \
# src/podcast/quicktime/UBAudioQueueRecorder.cpp
# HEADERS += src/podcast/quicktime/UBQuickTimeVideoEncoder.h \
# src/podcast/quicktime/UBQuickTimeFile.h \
# src/podcast/quicktime/UBAudioQueueRecorder.h
#}
HEADERS += src/podcast/UBPodcastController.h \
src/podcast/UBAbstractVideoEncoder.h \
src/podcast/UBPodcastRecordingPalette.h \
src/podcast/youtube/UBYouTubePublisher.h \
src/podcast/intranet/UBIntranetPodcastPublisher.h
SOURCES += src/podcast/UBPodcastController.cpp \
src/podcast/UBAbstractVideoEncoder.cpp \
src/podcast/UBPodcastRecordingPalette.cpp \
src/podcast/youtube/UBYouTubePublisher.cpp \
src/podcast/intranet/UBIntranetPodcastPublisher.cpp
win32 {
SOURCES += src/podcast/windowsmedia/UBWindowsMediaVideoEncoder.cpp \
src/podcast/windowsmedia/UBWindowsMediaFile.cpp \
src/podcast/windowsmedia/UBWaveRecorder.cpp
HEADERS += src/podcast/windowsmedia/UBWindowsMediaVideoEncoder.h \
src/podcast/windowsmedia/UBWindowsMediaFile.h \
src/podcast/windowsmedia/UBWaveRecorder.h
}
macx {
SOURCES += src/podcast/quicktime/UBQuickTimeVideoEncoder.cpp \
src/podcast/quicktime/UBAudioQueueRecorder.cpp
HEADERS += src/podcast/quicktime/UBQuickTimeVideoEncoder.h \
src/podcast/quicktime/UBQuickTimeFile.h \
src/podcast/quicktime/UBAudioQueueRecorder.h
OBJECTIVE_SOURCES += src/podcast/quicktime/UBQuickTimeFile.mm
}
......@@ -151,10 +151,10 @@ QString UBAudioQueueRecorder::deviceUIDFromDeviceID(AudioDeviceID id)
{
char *cname = new char[1024];
CFStringGetCString (name, cname, 1024, kCFStringEncodingASCII);
CFStringGetCString (name, cname, 1024, kCFStringEncodingISOLatin1);
int length = CFStringGetLength (name);
uid = QString::fromAscii(cname, length);
uid = QString::fromLatin1(cname, length);
delete cname;
......
This diff is collapsed.
......@@ -30,12 +30,29 @@
#include <QtCore>
#include <ApplicationServices/ApplicationServices.h>
#include <QuickTime/QuickTime.h>
#include <AudioToolbox/AudioToolbox.h>
#include <CoreVideo/CoreVideo.h>
#include "UBAudioQueueRecorder.h"
// Trick to get around the fact that the C++ compiler doesn't
// like Objective C code.
#ifdef __OBJC__ // defined by the Objective C compiler
@class AVAssetWriter;
@class AVAssetWriterInput;
@class AVAssetWriterInputPixelBufferAdaptor;
typedef AVAssetWriter* AssetWriterPTR;
typedef AVAssetWriterInput* AssetWriterInputPTR;
typedef AVAssetWriterInputPixelBufferAdaptor* AssetWriterInputAdaptorPTR;
#else
typedef void* AssetWriterPTR;
typedef void* AssetWriterInputPTR;
typedef void* AssetWriterInputAdaptorPTR;
#endif
class UBQuickTimeFile : public QThread
{
Q_OBJECT;
......@@ -52,15 +69,11 @@ class UBQuickTimeFile : public QThread
CVPixelBufferRef newPixelBuffer();
bool isCompressionSessionRunning()
{
return mCompressionSessionRunning;
}
bool isCompressionSessionRunning() { return mCompressionSessionRunning; }
QString lastErrorMessage() const
{
return mLastErrorMessage;
}
QString lastErrorMessage() const { return mLastErrorMessage; }
void endSession();
struct VideoFrame
{
......@@ -79,47 +92,19 @@ class UBQuickTimeFile : public QThread
protected:
void run();
private slots:
void appendAudioBuffer(void* pBuffer, long pLength, int inNumberPacketDescriptions
, const AudioStreamPacketDescription* inPacketDescs);
private:
static OSStatus addEncodedFrameToMovie(void *encodedFrameOutputRefCon,
ICMCompressionSessionRef session,
OSStatus err,
ICMEncodedFrameRef encodedFrame,
void *reserved);
bool beginSession();
void appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp);
void addEncodedFrame(ICMEncodedFrameRef encodedFrame, OSStatus err);
bool createCompressionSession();
bool closeCompressionSession();
bool createMovie();
bool createVideoMedia();
bool createAudioMedia();
void setLastErrorMessage(const QString& error);
bool flushPendingFrames();
ICMCompressionSessionRef mVideoCompressionSession;
Media mVideoMedia;
Media mSoundMedia;
Track mVideoOutputTrack;
Track mSoundOutputTrack;
volatile CVPixelBufferPoolRef mCVPixelBufferPool;
SoundDescriptionHandle mSoundDescription;
Movie mOutputMovie;
DataHandler mOutputMovieDataHandler;
int mFramesPerSecond;
QSize mFrameSize;
......@@ -130,17 +115,17 @@ class UBQuickTimeFile : public QThread
QString mLastErrorMessage;
AudioStreamBasicDescription mAudioDataFormat;
QPointer<UBAudioQueueRecorder> mWaveRecorder;
QString mSpatialQuality;
CodecQ mSpatialQuality;
volatile bool mSouldStopCompression;
volatile bool mShouldStopCompression;
volatile bool mCompressionSessionRunning;
QString mAudioRecordingDeviceName;
volatile int mPendingFrames;
AssetWriterPTR mVideoWriter;
AssetWriterInputPTR mVideoWriterInput;
AssetWriterInputAdaptorPTR mAdaptor;
};
#endif /* UBQUICKTIMEFILE_H_ */
/*
* Copyright (C) 2013 Open Education Foundation
*
* Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
* l'Education Numérique en Afrique (GIP ENA)
*
* This file is part of OpenBoard.
*
* OpenBoard is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License,
* with a specific linking exception for the OpenSSL project's
* "OpenSSL" library (or with modified versions of it that use the
* same license as the "OpenSSL" library).
*
* OpenBoard is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBoard. If not, see <http://www.gnu.org/licenses/>.
*/
#include "UBQuickTimeFile.h"
#include <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
#include "UBAudioQueueRecorder.h"
#include <QtGui>
#include "core/memcheck.h"
QQueue<UBQuickTimeFile::VideoFrame> UBQuickTimeFile::frameQueue;
QMutex UBQuickTimeFile::frameQueueMutex;
QWaitCondition UBQuickTimeFile::frameBufferNotEmpty;
UBQuickTimeFile::UBQuickTimeFile(QObject * pParent)
: QThread(pParent)
, mVideoWriter(0)
, mVideoWriterInput(0)
, mAdaptor(0)
, mCVPixelBufferPool(0)
, mFramesPerSecond(-1)
, mTimeScale(100)
, mRecordAudio(true)
, mShouldStopCompression(false)
, mCompressionSessionRunning(false)
, mPendingFrames(0)
{
// NOOP
}
UBQuickTimeFile::~UBQuickTimeFile()
{
// NOOP
}
bool UBQuickTimeFile::init(const QString& pVideoFileName, const QString& pProfileData, int pFramesPerSecond
, const QSize& pFrameSize, bool pRecordAudio, const QString& audioRecordingDevice)
{
mFrameSize = pFrameSize;
mFramesPerSecond = pFramesPerSecond;
mVideoFileName = pVideoFileName;
mRecordAudio = pRecordAudio;
mSpatialQuality = pProfileData;
if (mRecordAudio)
mAudioRecordingDeviceName = audioRecordingDevice;
else
mAudioRecordingDeviceName = "";
qDebug() << "UBQuickTimeFile created; video size: " << pFrameSize.width() << " x " << pFrameSize.height();
return true;
}
void UBQuickTimeFile::run()
{
mShouldStopCompression = false;
mPendingFrames = 0;
if (!beginSession())
return;
mCompressionSessionRunning = true;
emit compressionSessionStarted();
do {
frameQueueMutex.lock();
frameBufferNotEmpty.wait(&UBQuickTimeFile::frameQueueMutex);
if (!frameQueue.isEmpty()) {
QQueue<VideoFrame> localQueue = frameQueue;
frameQueue.clear();
frameQueueMutex.unlock();
while (!localQueue.isEmpty()) {
if ([mVideoWriterInput isReadyForMoreMediaData]) {
VideoFrame frame = localQueue.dequeue();
appendVideoFrame(frame.buffer, frame.timestamp);
}
else
usleep(10000);
}
}
else
frameQueueMutex.unlock();
} while(!mShouldStopCompression);
endSession();
}
/**
* \brief Initialize the AVAssetWriter, which handles writing the media to file
*/
bool UBQuickTimeFile::beginSession()
{
NSError *outError;
NSString * outputPath = [[NSString alloc] initWithUTF8String: mVideoFileName.toUtf8().data()];
NSURL * outputUrl = [[NSURL alloc] initFileURLWithPath: outputPath];
if (!outputUrl) {
qDebug() << "Podcast video URL invalid; not recording";
return false;
}
// Create and check the assetWriter
mVideoWriter = [[AVAssetWriter assetWriterWithURL:outputUrl
fileType:AVFileTypeQuickTimeMovie
error:&outError] retain];
NSCParameterAssert(mVideoWriter);
mVideoWriter.movieTimeScale = mTimeScale;
int frameWidth = mFrameSize.width();
int frameHeight = mFrameSize.height();
// Create the input and check it
NSDictionary * videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:frameWidth], AVVideoWidthKey,
[NSNumber numberWithInt:frameHeight], AVVideoHeightKey,
nil];
mVideoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoSettings] retain];
NSCParameterAssert(mVideoWriterInput);
// Pixel Buffer Adaptor. This makes it possible to pass CVPixelBuffers to the WriterInput
NSDictionary* pixelBufSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
[NSNumber numberWithInt: frameWidth], kCVPixelBufferWidthKey,
[NSNumber numberWithInt: frameHeight], kCVPixelBufferHeightKey,
nil];
mAdaptor = [[AVAssetWriterInputPixelBufferAdaptor
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:mVideoWriterInput
sourcePixelBufferAttributes:pixelBufSettings] retain];
// Add the input(s) to the assetWriter
NSCParameterAssert([mVideoWriter canAddInput:mVideoWriterInput]);
[mVideoWriter addInput:mVideoWriterInput];
// begin the writing session
bool canStartWriting = [mVideoWriter startWriting];
[mVideoWriter startSessionAtSourceTime:CMTimeMake(0, mTimeScale)];
// return true if everything was created and started successfully
return (mVideoWriter != nil) && (mVideoWriterInput != nil) && canStartWriting;
}
/**
* \brief Close the recording sesion and finish writing the video file
*/
void UBQuickTimeFile::endSession()
{
[mVideoWriterInput markAsFinished];
bool success = [mVideoWriter finishWriting];
[mAdaptor release];
[mVideoWriterInput release];
[mVideoWriter release];
mAdaptor = nil;
mVideoWriterInput = nil;
mVideoWriter = nil;
}
/**
* \brief Request the recording to stop
*/
void UBQuickTimeFile::stop()
{
mShouldStopCompression = true;
}
/**
* \brief Create a CVPixelBufferRef from the input adaptor's CVPixelBufferPool
*/
CVPixelBufferRef UBQuickTimeFile::newPixelBuffer()
{
CVPixelBufferRef pixelBuffer = 0;
if(CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, mAdaptor.pixelBufferPool, &pixelBuffer) != kCVReturnSuccess)
{
setLastErrorMessage("Could not retrieve CV buffer from pool");
return 0;
}
return pixelBuffer;
}
/**
* \brief Add a frame to the pixel buffer adaptor
*/
void UBQuickTimeFile::appendVideoFrame(CVPixelBufferRef pixelBuffer, long msTimeStamp)
{
//qDebug() << "adding video frame at time: " << msTimeStamp;
CMTime t = CMTimeMake((msTimeStamp * mTimeScale / 1000.0), mTimeScale);
bool added = [mAdaptor appendPixelBuffer: pixelBuffer
withPresentationTime: t];
if (!added)
setLastErrorMessage(QString("Could not encode frame at time %1").arg(msTimeStamp));
CVPixelBufferRelease(pixelBuffer);
}
void UBQuickTimeFile::setLastErrorMessage(const QString& error)
{
mLastErrorMessage = error;
qWarning() << "UBQuickTimeFile error" << error;
}
......@@ -130,6 +130,14 @@ void UBQuickTimeVideoEncoder::newPixmap(const QImage& pImage, long timestamp)
}
}
/**
* \brief Encode QImage into a video frame and add it to the UBQuickTimeFile's queue.
*
* This method retrieves the raw image from the supplied QImage, and uses memcpy to
* dump it into a CVPixelBuffer, obtained through the UBQuickTimeFile member. The
* pixel buffer, along with the timestamp, constitute a video frame which is added
* to the member UBQuickTimeFile's queue.
*/
void UBQuickTimeVideoEncoder::encodeFrame(const QImage& pImage, long timestamp)
{
Q_ASSERT(pImage.format() == QImage::QImage::Format_RGB32); // <=> CVPixelBuffers / k32BGRAPixelFormat
......@@ -157,7 +165,7 @@ void UBQuickTimeVideoEncoder::encodeFrame(const QImage& pImage, long timestamp)
const uchar* imageBuffer = pImage.bits();
memcpy((void*) pixelBufferAddress, imageBuffer, pImage.numBytes());
memcpy((void*) pixelBufferAddress, imageBuffer, pImage.byteCount());
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment