diff options
Diffstat (limited to 'src/3rdparty/phonon/qt7')
52 files changed, 9629 insertions, 0 deletions
diff --git a/src/3rdparty/phonon/qt7/CMakeLists.txt b/src/3rdparty/phonon/qt7/CMakeLists.txt new file mode 100644 index 0000000..5310be1 --- /dev/null +++ b/src/3rdparty/phonon/qt7/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 or 3 of the License. +# +# This library 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. +project(phonon-qt7) +include(ConfigureChecks.cmake) + +include_directories(${OPENGL_INCLUDE_DIR}) + +if (BUILD_PHONON_QT7) + set(phonon_qt7_SRCS + quicktimevideoplayer.mm + backendheader.mm + medianodevideopart.mm + medianodeevent.mm + audiooutput.mm + backendinfo.mm + audiosplitter.mm + audioeffects.mm + quicktimestreamreader.mm + medianode.mm + backend.mm + mediaobject.mm + mediaobjectaudionode.mm + audiomixer.mm + quicktimeaudioplayer.mm + videoframe.mm + quicktimemetadata.mm + audiodevice.mm + audioconnection.mm + audiograph.mm + audionode.mm + videowidget.mm + ) + + automoc4_add_library(phonon_qt7 MODULE ${phonon_qt7_SRCS}) + target_link_libraries(phonon_qt7 + ${QT_QTGUI_LIBRARY} ${QT_QTOPENGL_LIBRARY} + ${PHONON_LIBS} + "-framework QuickTime" + "-framework AudioUnit" + "-framework AudioToolbox" + "-framework CoreAudio" + "-framework QuartzCore" + "-framework QTKit" + ) + install(TARGETS phonon_qt7 DESTINATION ${PLUGIN_INSTALL_DIR}) + +endif (BUILD_PHONON_QT7) diff --git a/src/3rdparty/phonon/qt7/ConfigureChecks.cmake b/src/3rdparty/phonon/qt7/ConfigureChecks.cmake new file mode 100644 index 0000000..9644867 --- /dev/null +++ b/src/3rdparty/phonon/qt7/ConfigureChecks.cmake @@ -0,0 +1,16 @@ +# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 2 or 3 of the License. +# +# This library 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see <http://www.gnu.org/licenses/>. + +# Find the frameworks if necessary +set(BUILD_PHONON_QT7 TRUE) diff --git a/src/3rdparty/phonon/qt7/audioconnection.h b/src/3rdparty/phonon/qt7/audioconnection.h new file mode 100644 index 0000000..2129744 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioconnection.h @@ -0,0 +1,84 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AudioConnection_H +#define Phonon_QT7_AudioConnection_H + +#include <QtCore/QObject> +#include "backendheader.h" + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class MediaNode; + class AudioNode; + class AudioGraph; + + class AudioConnection { + public: + AudioConnection(); + AudioConnection(MediaNode *sink); + AudioConnection(MediaNode *source, int output, MediaNode *sink, int input); + + AudioConnection(AudioNode *sink); + AudioConnection(AudioNode *source, int output, AudioNode *sink, int input); + + ~AudioConnection(); + + bool connect(AudioGraph *graph); + bool disconnect(AudioGraph *graph); + + bool updateStreamSpecification(); + bool isBetween(MediaNode *source, MediaNode *sink); + bool isValid(); + bool isSinkOnly(); + void freeMemoryAllocations(); + void invalidate(); + + MediaNode *m_source; + AudioNode *m_sourceAudioNode; + int m_sourceOutputBus; + + MediaNode *m_sink; + AudioNode *m_sinkAudioNode; + int m_sinkInputBus; + + AudioChannelLayout *m_sourceChannelLayout; + UInt32 m_sourceChannelLayoutSize; + + AudioChannelLayout *m_sinkChannelLayout; + UInt32 m_sinkChannelLayoutSize; + + AudioStreamBasicDescription m_sourceStreamDescription; + AudioStreamBasicDescription m_sinkStreamDescription; + + bool m_hasSourceSpecification; + bool m_hasSinkSpecification; + bool m_connected; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AudioConnection_H diff --git a/src/3rdparty/phonon/qt7/audioconnection.mm b/src/3rdparty/phonon/qt7/audioconnection.mm new file mode 100644 index 0000000..39b8b30 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioconnection.mm @@ -0,0 +1,152 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audioconnection.h" +#include "medianode.h" +#include "audionode.h" +#include "audiograph.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + + AudioConnection::AudioConnection() + : m_source(0), m_sourceAudioNode(0), m_sourceOutputBus(0), + m_sink(0), m_sinkAudioNode(0), m_sinkInputBus(0), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), + m_hasSourceSpecification(false), m_hasSinkSpecification(false), m_connected(false) + {} + + AudioConnection::AudioConnection(MediaNode *source, int output, MediaNode *sink, int input) + : m_source(source), m_sourceAudioNode(source->m_audioNode), m_sourceOutputBus(output), + m_sink(sink), m_sinkAudioNode(sink->m_audioNode), m_sinkInputBus(input), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), + m_hasSourceSpecification(false), m_hasSinkSpecification(false), m_connected(false) + {} + + AudioConnection::AudioConnection(MediaNode *sink) + : m_source(0), m_sourceAudioNode(0), m_sourceOutputBus(0), + m_sink(sink), m_sinkAudioNode(sink->m_audioNode), m_sinkInputBus(0), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), m_connected(false) + {} + + AudioConnection::AudioConnection(AudioNode *source, int output, AudioNode *sink, int input) + : m_source(0), m_sourceAudioNode(source), m_sourceOutputBus(output), + m_sink(0), m_sinkAudioNode(sink), m_sinkInputBus(input), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), + m_hasSourceSpecification(false), m_hasSinkSpecification(false), m_connected(false) + {} + + AudioConnection::AudioConnection(AudioNode *sink) + : m_source(0), m_sourceAudioNode(0), m_sourceOutputBus(0), + m_sink(0), m_sinkAudioNode(sink), m_sinkInputBus(0), + m_sourceChannelLayout(0), m_sinkChannelLayout(0), m_connected(false) + {} + + AudioConnection::~AudioConnection() + { + freeMemoryAllocations(); + } + + void AudioConnection::freeMemoryAllocations() + { + if (m_sinkChannelLayout && m_sourceChannelLayout != m_sinkChannelLayout) + free(m_sinkChannelLayout); + if (m_sourceChannelLayout) + free(m_sourceChannelLayout); + m_sinkChannelLayout = 0; + m_sourceChannelLayout = 0; + } + + bool AudioConnection::updateStreamSpecification() + { + m_hasSourceSpecification = false; + m_hasSinkSpecification = false; + freeMemoryAllocations(); + + bool updateOk; + if (m_sourceAudioNode){ + updateOk = m_sourceAudioNode->fillInStreamSpecification(this, AudioNode::Source); + if (!updateOk) + return false; + updateOk = m_sourceAudioNode->setStreamSpecification(this, AudioNode::Source); + if (!updateOk) + return false; + } + updateOk = m_sinkAudioNode->fillInStreamSpecification(this, AudioNode::Sink); + if (!updateOk) + return false; + updateOk = m_sinkAudioNode->setStreamSpecification(this, AudioNode::Sink); + if (!updateOk) + return false; + return true; + } + + bool AudioConnection::connect(AudioGraph *graph) + { + if (m_connected || !m_sourceAudioNode) + return true; + + DEBUG_AUDIO_GRAPH("Connection" << int(this) << "connect" + << int(m_sourceAudioNode) << m_sourceOutputBus << "->" + << int(m_sinkAudioNode) << m_sinkInputBus) + + AUNode sourceOut = m_sourceAudioNode->getOutputAUNode(); + AUNode sinkIn = m_sinkAudioNode->getInputAUNode(); + OSStatus err = AUGraphConnectNodeInput(graph->audioGraphRef(), sourceOut, m_sourceOutputBus, sinkIn, m_sinkInputBus); + m_connected = (err == noErr) ? true : false; + return m_connected; + } + + bool AudioConnection::disconnect(AudioGraph *graph) + { + if (!m_connected || !m_sourceAudioNode) + return true; + + DEBUG_AUDIO_GRAPH("Connection" << int(this) << "disconnect" + << int(m_sourceAudioNode) << m_sourceOutputBus << "->" + << int(m_sinkAudioNode) << m_sinkInputBus) + + AUNode sinkIn = m_sinkAudioNode->getInputAUNode(); + AUGraphDisconnectNodeInput(graph->audioGraphRef(), sinkIn, m_sinkInputBus); + m_connected = false; + return true; + } + + void AudioConnection::invalidate() + { + m_connected = false; + } + + bool AudioConnection::isBetween(MediaNode *source, MediaNode *sink){ + return (source == m_source) && (sink == m_sink); + } + + bool AudioConnection::isValid(){ + return (m_sourceAudioNode != 0); + } + + bool AudioConnection::isSinkOnly(){ + return (m_sourceAudioNode == 0) && (m_sinkAudioNode != 0); + } + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiodevice.h b/src/3rdparty/phonon/qt7/audiodevice.h new file mode 100644 index 0000000..36e937e --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiodevice.h @@ -0,0 +1,52 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIODEVICE_H +#define Phonon_QT7_AUDIODEVICE_H + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioDevice + { + public: + enum Scope {In, Out}; + + static QList<AudioDeviceID> devices(Scope scope); + static AudioDeviceID defaultDevice(Scope scope); + static AudioDeviceID defaultSystemDevice(Scope scope); + static AudioDeviceID currentDevice(AudioUnit unit, Scope scope); + static bool setDevice(AudioUnit unit, AudioDeviceID deviceID, Scope scope); + static QString deviceName(AudioDeviceID deviceId); + static QString deviceSourceName(AudioDeviceID deviceID); + static QString deviceSourceNameElseDeviceName(AudioDeviceID deviceID); + static QString deviceNameElseDeviceSourceName(AudioDeviceID deviceID); + static QString deviceUID(AudioDeviceID deviceID); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIODEVICE_H diff --git a/src/3rdparty/phonon/qt7/audiodevice.mm b/src/3rdparty/phonon/qt7/audiodevice.mm new file mode 100644 index 0000000..3aae0ee --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiodevice.mm @@ -0,0 +1,176 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiodevice.h" +#include "audiograph.h" +#include <QtCore/QVector> +#include "backendheader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QList<AudioDeviceID> AudioDevice::devices(Scope scope) +{ + QList<AudioDeviceID> devices; + + // Insert the default device explicit + if (AudioDeviceID defdev = defaultDevice(scope)) + devices << defdev; + + // How many input/output devices are awailable: + UInt32 deviceCount = 0; + OSStatus err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &deviceCount, 0); + BACKEND_ASSERT3(err == noErr, "Could not get number of audio devices awailable.", FATAL_ERROR, devices) + + // Get list of all devices: + AudioDeviceID deviceArray[deviceCount]; + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &deviceCount, &deviceArray); + BACKEND_ASSERT3(err == noErr, "Could not get audio devices list.", FATAL_ERROR, devices) + + for (uint i=0; i<deviceCount; i++){ + if (!devices.contains(deviceArray[i])){ + // Check if the current device is input or output: + UInt32 size; + err = AudioDeviceGetPropertyInfo(deviceArray[i], 0, scope == In, kAudioDevicePropertyStreams, &size, 0); + if (err == noErr && size > 0) + devices << deviceArray[i]; + } + } + return devices; +} + +AudioDeviceID AudioDevice::defaultSystemDevice(Scope scope) +{ + ARGUMENT_UNSUPPORTED(scope, In, NORMAL_ERROR, 0) + AudioDeviceID deviceID = 0; + UInt32 size = sizeof(deviceID); + OSStatus err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultSystemOutputDevice, &size, &deviceID); + BACKEND_ASSERT3(err == noErr, "Could not get default system audio device.", FATAL_ERROR, 0) + return deviceID; +} + +AudioDeviceID AudioDevice::defaultDevice(Scope scope) +{ + ARGUMENT_UNSUPPORTED(scope, In, NORMAL_ERROR, 0) + AudioDeviceID deviceID = 0; + UInt32 size = sizeof(deviceID); + OSStatus err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &deviceID); + BACKEND_ASSERT3(err == noErr, "Could not get default output audio device.", FATAL_ERROR, 0) + return deviceID; +} + +AudioDeviceID AudioDevice::currentDevice(AudioUnit /*unit*/, Scope /*scope*/) +{ + return 0; +#if 0 + +kAudioDevicePropertyDeviceUID + + if (!m_audioUnit) + return 0; + AudioDeviceID deviceID = 0; + UInt32 size = sizeof(deviceID); + OSStatus err = AudioUnitGetProperty(m_audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &size, &deviceID); + BACKEND_ASSERT3(err == noErr, "Could not get current audio device.", FATAL_ERROR, 0) + return deviceID; +#endif +} + +bool AudioDevice::setDevice(AudioUnit unit, AudioDeviceID deviceID, Scope scope) +{ + ARGUMENT_UNSUPPORTED(scope, In, NORMAL_ERROR, false) + UInt32 size = sizeof(deviceID); + OSStatus err = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceID, size); + if (err != noErr) + return false; + return true; +} + +QString AudioDevice::deviceSourceNameElseDeviceName(AudioDeviceID deviceID) +{ + QString name = deviceSourceName(deviceID); + if (name.isEmpty()) + name = deviceName(deviceID); + return name; +} + +QString AudioDevice::deviceNameElseDeviceSourceName(AudioDeviceID deviceID) +{ + QString name = deviceName(deviceID); + if (name.isEmpty()) + name = deviceSourceName(deviceID); + return name; +} + +QString AudioDevice::deviceName(AudioDeviceID deviceID) +{ + if (!deviceID) + return QString(); + CFStringRef cfString = 0; + UInt32 size = sizeof(cfString); + OSStatus err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDeviceNameCFString, &size, &cfString); + if (err != noErr) + return QString(); + QString name = PhononCFString::toQString(cfString); + CFRelease(cfString); + return name; +} + +QString AudioDevice::deviceSourceName(AudioDeviceID deviceID) +{ + if (!deviceID) + return QString(); + UInt32 dataSource = 0; + UInt32 size = sizeof(dataSource); + OSStatus err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDataSource, &size, &dataSource); + if (err != noErr) + return QString(); + + CFStringRef cfName = 0; + AudioValueTranslation translation = {&dataSource, sizeof(dataSource), &cfName, sizeof(cfName)}; + size = sizeof(translation); + err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDataSourceNameForIDCFString, &size, &translation); + if (err != noErr){ + return QString(); + } + QString name = PhononCFString::toQString(cfName); + CFRelease(cfName); + return name; +} + +QString AudioDevice::deviceUID(AudioDeviceID deviceID) +{ + if (!deviceID) + return QString(); + + CFStringRef cfString = 0; + UInt32 size = sizeof(cfString); + OSStatus err = AudioDeviceGetProperty(deviceID, 0, 0, kAudioDevicePropertyDeviceUID, &size, &cfString); + if (err != noErr) + return QString(); + QString uid = PhononCFString::toQString(cfString); + CFRelease(cfString); + return uid; +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audioeffects.h b/src/3rdparty/phonon/qt7/audioeffects.h new file mode 100644 index 0000000..33fff1c --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioeffects.h @@ -0,0 +1,80 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOEFFECTS_H +#define Phonon_QT7_AUDIOEFFECTS_H + +#include <QtCore/QVariant> +#include <QtCore/QHash> +#include <phonon/effectinterface.h> +#include <phonon/effectparameter.h> +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioEffectAudioNode : public AudioNode + { + public: + AudioEffectAudioNode(int effectType); + int m_effectType; + + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + + QVariant parameterValue(const Phonon::EffectParameter &value) const; + void setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue); + + private: + QHash<int, float> m_alteredParameters; + }; + +/////////////////////////////////////////////////////////////////////// + + class AudioEffect : public MediaNode, Phonon::EffectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) + + public: + AudioEffect(int effectType, QObject *parent = 0); + AudioEffectAudioNode *m_audioNode; + + QString name(); + QString description(); + + // EffectInterface: + virtual QList<Phonon::EffectParameter> parameters() const; + virtual QVariant parameterValue(const Phonon::EffectParameter ¶meter) const; + virtual void setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue); + + static QList<int> effectList(); + + private: + Phonon::EffectParameter createParameter(const AudioUnit &audioUnit, const AudioUnitParameterID &id) const; + }; + + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOEFFECTS_H diff --git a/src/3rdparty/phonon/qt7/audioeffects.mm b/src/3rdparty/phonon/qt7/audioeffects.mm new file mode 100644 index 0000000..b0ec3cc --- /dev/null +++ b/src/3rdparty/phonon/qt7/audioeffects.mm @@ -0,0 +1,254 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audioeffects.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioEffectAudioNode::AudioEffectAudioNode(int effectType) + : AudioNode(1, 1), m_effectType(effectType) +{ +} + +ComponentDescription AudioEffectAudioNode::getAudioNodeDescription() const +{ + ComponentDescription d; + d.componentType = kAudioUnitType_Effect; + d.componentSubType = m_effectType; + d.componentManufacturer = kAudioUnitManufacturer_Apple; + d.componentFlags = 0; + d.componentFlagsMask = 0; + return d; +} + +void AudioEffectAudioNode::initializeAudioUnit() +{ + if (!m_audioUnit) + return; + foreach(int id, m_alteredParameters.keys()){ + Float32 value = m_alteredParameters.value(id); + ComponentResult res = AudioUnitSetParameter(m_audioUnit, id, kAudioUnitScope_Global, 0, value, 0); + BACKEND_ASSERT2(res == noErr, "Could not initialize audio effect.", NORMAL_ERROR) + } +} + +QVariant AudioEffectAudioNode::parameterValue(const Phonon::EffectParameter ¶meter) const +{ + if (m_audioUnit){ + Float32 value = 0; + AudioUnitGetParameter(m_audioUnit, parameter.id(), kAudioUnitScope_Global, 0, &value); + return QVariant(value); + } else if (m_alteredParameters.contains(parameter.id())){ + return QVariant(m_alteredParameters.value(parameter.id())); + } else { + // Use default value: + AudioUnit tmpAudioUnit; + ComponentDescription description = getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get parameters of audio effect.", NORMAL_ERROR, QVariant()) + OSErr err = OpenAComponent(component, &tmpAudioUnit); + BACKEND_ASSERT3(err == noErr, "Could not get parameters of audio effect.", NORMAL_ERROR, QVariant()) + AudioUnitParameterInfo info; + UInt32 size = sizeof(info); + ComponentResult res = AudioUnitGetProperty(tmpAudioUnit, + kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, parameter.id(), &info, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter info from audio effect.", NORMAL_ERROR, QVariant()) + return QVariant(info.defaultValue); + } +} + +void AudioEffectAudioNode::setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue) +{ + Float32 value = 0; + if (newValue.isValid()){ + value = newValue.toDouble(); + m_alteredParameters.insert(parameter.id(), value); + } else { + // Use default value: + m_alteredParameters.remove(parameter.id()); + if (m_audioUnit){ + AudioUnit tmpAudioUnit; + ComponentDescription description = getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT2(component, "Could not get parameters of audio effect.", NORMAL_ERROR) + OSErr err = OpenAComponent(component, &tmpAudioUnit); + BACKEND_ASSERT2(err == noErr, "Could not get parameters of audio effect.", NORMAL_ERROR) + AudioUnitParameterInfo info; + UInt32 size = sizeof(info); + ComponentResult res = AudioUnitGetProperty(tmpAudioUnit, + kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, parameter.id(), &info, &size); + BACKEND_ASSERT2(res == noErr, "Could not get parameter info from audio effect.", NORMAL_ERROR) + value = info.defaultValue; + } + } + + if (m_audioUnit){ + ComponentResult res = AudioUnitSetParameter(m_audioUnit, parameter.id(), kAudioUnitScope_Global, 0, value, 0); + BACKEND_ASSERT2(res == noErr, "Could not set effect parameter value.", NORMAL_ERROR) + } +} + +/////////////////////////////////////////////////////////////////////// + +AudioEffect::AudioEffect(int effectType, QObject *parent) + : MediaNode(AudioSink | AudioSource, 0, parent) +{ + m_audioNode = new AudioEffectAudioNode(effectType); + setAudioNode(m_audioNode); +} + +QList<Phonon::EffectParameter> AudioEffect::parameters() const +{ + QList<Phonon::EffectParameter> effectList; + // Create a temporary audio unit: + AudioUnit audioUnit; + ComponentDescription description = m_audioNode->getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get parameters of audio effect.", NORMAL_ERROR, effectList) + OSErr err = OpenAComponent(component, &audioUnit); + BACKEND_ASSERT3(err == noErr, "Could not get parameters of audio effect.", NORMAL_ERROR, effectList) + + UInt32 size = 0; + // Get parameter count: + ComponentResult res = AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, 0, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter count from audio effect.", NORMAL_ERROR, effectList) + int paramCount = size / sizeof(AudioUnitParameterID); + + // Get parameters: + AudioUnitParameterID parameters[paramCount]; + res = AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, ¶meters, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter list from audio effect.", NORMAL_ERROR, effectList) + + for (int i=0; i<paramCount; ++i) + effectList << createParameter(audioUnit, parameters[i]); + + CloseComponent(audioUnit); + return effectList; +} + +QString AudioEffect::name() +{ + ComponentDescription description = m_audioNode->getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get audio effect name.", NORMAL_ERROR, QLatin1String("<unknown effect>")) + + ComponentDescription cDesc; + Handle nameH = NewHandle(0); + GetComponentInfo(component, &cDesc, nameH, 0, 0); + HLock(nameH); + char *namePtr = *nameH; + int len = *namePtr++; + namePtr[len] = 0; + QString qsName = QString::fromUtf8(namePtr); + DisposeHandle(nameH); + return qsName; +} + +QString AudioEffect::description() +{ + ComponentDescription description = m_audioNode->getAudioNodeDescription(); + Component component = FindNextComponent(0, &description); + BACKEND_ASSERT3(component, "Could not get audio effect description.", NORMAL_ERROR, QLatin1String("<unknown effect>")) + + ComponentDescription cDesc; + Handle descH = NewHandle(0); + GetComponentInfo(component, &cDesc, 0, descH, 0); + HLock(descH); + char *descPtr = *descH; + int len = *descPtr++; + descPtr[len] = 0; + QString qsDesc = QString::fromUtf8(descPtr); + DisposeHandle(descH); + return qsDesc; +} + +QList<int> AudioEffect::effectList() +{ + QList<int> effects; + + ComponentDescription d; + d.componentType = kAudioUnitType_Effect; + d.componentSubType = 0; + d.componentManufacturer = 0; + d.componentFlags = 0; + d.componentFlagsMask = 0; + Component component = FindNextComponent(0, &d); + + while (component) { + ComponentDescription cDesc; + GetComponentInfo(component, &cDesc, 0, 0, 0); + effects << cDesc.componentSubType; + component = FindNextComponent(component, &d); + } + return effects; +} + +Phonon::EffectParameter AudioEffect::createParameter(const AudioUnit &audioUnit, const AudioUnitParameterID &id) const +{ + AudioUnitParameterInfo info; + UInt32 size = sizeof(info); + ComponentResult res = AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, id, &info, &size); + BACKEND_ASSERT3(res == noErr, "Could not get parameter info from audio effect.", NORMAL_ERROR, Phonon::EffectParameter()) + + QString name = info.flags & kAudioUnitParameterFlag_HasCFNameString + ? PhononCFString::toQString(info.cfNameString) : QLatin1String("<unknown parameter>"); + + Phonon::EffectParameter::Hint hint; + switch(info.unit){ + case (kAudioUnitParameterUnit_Indexed): + case (kAudioUnitParameterUnit_Seconds): + case (kAudioUnitParameterUnit_SampleFrames): + case (kAudioUnitParameterUnit_Milliseconds): + hint = Phonon::EffectParameter::IntegerHint; + break; + case (kAudioUnitParameterUnit_Boolean): + hint = Phonon::EffectParameter::ToggledHint; + break; + default: + hint = Phonon::EffectParameter::LogarithmicHint; + break; + } + + QVariant def(info.defaultValue); + QVariant min(info.minValue); + QVariant max(info.maxValue); + return Phonon::EffectParameter(id, name, hint, def, min, max, QVariantList(), name); +} + +QVariant AudioEffect::parameterValue(const Phonon::EffectParameter &value) const +{ + return m_audioNode->parameterValue(value); +} + +void AudioEffect::setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue) +{ + m_audioNode->setParameterValue(parameter, newValue); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_audioeffects.cpp" diff --git a/src/3rdparty/phonon/qt7/audiograph.h b/src/3rdparty/phonon/qt7/audiograph.h new file mode 100644 index 0000000..76bc21a --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiograph.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOGRAPH_H +#define Phonon_QT7_AUDIOGRAPH_H + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> + +#include <QtCore/qnamespace.h> +#include "audioconnection.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioGraph : public MediaNode + { + public: + AudioGraph(MediaNode *root); + virtual ~AudioGraph(); + AUGraph audioGraphRef(); + void startAllOverFromScratch(); + + bool openAndInit(); + void start(); + void tryStartGraph(); + void stop(); + void prepare(); + void rebuildGraph(); + void update(); + bool isRunning(); + void setPaused(bool pause); + bool graphCannotPlay(); + void rebuildGraphIfNeeded(); + void updateStreamSpecifications(); + void setStatusCannotPlay(); + MediaNode *root(); + void notify(const MediaNodeEvent *event, bool propagate = true); + + private: + friend class MediaNode; + friend class AudioNode; + + void deleteGraph(); + bool updateStreamSpecificationRecursive(AudioConnection *connection); + void createAndConnectAuNodesRecursive(AudioConnection *connection); + bool createAudioUnitsRecursive(AudioConnection *connection); + + void connectLate(AudioConnection *connection); + void disconnectLate(AudioConnection *connection); + + int nodeCount(); + + bool m_initialized; + bool m_startedLogically; + bool m_rebuildLater; + bool m_graphCannotPlay; + bool m_paused; + + AUGraph m_audioGraphRef; + MediaNode *m_root; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOGRAPH_H diff --git a/src/3rdparty/phonon/qt7/audiograph.mm b/src/3rdparty/phonon/qt7/audiograph.mm new file mode 100644 index 0000000..0d096e3 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiograph.mm @@ -0,0 +1,320 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiograph.h" +#include "quicktimeaudioplayer.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioGraph::AudioGraph(MediaNode *root) : MediaNode(AudioGraphNode, 0, root), m_root(root) +{ + m_audioGraphRef = 0; + m_initialized = false; + m_startedLogically = false; + m_graphCannotPlay = false; + m_paused = false; +} + +AudioGraph::~AudioGraph() +{ + deleteGraph(); +} + +void AudioGraph::startAllOverFromScratch() +{ + MediaNodeEvent event(MediaNodeEvent::AudioGraphAboutToBeDeleted, this); + m_root->notify(&event); + deleteGraph(); +} + +void AudioGraph::deleteGraph() +{ + if (m_audioGraphRef){ + AUGraphStop(m_audioGraphRef); + AUGraphUninitialize(m_audioGraphRef); + AUGraphClose(m_audioGraphRef); + DisposeAUGraph(m_audioGraphRef); + m_initialized = false; + m_graphCannotPlay = false; + DEBUG_AUDIO_GRAPH("Graph ref in" << int(this) << "is deleted") + } +} + +MediaNode *AudioGraph::root() +{ + return m_root; +} + +AUGraph AudioGraph::audioGraphRef() +{ + return m_audioGraphRef; +} + +void AudioGraph::setStatusCannotPlay() +{ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "received 'cannot play' request") + if (!m_graphCannotPlay){ + stop(); + m_graphCannotPlay = true; + MediaNodeEvent e(MediaNodeEvent::AudioGraphCannotPlay, this); + m_root->notify(&e); + } +} + +void AudioGraph::rebuildGraph() +{ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "is rebuilding") + startAllOverFromScratch(); + if (!openAndInit()){ + setStatusCannotPlay(); + } else { + tryStartGraph(); + m_graphCannotPlay = false; + } +} + +bool AudioGraph::graphCannotPlay() +{ + return m_graphCannotPlay; +} + +void AudioGraph::updateStreamSpecifications() +{ + if (!m_initialized){ + if (m_graphCannotPlay) + rebuildGraph(); + return; + } + + AudioConnection rootConnection(m_root); + bool updateOk = updateStreamSpecificationRecursive(&rootConnection); + if (!updateOk){ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.") + rebuildGraph(); + } +} + +bool AudioGraph::updateStreamSpecificationRecursive(AudioConnection *connection) +{ + bool updateOk = connection->updateStreamSpecification(); + if (!updateOk) + return false; + + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){ + if (!updateStreamSpecificationRecursive(connection->m_sink->m_audioSinkList[i])) + return false; + } + return true; +} + +bool AudioGraph::openAndInit() +{ + OSStatus err; + err = NewAUGraph(&m_audioGraphRef); + BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false) + + MediaNodeEvent eventNew(MediaNodeEvent::NewAudioGraph, this); + m_root->notify(&eventNew); + + AudioConnection rootConnection(m_root); + createAndConnectAuNodesRecursive(&rootConnection); + err = AUGraphOpen(m_audioGraphRef); + BACKEND_ASSERT3(err == noErr, "Could not create audio graph.", NORMAL_ERROR, false) + + if (!createAudioUnitsRecursive(&rootConnection)) + return false; + + err = AUGraphInitialize(m_audioGraphRef); + BACKEND_ASSERT3(err == noErr, "Could not initialize audio graph.", NORMAL_ERROR, false) + + m_initialized = true; + MediaNodeEvent eventInit(MediaNodeEvent::AudioGraphInitialized, this); + m_root->notify(&eventInit); + return true; +} + +void AudioGraph::createAndConnectAuNodesRecursive(AudioConnection *connection) +{ + connection->m_sink->m_audioNode->createAndConnectAUNodes(); + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){ + AudioConnection *c = connection->m_sink->m_audioSinkList[i]; + createAndConnectAuNodesRecursive(c); + bool ok = c->connect(this); + BACKEND_ASSERT2(ok, "Could not connect an audio nodes pair in the audio graph.", NORMAL_ERROR) + } +} + +bool AudioGraph::createAudioUnitsRecursive(AudioConnection *connection) +{ + connection->m_sink->m_audioNode->createAudioUnits(); + bool ok = connection->updateStreamSpecification(); + if (!ok) + return false; + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i){ + if (!createAudioUnitsRecursive(connection->m_sink->m_audioSinkList[i])) + return false; + } + return true; +} + +void AudioGraph::tryStartGraph() +{ + // The graph will only start if the background AUGraph + // is valid. Therefore we just try. If it fails, user + // actions like connect etc. migh make the graph valid + // at a later point. + if (m_startedLogically && !isRunning()){ + OSStatus err = AUGraphStart(m_audioGraphRef); + if (err == noErr) + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "started") + else + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not start") + } +} + +bool AudioGraph::isRunning() +{ + Boolean running = false; + AUGraphIsRunning(m_audioGraphRef, &running); + return running; +} + +void AudioGraph::setPaused(bool pause) +{ + // This function should only make + // a difference if the graph is + // running before pausing. + if (pause){ + if (isRunning()){ + stop(); + m_paused = true; + } + } else if (m_paused){ + start(); + m_paused = false; + } +} + +void AudioGraph::connectLate(AudioConnection *connection) +{ + MediaNodeEvent event(MediaNodeEvent::NewAudioGraph, this); + connection->m_sink->notify(&event); + + if (!m_initialized) + return; + + DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "create and connect audio sink after init:" << int(connection->m_sink->m_audioNode)) + AudioConnection startConnection(connection->m_source); + createAndConnectAuNodesRecursive(&startConnection); + + if (!createAudioUnitsRecursive(&startConnection)){ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not update stream specification. Rebuild.") + rebuildGraph(); + } +} + +void AudioGraph::disconnectLate(AudioConnection *connection) +{ + if (!m_initialized) + return; + + DEBUG_AUDIO_GRAPH("Graph:" << int(this) << "disconnect audio sink after init:" << int(connection->m_sink->m_audioNode)) + + if (!connection->disconnect(this)){ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "could not disconnect audio sink. Rebuild.") + rebuildGraph(); + } +} + +void AudioGraph::update() +{ + if (m_startedLogically){ + if (m_initialized){ + // Quick solution: + AUGraphUpdate(m_audioGraphRef, 0); + tryStartGraph(); + } else + rebuildGraph(); + } +} + +int AudioGraph::nodeCount() +{ + if (!m_audioGraphRef) + return 0; + UInt32 count; + AUGraphGetNodeCount(m_audioGraphRef, &count); + return int(count); +} + +void AudioGraph::prepare() +{ + if (!m_initialized) + rebuildGraph(); +} + +void AudioGraph::start() +{ + // Start does not mean 'start to play + // music'. It means 'prepare to receive + // audio from the player units'. + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to start (cannot play:" << m_graphCannotPlay << ")") + m_startedLogically = true; + + if (m_graphCannotPlay) + return; + + if (!m_initialized) + rebuildGraph(); + + if (!m_graphCannotPlay) + tryStartGraph(); +} + +void AudioGraph::stop() +{ + DEBUG_AUDIO_GRAPH("Graph" << int(this) << "asked to stop") + if (m_audioGraphRef) + AUGraphStop(m_audioGraphRef); + m_startedLogically = false; +} + +void AudioGraph::notify(const MediaNodeEvent *event, bool propagate) +{ + switch (event->type()){ + case MediaNodeEvent::StartConnectionChange: + if (m_graphCannotPlay) + startAllOverFromScratch(); + break; + case MediaNodeEvent::EndConnectionChange: + update(); + break; + default: + break; + } + m_root->notify(event, propagate); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiomixer.h b/src/3rdparty/phonon/qt7/audiomixer.h new file mode 100644 index 0000000..275d7df --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiomixer.h @@ -0,0 +1,91 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOMIXER_H +#define Phonon_QT7_AUDIOMIXER_H + +#include <QtCore/QObject> +#include <QtCore/QTime> +#include <QtCore/QEvent> +#include <phonon/effectinterface.h> +#include <phonon/effectparameter.h> +#include <phonon/volumefaderinterface.h> +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioMixerAudioNode : public AudioNode + { + public: + AudioMixerAudioNode(); + void setVolume(float volume, int bus = 0); + float volume(int bus = 0); + + protected: + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + + private: + friend class AudioMixer; + int m_numberOfBusses; + float m_volume; + }; + + class AudioMixer : public MediaNode, Phonon::EffectInterface, Phonon::VolumeFaderInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface Phonon::VolumeFaderInterface) + + public: + AudioMixer(QObject *parent = 0); + ~AudioMixer(); + AudioMixerAudioNode *m_audioNode; + Phonon::VolumeFaderEffect::FadeCurve m_fadeCurve; + + int m_fadeTimer; + int m_fadeDuration; + float m_fadeToVolume; + float m_fadeFromVolume; + QTime m_fadeStartTime; + + // EffectInterface: + QList<Phonon::EffectParameter> parameters() const; + QVariant parameterValue(const Phonon::EffectParameter ¶meter) const; + void setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue); + + // VolumeFaderInterface: + float volume() const; + void setVolume(float volume); + Phonon::VolumeFaderEffect::FadeCurve fadeCurve() const; + void setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve); + void fadeTo(float volume, int fadeTime); + void updateFade(); + + protected: + bool event(QEvent *event); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOMIXER_H diff --git a/src/3rdparty/phonon/qt7/audiomixer.mm b/src/3rdparty/phonon/qt7/audiomixer.mm new file mode 100644 index 0000000..30b1e6f --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiomixer.mm @@ -0,0 +1,181 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiomixer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioMixerAudioNode::AudioMixerAudioNode() : AudioNode(30, 1) +{ + m_numberOfBusses = 2; + m_volume = 1.0f; +} + +ComponentDescription AudioMixerAudioNode::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Mixer; + description.componentSubType = kAudioUnitSubType_StereoMixer; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void AudioMixerAudioNode::initializeAudioUnit() +{ + // Set bus count: + OSStatus err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &m_numberOfBusses, sizeof(int)); + BACKEND_ASSERT2(err == noErr, "Could not set number of busses on audio mixer node.", FATAL_ERROR) +} + +void AudioMixerAudioNode::setVolume(float volume, int bus) +{ + if (volume < 0) + m_volume = 0; + else if (volume > 1) + m_volume = 1; + else + m_volume = volume; + + if (m_audioUnit){ +// Float32 db = Float32(volume);//Float32(20.0 * log10(volume)); // convert to db + Float32 db = Float32(volume); + OSStatus err = AudioUnitSetParameter(m_audioUnit, kStereoMixerParam_Volume, kAudioUnitScope_Input, bus, db, 0); + BACKEND_ASSERT2(err == noErr, "Could not set volume on audio mixer node.", NORMAL_ERROR) + } +} + +float AudioMixerAudioNode::volume(int bus) +{ + if (!m_audioUnit) + return 0; + + Float32 db; + OSStatus err = AudioUnitGetParameter(m_audioUnit, kStereoMixerParam_Volume, kAudioUnitScope_Input, bus, &db); + BACKEND_ASSERT3(err == noErr, "Could not get volume on audio mixer node.", NORMAL_ERROR, 0) + return float(db); +} + +/////////////////////////////////////////////////////////////////////// + +AudioMixer::AudioMixer(QObject *parent) : MediaNode(AudioSink | AudioSource, 0, parent) +{ + m_audioNode = new AudioMixerAudioNode(); + setAudioNode(m_audioNode); + m_fadeCurve = Phonon::VolumeFaderEffect::Fade3Decibel; + m_fadeTimer = 0; + m_fadeDuration = 0; + m_fadeFromVolume = 0; + m_fadeToVolume = 0; +} + +AudioMixer::~AudioMixer() +{ + if (m_fadeTimer) + killTimer(m_fadeTimer); +} + +QList<Phonon::EffectParameter> AudioMixer::parameters() const +{ + QList<Phonon::EffectParameter> ret; + return ret; +} + +QVariant AudioMixer::parameterValue(const Phonon::EffectParameter &value) const +{ + NOT_IMPLEMENTED; + Q_UNUSED(value); + return QVariant(); +} + +void AudioMixer::setParameterValue(const Phonon::EffectParameter ¶meter, const QVariant &newValue) +{ + NOT_IMPLEMENTED; + Q_UNUSED(parameter); + Q_UNUSED(newValue); +} + +float AudioMixer::volume() const +{ + return m_audioNode->volume(0); +} + +void AudioMixer::setVolume(float volume) +{ + m_audioNode->setVolume(volume, 0); +} + +Phonon::VolumeFaderEffect::FadeCurve AudioMixer::fadeCurve() const +{ + return m_fadeCurve; +} + +void AudioMixer::setFadeCurve(Phonon::VolumeFaderEffect::FadeCurve fadeCurve) +{ + m_fadeCurve = fadeCurve; +} + +void AudioMixer::fadeTo(float volume, int fadeTime) +{ + m_fadeToVolume = volume; + m_fadeDuration = fadeTime; + m_fadeFromVolume = m_audioNode->volume(0); + + m_fadeStartTime.start(); + if (m_fadeTimer) + killTimer(m_fadeTimer); + m_fadeTimer = startTimer(100); +} + +void AudioMixer::updateFade() +{ + float step = float(m_fadeStartTime.elapsed()) / float(m_fadeDuration); + if (step > 1){ + step = 1; + if (m_fadeTimer) + killTimer(m_fadeTimer); + } + float volume = m_fadeFromVolume + ((m_fadeToVolume - m_fadeFromVolume) * step); + m_audioNode->setVolume(volume, 0); +} + +bool AudioMixer::event(QEvent *event) +{ + switch (event->type()){ + case QEvent::Timer:{ + QTimerEvent *timerEvent = static_cast<QTimerEvent *>(event); + if (timerEvent->timerId() == m_fadeTimer) + updateFade(); + break; } + default: + break; + } + return MediaNode::event(event); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_audiomixer.cpp" diff --git a/src/3rdparty/phonon/qt7/audionode.h b/src/3rdparty/phonon/qt7/audionode.h new file mode 100644 index 0000000..dfec817 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audionode.h @@ -0,0 +1,86 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AudioNode_H +#define Phonon_QT7_AudioNode_H + +#include <QtCore/QObject> +#include "backendheader.h" +#include "audioconnection.h" +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioGraph; + class MediaNodeEvent; + class MediaNode; + + class MediaNodeConnection{ + MediaNode *source; + MediaNode *sink; + int inputPort; + int outputPort; + }; + + class AudioNode + { + public: + enum ConnectionSide {Source, Sink}; + + AudioNode(int maxInput, int maxOutput); + virtual ~AudioNode(); + + virtual void createAndConnectAUNodes(); + virtual void createAudioUnits(); + virtual void setGraph(AudioGraph *audioGraph); + virtual AUNode getInputAUNode(); + virtual AUNode getOutputAUNode(); + virtual bool fillInStreamSpecification(AudioConnection *connection, ConnectionSide side); + virtual bool setStreamSpecification(AudioConnection *connection, ConnectionSide side); + void notify(const MediaNodeEvent *event); + + virtual void mediaNodeEvent(const MediaNodeEvent *event); + Float64 getTimeInSamples(int timeProperty); + + AudioGraph *m_audioGraph; + AudioConnection *m_lastConnectionIn; + + int m_maxInputBusses; + int m_maxOutputBusses; + + protected: + AUNode m_auNode; + AudioUnit m_audioUnit; + + // Only the following methods needs to + // be overridden by only_one-audio-unit nodes: + virtual ComponentDescription getAudioNodeDescription() const; + virtual void initializeAudioUnit(); + + private: + bool setStreamHelp(AudioConnection *c, int bus, OSType scope, bool fromSource); + }; +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AudioNode_H diff --git a/src/3rdparty/phonon/qt7/audionode.mm b/src/3rdparty/phonon/qt7/audionode.mm new file mode 100644 index 0000000..77cd627 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audionode.mm @@ -0,0 +1,243 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audionode.h" +#include "audiograph.h" +#include "audioconnection.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioNode::AudioNode(int maxInputBusses, int maxOutputBusses) +{ + m_auNode = 0; + m_audioUnit = 0; + m_audioGraph = 0; + m_maxInputBusses = maxInputBusses; + m_maxOutputBusses = maxOutputBusses; + m_lastConnectionIn = 0; +} + +AudioNode::~AudioNode() +{ + setGraph(0); +} + +void AudioNode::setGraph(AudioGraph *audioGraph) +{ + if (m_audioGraph == audioGraph) + return; + + DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "is setting graph:" << int(audioGraph)) + if (m_auNode){ + AUGraphRemoveNode(m_audioGraph->audioGraphRef(), m_auNode); + m_auNode = 0; + } + + m_audioUnit = 0; + m_lastConnectionIn = 0; + m_audioGraph = audioGraph; +} + +void AudioNode::createAndConnectAUNodes() +{ + if (m_auNode) + return; + + ComponentDescription description = getAudioNodeDescription(); + DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "creates AUNode" + << QString(!FindNextComponent(0, &description) ? "ERROR: COMPONENT NOT FOUND!" : "OK!")) + + OSStatus err = noErr; + + // The proper function to call here is AUGraphAddNode() but the type has + // changed between 10.5 and 10.6. it's still OK to call this function, but + // if we want to use the proper thing we need to move over to + // AudioComponentDescription everywhere, which is very similar to the + // ComponentDescription, but a different size. however, + // AudioComponentDescription only exists on 10.6+. More fun than we need to + // deal with at the moment, so we'll take the "deprecated" warning instead. + err = AUGraphNewNode(m_audioGraph->audioGraphRef(), &description, 0, 0, &m_auNode); + + BACKEND_ASSERT2(err != kAUGraphErr_OutputNodeErr, "A MediaObject can only be connected to one audio output device.", FATAL_ERROR) + BACKEND_ASSERT2(err == noErr, "Could not create new AUNode.", FATAL_ERROR) +} + +AUNode AudioNode::getInputAUNode() +{ + return m_auNode; +} + +AUNode AudioNode::getOutputAUNode() +{ + return m_auNode; +} + +void AudioNode::createAudioUnits() +{ + if (m_audioUnit) + return; + + DEBUG_AUDIO_GRAPH("AudioNode" << int(this) << "creates AudioUnit") + OSStatus err = AUGraphGetNodeInfo(m_audioGraph->audioGraphRef(), m_auNode, 0, 0, 0, &m_audioUnit); + BACKEND_ASSERT2(err == noErr, "Could not get audio unit from audio node.", FATAL_ERROR) + initializeAudioUnit(); +} + +ComponentDescription AudioNode::getAudioNodeDescription() const +{ + // Override if needed. + ComponentDescription cd; + Q_UNUSED(cd); + return cd; +} + +bool AudioNode::setStreamHelp(AudioConnection *c, int bus, OSType scope, bool fromSource) +{ + if (fromSource){ + OSStatus err = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, + bus, &c->m_sourceStreamDescription, sizeof(AudioStreamBasicDescription)); + if (err != noErr){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " - failed setting stream format") + return false; + } + AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_AudioChannelLayout, scope, + bus, c->m_sourceChannelLayout, c->m_sourceChannelLayoutSize); + } else { + OSStatus err = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat, scope, + bus, &c->m_sinkStreamDescription, sizeof(AudioStreamBasicDescription)); + if (err != noErr){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " - failed setting stream format") + return false; + } + AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_AudioChannelLayout, scope, + bus, c->m_sinkChannelLayout, c->m_sourceChannelLayoutSize); + } + return true; +} + +bool AudioNode::setStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + // This object am source of connection: + if (connection->m_hasSourceSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification out" + << connection->m_sourceOutputBus << "from connection source") + return setStreamHelp(connection, connection->m_sourceOutputBus, kAudioUnitScope_Output, true); + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "did not set stream specification out") + } + } else { + if (connection->m_hasSinkSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification" + << connection->m_sinkInputBus << "from connection sink") + return setStreamHelp(connection, connection->m_sinkInputBus, kAudioUnitScope_Input, false); + } else if (connection->m_hasSourceSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "sets stream specification" + << connection->m_sinkInputBus << "from connection source") + return setStreamHelp(connection, connection->m_sinkInputBus, kAudioUnitScope_Input, true); + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "did not set stream specification in") + } + } + return true; +} + +bool AudioNode::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + // As default, use the last description to describe the source: + if (m_lastConnectionIn->m_hasSinkSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is source, and fills in stream spec using last connection sink.") + connection->m_sourceStreamDescription = m_lastConnectionIn->m_sinkStreamDescription; + connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_lastConnectionIn->m_sinkChannelLayoutSize); + memcpy(connection->m_sourceChannelLayout, m_lastConnectionIn->m_sinkChannelLayout, m_lastConnectionIn->m_sinkChannelLayoutSize); + connection->m_sourceChannelLayoutSize = m_lastConnectionIn->m_sinkChannelLayoutSize; + connection->m_hasSourceSpecification = true; + } else if (m_lastConnectionIn->m_hasSourceSpecification){ + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is source, and fills in stream spec using last connection source.") + connection->m_sourceStreamDescription = m_lastConnectionIn->m_sourceStreamDescription; + connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_lastConnectionIn->m_sourceChannelLayoutSize); + memcpy(connection->m_sourceChannelLayout, m_lastConnectionIn->m_sourceChannelLayout, m_lastConnectionIn->m_sourceChannelLayoutSize); + connection->m_sourceChannelLayoutSize = m_lastConnectionIn->m_sourceChannelLayoutSize; + connection->m_hasSourceSpecification = true; + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << " __WARNING__: could not get stream specification...") + } + } else { + DEBUG_AUDIO_STREAM("AudioNode" << int(this) << "is sink, skips filling in stream.") + if (!connection->isSinkOnly()) + m_lastConnectionIn = connection; + } + return true; +} + +/** + Let timeProperty be one of e.g + {kAudioUnitProperty_Latency, kAudioUnitProperty_TailTime, + kAudioOutputUnitProperty_StartTime, kAudioUnitProperty_CurrentPlayTime} +*/ +Float64 AudioNode::getTimeInSamples(int timeProperty) +{ + if (!m_audioUnit) + return 0; + + AudioTimeStamp timeStamp; + UInt32 size = sizeof(timeStamp); + memset(&timeStamp, 0, sizeof(timeStamp)); + OSStatus err = AudioUnitGetProperty(m_audioUnit, + timeProperty, kAudioUnitScope_Global, + 0, &timeStamp, &size); + if (err != noErr) + return 0; + return timeStamp.mSampleTime; +} + +void AudioNode::notify(const MediaNodeEvent *event) +{ + switch(event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + setGraph(0); + break; + case MediaNodeEvent::NewAudioGraph: + setGraph(static_cast<AudioGraph *>(event->data())); + break; + default: + break; + } + + mediaNodeEvent(event); +} + +void AudioNode::mediaNodeEvent(const MediaNodeEvent */*event*/) +{ + // Override if needed +} + +void AudioNode::initializeAudioUnit() +{ + // Override if needed. +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiooutput.h b/src/3rdparty/phonon/qt7/audiooutput.h new file mode 100644 index 0000000..c4a0526 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiooutput.h @@ -0,0 +1,88 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOOUTPUT_H +#define Phonon_QT7_AUDIOOUTPUT_H + +#include <QtCore/QObject> +#include <phonon/audiooutputinterface.h> +#include <phonon/abstractaudiooutput.h> + +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioOutputAudioPart : public QObject, AudioNode + { + Q_OBJECT + public: + AudioOutputAudioPart(); + + void setVolume(float volume); + float volume(); + + protected: + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + + signals: + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + + private: + friend class AudioOutput; + qreal m_volume; + AudioDeviceID m_audioDevice; + void setAudioDevice(AudioDeviceID device); + }; + + class AudioOutput : public MediaNode, public AudioOutputInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::AudioOutputInterface) + + public: + AudioOutput(QObject *parent = 0); + ~AudioOutput(); + + qreal volume() const; + void setVolume(qreal); + int outputDevice() const; + bool setOutputDevice(int); + + signals: + void volumeChanged(qreal newVolume); + void audioDeviceFailed(); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + + private: + AudioOutputAudioPart *m_audioOutput; + int m_device; + bool m_redirectToMovie; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_AUDIOOUTPUT_H diff --git a/src/3rdparty/phonon/qt7/audiooutput.mm b/src/3rdparty/phonon/qt7/audiooutput.mm new file mode 100644 index 0000000..38066d5 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiooutput.mm @@ -0,0 +1,168 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiooutput.h" +#include "audiograph.h" +#include "audiodevice.h" +#include "mediaobject.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioOutputAudioPart::AudioOutputAudioPart() : AudioNode(1, 0) +{ + m_audioDevice = AudioDevice::defaultDevice(AudioDevice::Out); + m_volume = 1; +} + +ComponentDescription AudioOutputAudioPart::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Output; + description.componentSubType = kAudioUnitSubType_DefaultOutput; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void AudioOutputAudioPart::initializeAudioUnit() +{ + setAudioDevice(m_audioDevice); + setVolume(m_volume); +} + +void AudioOutputAudioPart::setAudioDevice(AudioDeviceID device) +{ + m_audioDevice = device; + if (!m_audioDevice) + return; + if (!m_audioUnit) + return; + bool ok = AudioDevice::setDevice(m_audioUnit, m_audioDevice, AudioDevice::Out); + if (!ok) + emit audioDeviceFailed(); +} + +void AudioOutputAudioPart::setVolume(float volume) +{ + if (volume < 0) + m_volume = 0; + if (volume > 1) + m_volume = 1; + else + m_volume = volume; + + if (m_audioUnit){ + float db = volume;//20.0 * log10(volume); // convert to db + OSStatus err = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Input, 0, db, 0); + BACKEND_ASSERT2(err == noErr, "Could not set volume on output audio unit.", FATAL_ERROR) + emit volumeChanged(qreal(db)); + } +} + +float AudioOutputAudioPart::volume() +{ + return m_volume; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +AudioOutput::AudioOutput(QObject *parent) : MediaNode(AudioSink, parent) +{ + m_audioOutput = new AudioOutputAudioPart(); + setAudioNode(m_audioOutput); + connect(m_audioOutput, SIGNAL(volumeChanged(qreal)), this, SIGNAL(volumeChanged(qreal))); + connect(m_audioOutput, SIGNAL(audioDeviceFailed()), this, SIGNAL(audioDeviceFailed())); + m_redirectToMovie = false; +} + +AudioOutput::~AudioOutput() +{ +} + +void AudioOutput::setVolume(qreal volume) +{ + IMPLEMENTED; + m_audioOutput->setVolume(float(volume)); + if (m_owningMediaObject) + m_owningMediaObject->setVolumeOnMovie(volume); + + emit volumeChanged(m_audioOutput->volume()); +} + +qreal AudioOutput::volume() const +{ + IMPLEMENTED; + return qreal(m_audioOutput->volume()); +} + +bool AudioOutput::setOutputDevice(int device) +{ + IMPLEMENTED; + if (device == -1) + return false; + + if (m_owningMediaObject){ + bool ok = m_owningMediaObject->setAudioDeviceOnMovie(device); + if (!ok) + return false; + } + + if (m_audioGraph){ + MediaNodeEvent event1(MediaNodeEvent::AboutToRestartAudioStream, this); + m_audioGraph->notify(&event1); + } + + m_audioOutput->setAudioDevice(device); + + if (m_audioGraph){ + MediaNodeEvent event2(MediaNodeEvent::RestartAudioStreamRequest, this); + m_audioGraph->notify(&event2); + } + return true; +} + +int AudioOutput::outputDevice() const +{ + IMPLEMENTED; + return m_audioOutput->m_audioDevice; +} + +void AudioOutput::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::SetMediaObject: + if (static_cast<MediaObject *>(event->data())){ + setVolume(volume()); + setOutputDevice(outputDevice()); + } + break; + default: + break; + } +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_audiooutput.cpp" diff --git a/src/3rdparty/phonon/qt7/audiopartoutput.h b/src/3rdparty/phonon/qt7/audiopartoutput.h new file mode 100644 index 0000000..0ccdfb6 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiopartoutput.h @@ -0,0 +1,47 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOPARTOUTPUT_H +#define Phonon_QT7_AUDIOPARTOUTPUT_H + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include "audionode.h" +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioPartOutput : public AudioNode + { + public: + AudioPartOutput(); + virtual ~AudioPartOutput(); + + private: + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(AudioNode *source); + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOPARTOUTPUT_H diff --git a/src/3rdparty/phonon/qt7/audiopartoutput.mm b/src/3rdparty/phonon/qt7/audiopartoutput.mm new file mode 100644 index 0000000..b985e69 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiopartoutput.mm @@ -0,0 +1,69 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiopartoutput.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioPartOutput::AudioPartOutput() + : AudioNode() +{ +} + +AudioPartOutput::~AudioPartOutput() +{ +} + +ComponentDescription AudioPartOutput::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Output; + description.componentSubType = kAudioUnitSubType_DefaultOutput; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void AudioPartOutput::initializeAudioUnit(AudioNode *source) +{ + m_audioStreamDescription = source->outputStreamDescription(); + m_audioChannelLayout = source->outputChannelLayout(); + m_audioChannelLayoutSize = source->outputChannelLayoutSize(); + + // Specify the stream format: + OSStatus err; + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, m_audioStreamDescription, sizeof(AudioStreamBasicDescription)); + BACKEND_ASSERT2(err == noErr, "Could not set stream format on audio output unit.", FATAL_ERROR) + + // Set the channel layout: + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, + 0, m_audioChannelLayout, m_audioChannelLayoutSize); + BACKEND_ASSERT2(err == noErr, "Could not set channel layout on audio output unit.", FATAL_ERROR) +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/audiosplitter.h b/src/3rdparty/phonon/qt7/audiosplitter.h new file mode 100644 index 0000000..ed6101f --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiosplitter.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_AUDIOSPLITTER_H +#define Phonon_QT7_AUDIOSPLITTER_H + +#include <QtCore/QFile> + +#include "medianode.h" +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioNodeSplitter : public AudioNode + { + public: + AudioNodeSplitter(); + ComponentDescription getAudioNodeDescription() const; + }; + + class AudioSplitter : public MediaNode + { + public: + AudioSplitter(QObject *parent = 0); + ~AudioSplitter(); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_AUDIOSPLITTER_H diff --git a/src/3rdparty/phonon/qt7/audiosplitter.mm b/src/3rdparty/phonon/qt7/audiosplitter.mm new file mode 100644 index 0000000..685ea12 --- /dev/null +++ b/src/3rdparty/phonon/qt7/audiosplitter.mm @@ -0,0 +1,52 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "audiosplitter.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +AudioNodeSplitter::AudioNodeSplitter() : AudioNode(1, 2) +{ +} + +ComponentDescription AudioNodeSplitter::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_FormatConverter; + description.componentSubType = kAudioUnitSubType_Splitter; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +AudioSplitter::AudioSplitter(QObject *parent) : MediaNode(AudioSink | AudioSource, new AudioNodeSplitter(), parent) +{ +} + +AudioSplitter::~AudioSplitter() +{ +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/backend.h b/src/3rdparty/phonon/qt7/backend.h new file mode 100644 index 0000000..287fcec --- /dev/null +++ b/src/3rdparty/phonon/qt7/backend.h @@ -0,0 +1,61 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_BACKEND_H +#define Phonon_QT7_BACKEND_H + +#include <QtCore/QList> +#include <QtCore/QPointer> +#include <QtCore/QStringList> +#include <phonon/backendinterface.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class Backend : public QObject, public BackendInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::BackendInterface) + + public: + Backend(); + Backend(QObject *parent, const QStringList &args); + virtual ~Backend(); + + QObject *createObject(BackendInterface::Class, QObject *parent, const QList<QVariant> &args); + QStringList availableMimeTypes() const; + QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const; + QHash<QByteArray, QVariant> objectDescriptionProperties(ObjectDescriptionType type, int index) const; + + bool startConnectionChange(QSet<QObject *> nodes); + bool connectNodes(QObject *source, QObject *sink); + bool disconnectNodes(QObject *source, QObject *sink); + bool endConnectionChange(QSet<QObject *> nodes); + + Q_SIGNALS: + void objectDescriptionChanged(ObjectDescriptionType); + + private: + bool quickTime7Available(); + }; +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_BACKEND_H diff --git a/src/3rdparty/phonon/qt7/backend.mm b/src/3rdparty/phonon/qt7/backend.mm new file mode 100644 index 0000000..b3ca106 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backend.mm @@ -0,0 +1,276 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "backend.h" +#include <QtCore/QDebug> +#include <QtCore/QSet> +#include <QtCore/QVariant> +#include <QtCore/QtPlugin> + +#include "backendheader.h" + +#include "videowidget.h" +#include "audiooutput.h" +#include "mediaobject.h" +#include "videoeffect.h" +#include "medianode.h" +#include "audiodevice.h" +#include "audiomixer.h" +#include "backendinfo.h" +#include "quicktimeaudioplayer.h" + +#include "audiograph.h" +#include "audiomixer.h" +#include "audiooutput.h" +#include "audiosplitter.h" +#include "audioeffects.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +Backend::Backend() +{ + IMPLEMENTED << "Creating backend QT7"; +} + +Backend::Backend(QObject *parent, const QStringList &) : QObject(parent) +{ + IMPLEMENTED << "Creating backend QT7"; + setProperty("identifier", QLatin1String("Mac OS X/QuickTime7")); + setProperty("backendName", QLatin1String("Mac OS X/QuickTime7")); + setProperty("backendComment", QLatin1String("Developed by Trolltech")); + setProperty("backendVersion", QLatin1String("0.1")); + setProperty("backendIcon", QLatin1String("")); + setProperty("backendWebsite", QLatin1String("http://qt.nokia.com/")); +} + +Backend::~Backend() +{ +} + +bool Backend::quickTime7Available() +{ + static bool ok = BackendInfo::isQuickTimeVersionAvailable(0x0700); + if (!ok){ + static bool messageWritten = false; + if (!messageWritten && qgetenv("PHONON_DEBUG") == "1"){ + messageWritten = true; + QString str("WARNING: Phonon backend plugin need QuickTime 7 or newer to work."); + str += " This computer has version " + + BackendInfo::quickTimeVersionString() + + " installed."; + qWarning(str.toAscii().data()); + } + return false; + } + return true; +} + +QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) +{ + if (!quickTime7Available()) + return 0; + + switch (c) { + case MediaObjectClass: + IMPLEMENTED << "Creating new MediaObjectClass."; + return new MediaObject(parent); + break; + case VolumeFaderEffectClass: + IMPLEMENTED << "Creating new VolumeFaderEffectClass."; + return new AudioMixer(parent); + break; + case AudioOutputClass: + IMPLEMENTED << "Creating new AudioOutputClass."; + return new AudioOutput(parent); + break; + case AudioDataOutputClass: + NOT_IMPLEMENTED << "Creating new AudioDataOutputClass."; + break; + case VisualizationClass: + NOT_IMPLEMENTED << "Creating new VisualizationClass."; + break; + case VideoDataOutputClass: + NOT_IMPLEMENTED << "Creating new VideoDataOutputClass."; + break; + case EffectClass: + IMPLEMENTED << "Creating new EffectClass."; + return new AudioEffect(args[0].toInt()); + break; + case VideoWidgetClass: + IMPLEMENTED << "Creating new VideoWidget."; + return new VideoWidget(parent); + break; + default: + return 0; + } + return 0; +} + +bool Backend::startConnectionChange(QSet<QObject *> objects) +{ + IMPLEMENTED; + QList<AudioGraph *> notifiedGraphs; + for (int i=0; i<objects.size(); i++){ + MediaNode *node = qobject_cast<MediaNode*>(objects.values()[i]); + if (node && node->m_audioGraph && !notifiedGraphs.contains(node->m_audioGraph)){ + MediaNodeEvent event(MediaNodeEvent::StartConnectionChange); + node->m_audioGraph->notify(&event); + notifiedGraphs << node->m_audioGraph; + } + } + return true; +} + +bool Backend::endConnectionChange(QSet<QObject *> objects) +{ + IMPLEMENTED; + QList<AudioGraph *> notifiedGraphs; + for (int i=0; i<objects.size(); i++){ + MediaNode *node = qobject_cast<MediaNode*>(objects.values()[i]); + if (node && node->m_audioGraph && !notifiedGraphs.contains(node->m_audioGraph)){ + MediaNodeEvent event(MediaNodeEvent::EndConnectionChange); + node->m_audioGraph->notify(&event); + notifiedGraphs << node->m_audioGraph; + } + } + return true; +} + +bool Backend::connectNodes(QObject *aSource, QObject *aSink) +{ + IMPLEMENTED; + MediaNode *source = qobject_cast<MediaNode*>(aSource); + if (!source) return false; + MediaNode *sink = qobject_cast<MediaNode*>(aSink); + if (!sink) return false; + + return source->connectToSink(sink); +} + + +bool Backend::disconnectNodes(QObject *aSource, QObject *aSink) +{ + IMPLEMENTED; + MediaNode *source = qobject_cast<MediaNode*>(aSource); + if (!source) return false; + MediaNode *sink = qobject_cast<MediaNode*>(aSink); + if (!sink) return false; + + return source->disconnectToSink(sink); +} + + +QStringList Backend::availableMimeTypes() const +{ + IMPLEMENTED; + return BackendInfo::quickTimeMimeTypes(BackendInfo::In); +} + +/** +* Returns a set of indexes that acts as identifiers for the various properties +* this backend supports for the given ObjectDescriptionType. +* More information for a given property/index can be +* looked up in Backend::objectDescriptionProperties(...). +*/ +QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const +{ + QList<int> ret; + + switch (type){ + case AudioOutputDeviceType:{ + IMPLEMENTED_SILENT << "Creating index set for type: AudioOutputDeviceType"; + QList<AudioDeviceID> devices = AudioDevice::devices(AudioDevice::Out); + for (int i=0; i<devices.size(); i++) + ret << int(devices[i]); + break; } + case EffectType:{ + IMPLEMENTED_SILENT << "Creating index set for type: EffectType"; + if (QuickTimeAudioPlayer::soundPlayerIsAwailable()) + ret = AudioEffect::effectList(); + break; } + +#if 0 // will be awailable in a later version of phonon. + case AudioCaptureDeviceType:{ + IMPLEMENTED_SILENT << "Creating index set for type: AudioCaptureDeviceType"; + QList<AudioDeviceID> devices = AudioDevice::devices(AudioDevice::In).keys(); + for (int i=0; i<devices.size(); i++) + ret <<int(devices[i]); + break; } + case VideoEffectType:{ + // Just count the number of filters awailable (c), and + // add return a set with the numbers 1..c inserted: + IMPLEMENTED_SILENT << "Creating index set for type: VideoEffectType"; + QList<QtCore/QString> filters = objc_getCiFilterInfo()->filterDisplayNames; + for (int i=0; i<filters.size(); i++) + ret << insert(i); + break; } +#endif + default: + NOT_IMPLEMENTED; + break; + } + return ret; +} + +QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const +{ + QHash<QByteArray, QVariant> ret; + + switch (type){ + case AudioOutputDeviceType:{ + IMPLEMENTED_SILENT << "Creating description hash for type: AudioOutputDeviceType"; + ret.insert("name", AudioDevice::deviceSourceNameElseDeviceName(index)); + ret.insert("description", AudioDevice::deviceNameElseDeviceSourceName(index)); + break; } + case EffectType:{ + AudioEffect e(index); + ret.insert("name", e.name()); + ret.insert("description", e.description()); + break; } + +#if 0 // will be awailable in a later version of phonon. + case VideoEffectType:{ + // Get list of effects, pick out filter at index, and return its name: + IMPLEMENTED_SILENT << "Creating description hash for type: VideoEffectType"; + QList<QtCore/QString> filters = objc_getCiFilterInfo()->filterDisplayNames; + ret.insert("name", filters[index]); + case AudioCaptureDeviceType:{ + IMPLEMENTED_SILENT << "Creating description hash for type: AudioCaptureDeviceType"; + QMap<AudioDeviceID, QString> devices = AudioDevice::devices(AudioDevice::In); + ret.insert("name", devices.value(index)); + break; } +#endif + default: + NOT_IMPLEMENTED; + break; + } + + return ret; +} + +Q_EXPORT_PLUGIN2(phonon_qt7, Backend) +}} + +QT_END_NAMESPACE + +#include "moc_backend.cpp" + diff --git a/src/3rdparty/phonon/qt7/backendheader.h b/src/3rdparty/phonon/qt7/backendheader.h new file mode 100644 index 0000000..fd0d892 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendheader.h @@ -0,0 +1,184 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_BACKENDHEADER_H +#define Phonon_QT7_BACKENDHEADER_H + +#include <QtCore/QString> +#import <Foundation/NSAutoreleasePool.h> +#include <CoreFoundation/CFBase.h> + +#ifndef Q_WS_MAC64 +#define QUICKTIME_C_API_AVAILABLE +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +// Implemented in error.cpp: +void gSetErrorString(const QString &errorString); +QString gGetErrorString(); +void gSetErrorLocation(const QString &errorLocation); +void gSetErrorType(int type); +int gGetErrorType(); +void gClearError(); + +#define NO_ERROR 0 +#define NORMAL_ERROR 1 +#define FATAL_ERROR 2 + +#define ERROR_LOCATION \ + QLatin1String("Function: ") + QLatin1String(__FUNCTION__) \ + + QLatin1String(", File: ") + QLatin1String(__FILE__) \ + + QLatin1String(", Line: ") + QString::number(__LINE__) + +#define SET_ERROR(string, type){ \ + Phonon::QT7::gSetErrorString(string); \ + Phonon::QT7::gSetErrorType(type); \ + Phonon::QT7::gSetErrorLocation(ERROR_LOCATION); } + +#define BACKEND_ASSERT(test, string, type) \ + bool fail = !test; \ + if (fail) \ + SET_ERROR(QLatin1String(string), type) \ + if (fail) + +#define BACKEND_ASSERT2(test, string, type) \ + if (!(test)) { \ + SET_ERROR(QLatin1String(string), type) \ + return; \ + } + +#define BACKEND_ASSERT3(test, string, type, ret) \ + if (!(test)) { \ + SET_ERROR(QLatin1String(string), type) \ + return ret; \ + } + +#define ARGUMENT_UNSUPPORTED(a, x, type, ret) \ + if ((a) == (x)) { \ + SET_ERROR("Argument value not supported: "#a" == "#x, type); \ + return ret; \ + } + +#define CASE_UNSUPPORTED(string, type) SET_ERROR(string, type) + +#ifdef SET_DEBUG_IMPLEMENTED +#define IMPLEMENTED qDebug() << "QT7:" << __FUNCTION__ << "(" << __FILE__ << "):" +#else +#define IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_HALF_IMPLEMENTED +#define HALF_IMPLEMENTED qDebug() << "QT7: --- HALF IMPLEMENTED:" << __FUNCTION__ << "(" << __FILE__ << "," << __LINE__ << "):" +#else +#define HALF_IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_NOT_IMPLEMENTED +#define NOT_IMPLEMENTED qDebug() << "QT7: *** NOT IMPLEMENTED:" << __FUNCTION__ << "(" << __FILE__ << "," << __LINE__ << "):" +#else +#define NOT_IMPLEMENTED if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_IMPLEMENTED_SILENT +#define IMPLEMENTED_SILENT qDebug() << "QT7: (silent)" << __FUNCTION__ << "(" << __FILE__ << "," << __LINE__ << "):" +#else +#define IMPLEMENTED_SILENT if (1); else qDebug() +#endif + +#ifdef SET_DEBUG_AUDIO_GRAPH +#define DEBUG_AUDIO_GRAPH(x) qDebug() << "QT7 DEBUG GRAPH:" << x; +#else +#define DEBUG_AUDIO_GRAPH(x) {} +#endif + +#ifdef SET_DEBUG_AUDIO_STREAM +#define DEBUG_AUDIO_STREAM(x) qDebug() << "QT7 DEBUG STREAM:" << x; +#else +#define DEBUG_AUDIO_STREAM(x) {} +#endif + +///////////////////////////////////////////////////////////////////////////////////////// + +class PhononAutoReleasePool +{ +private: + void *pool; +public: + PhononAutoReleasePool(); + ~PhononAutoReleasePool(); +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +template <typename T> +class PhononCFType +{ +public: + inline PhononCFType(const T &t = 0) : type(t) {} + inline PhononCFType(const PhononCFType &helper) : type(helper.type) { if (type) CFRetain(type); } + inline ~PhononCFType() { if (type) CFRelease(type); } + inline operator T() { return type; } + inline PhononCFType operator =(const PhononCFType &helper) + { + if (helper.type) + CFRetain(helper.type); + CFTypeRef type2 = type; + type = helper.type; + if (type2) + CFRelease(type2); + return *this; + } + inline T *operator&() { return &type; } + static PhononCFType constructFromGet(const T &t) + { + CFRetain(t); + return PhononCFType<T>(t); + } +protected: + T type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class PhononCFString : public PhononCFType<CFStringRef> +{ +public: + inline PhononCFString(const QString &str) : PhononCFType<CFStringRef>(0), string(str) {} + inline PhononCFString(const CFStringRef cfstr = 0) : PhononCFType<CFStringRef>(cfstr) {} + inline PhononCFString(const PhononCFType<CFStringRef> &other) : PhononCFType<CFStringRef>(other) {} + operator QString() const; + operator CFStringRef() const; + static QString toQString(CFStringRef cfstr); + static CFStringRef toCFStringRef(const QString &str); +private: + QString string; +}; +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#ifdef Q_CC_INTEL +#pragma warning (disable : 1899) // mute icc warning for the use of 4cc +#endif + +#endif // Phonon_QT7_BACKENDHEADER_H diff --git a/src/3rdparty/phonon/qt7/backendheader.mm b/src/3rdparty/phonon/qt7/backendheader.mm new file mode 100644 index 0000000..3a73389 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendheader.mm @@ -0,0 +1,127 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "backendheader.h" +#include <QtCore/QString> +#include <QtCore/QDebug> + +#include <CoreFoundation/CoreFoundation.h> +#include <QVarLengthArray> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +Q_GLOBAL_STATIC(QString, gErrorString) +int gErrorType = NO_ERROR; + +void gSetErrorString(const QString &errorString) +{ + if (qgetenv("PHONON_DEBUG") == "1"){ + qDebug() << "Error:" << errorString; + } + + if (!gErrorString()->isEmpty()) + return; // not yet caught. + + *gErrorString() = errorString; +} + +QString gGetErrorString() +{ + return *gErrorString(); +} + +void gSetErrorLocation(const QString &errorLocation) +{ + if (qgetenv("PHONON_DEBUG") == "1"){ + qDebug() << "Location:" << errorLocation; + } +} + +void gSetErrorType(int errorType) +{ + if (gErrorType != NO_ERROR) + return; // not yet caught. + gErrorType = errorType; +} + +int gGetErrorType() +{ + return gErrorType; +} + +void gClearError() +{ + gErrorString()->clear(); + gErrorType = NO_ERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +PhononAutoReleasePool::PhononAutoReleasePool() +{ + pool = (void*)[[NSAutoreleasePool alloc] init]; +} + +PhononAutoReleasePool::~PhononAutoReleasePool() +{ + [(NSAutoreleasePool*)pool release]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +QString PhononCFString::toQString(CFStringRef str) +{ + if(!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast<const QChar *>(chars), length); + + QVarLengthArray<UniChar> buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast<const QChar *>(buffer.constData()), length); +} + +PhononCFString::operator QString() const +{ + if (string.isEmpty() && type) + const_cast<PhononCFString*>(this)->string = toQString(type); + return string; +} + +CFStringRef PhononCFString::toCFStringRef(const QString &string) +{ + return CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar *>(string.unicode()), + string.length()); +} + +PhononCFString::operator CFStringRef() const +{ + if (!type) + const_cast<PhononCFString*>(this)->type = toCFStringRef(string); + return type; +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/backendinfo.h b/src/3rdparty/phonon/qt7/backendinfo.h new file mode 100644 index 0000000..c30cda3 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendinfo.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_BACKENDINFO_H +#define Phonon_QT7_BACKENDINFO_H + +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class BackendInfo + { + public: + enum Scope {In, Out}; + + static QString quickTimeVersionString(); + static bool isQuickTimeVersionAvailable(int minHexVersion); + static QStringList quickTimeMimeTypes(Scope scope); + static QStringList quickTimeCompressionFormats(); + static QStringList coreAudioCodecs(Scope scope); + static QStringList coreAudioFileTypes(Scope scope); + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_BACKENDINFO_H diff --git a/src/3rdparty/phonon/qt7/backendinfo.mm b/src/3rdparty/phonon/qt7/backendinfo.mm new file mode 100644 index 0000000..d84e014 --- /dev/null +++ b/src/3rdparty/phonon/qt7/backendinfo.mm @@ -0,0 +1,311 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#import <QTKit/QTMovie.h> +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +#include "backendinfo.h" +#include "backendheader.h" + +#include <AudioToolbox/AudioToolbox.h> +#include <AudioUnit/AudioUnit.h> +#include <CoreServices/CoreServices.h> + + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QString BackendInfo::quickTimeVersionString() +{ + SInt32 version; + OSStatus err = Gestalt(gestaltQuickTimeVersion, &version); + if (err != noErr) + return QString("00.0.0.0000"); + QString versionString = QString("%1%2.%3.%4.%5%6%7%8") + .arg((version >> (7*4)) & 0xF) + .arg((version >> (6*4)) & 0xF) + .arg((version >> (5*4)) & 0xF) + .arg((version >> (4*4)) & 0xF) + .arg((version >> (3*4)) & 0xF) + .arg((version >> (2*4)) & 0xF) + .arg((version >> (1*4)) & 0xF) + .arg((version >> (0*4)) & 0xF); + return versionString; +} + +bool BackendInfo::isQuickTimeVersionAvailable(int minHexVersion) +{ + // minHexVersion == 0x0741 means version 7.4.1 + SInt32 qtHexVersion; + OSStatus err = Gestalt(gestaltQuickTimeVersion, &qtHexVersion); + return (err == noErr) ? ((qtHexVersion >> 16) >= minHexVersion) : 0; +} + +#ifdef QUICKTIME_C_API_AVAILABLE +static QString getMimeTypeTag(QTAtomContainer mimeList, int index, OSType type) +{ + QTAtom mimeAtom = QTFindChildByIndex(mimeList, kParentAtomIsContainer, type, index, 0); + char mimeCharArray[256]; + long length; + OSStatus err = QTCopyAtomDataToPtr(mimeList, mimeAtom, true, sizeof(mimeCharArray)-1, mimeCharArray, &length); + if (err == noErr) + return QString::fromAscii(mimeCharArray, length); + return QString(); +} +#endif // QUICKTIME_C_API_AVAILABLE + +#ifdef QUICKTIME_C_API_AVAILABLE +QStringList BackendInfo::quickTimeMimeTypes(Scope scope) +{ + QStringList mimeTypes; + ARGUMENT_UNSUPPORTED(scope, Out, NORMAL_ERROR, mimeTypes) + + ComponentDescription description; + description.componentType = MovieImportType; + description.componentSubType = 0; + description.componentManufacturer = 0; + description.componentFlags = hasMovieImportMIMEList | canMovieImportFiles; + description.componentFlagsMask = canMovieImportFiles | movieImportSubTypeIsFileExtension | hasMovieImportMIMEList; + Component component = FindNextComponent(0, &description); + + while (component) { + QTAtomContainer mimeList = 0; + OSStatus err = MovieImportGetMIMETypeList((MovieImportComponent)component, &mimeList); + if (err == noErr){ + int count = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, 0); + for (int i=1; i<=count; ++i){ + QString mimeType = getMimeTypeTag(mimeList, i, kMimeInfoMimeTypeTag); + if (mimeType.startsWith(QLatin1String("audio")) || mimeType.startsWith(QLatin1String("video"))){ + if (err == noErr && !mimeType.isEmpty()) + mimeTypes << mimeType; + } + } + } + QTDisposeAtomContainer(mimeList); + component = FindNextComponent(component, &description); + } + mimeTypes.sort(); + return mimeTypes; +} + +#else // QUICKTIME_C_API_AVAILABLE == false + +QString mimeForExtensionAudio(const QString &ext) +{ + if (ext == "3g2") return QLatin1String("audio/3g2"); + if (ext == "3gp") return QLatin1String("audio/3gp"); + if (ext == "aac") return QLatin1String("audio/aac"); + if (ext == "ac3") return QLatin1String("audio/ac3"); + if (ext == "aif") return QLatin1String("audio/aif"); + if (ext == "aifc") return QLatin1String("audio/aifc"); + if (ext == "aiff") return QLatin1String("audio/aiff"); + if (ext == "amr") return QLatin1String("audio/amr"); + if (ext == "au") return QLatin1String("audio/au"); + if (ext == "bwf") return QLatin1String("audio/bwf"); + if (ext == "caf") return QLatin1String("audio/caf"); + if (ext == "cdda") return QLatin1String("audio/cdda"); + if (ext == "gsm") return QLatin1String("audio/gsm"); + if (ext == "kar") return QLatin1String("audio/kar"); + if (ext == "m1a") return QLatin1String("audio/m1a"); + if (ext == "m1s") return QLatin1String("audio/m1s"); + if (ext == "m3u") return QLatin1String("audio/m3u"); + if (ext == "m3url") return QLatin1String("audio/m3url"); + if (ext == "mid") return QLatin1String("audio/mid"); + if (ext == "midi") return QLatin1String("audio/midi"); + if (ext == "mka") return QLatin1String("audio/mka"); + if (ext == "mp3") return QLatin1String("audio/mp3"); + if (ext == "mp4") return QLatin1String("audio/mp4"); + if (ext == "mpa") return QLatin1String("audio/mpa"); + if (ext == "mpeg") return QLatin1String("audio/mpeg"); + if (ext == "mpg") return QLatin1String("audio/mpg"); + if (ext == "mpg4") return QLatin1String("audio/mpg4"); + if (ext == "mpm") return QLatin1String("audio/mpm"); + if (ext == "qcp") return QLatin1String("audio/qcp"); + if (ext == "sd2") return QLatin1String("audio/sd2"); + if (ext == "smf") return QLatin1String("audio/smf"); + if (ext == "snd") return QLatin1String("audio/snd"); + if (ext == "ulw") return QLatin1String("audio/ulw"); + if (ext == "wav") return QLatin1String("audio/wav"); + if (ext == "wax") return QLatin1String("audio/wax"); + if (ext == "wma") return QLatin1String("audio/wma"); + return QString(); +} + +QString mimeForExtensionVideo(const QString &ext) +{ + if (ext == "3g2") return QLatin1String("video/3g2"); + if (ext == "3gp") return QLatin1String("video/3gp"); + if (ext == "asf") return QLatin1String("video/asf"); + if (ext == "asx") return QLatin1String("video/asx"); + if (ext == "avi") return QLatin1String("video/avi"); + if (ext == "dif") return QLatin1String("video/dif"); + if (ext == "dv") return QLatin1String("video/dv"); + if (ext == "flc") return QLatin1String("video/flc"); + if (ext == "fli") return QLatin1String("video/fli"); + if (ext == "m15") return QLatin1String("video/m15"); + if (ext == "m1a") return QLatin1String("video/m1a"); + if (ext == "m1s") return QLatin1String("video/m1s"); + if (ext == "m1v") return QLatin1String("video/m1v"); + if (ext == "m75") return QLatin1String("video/m75"); + if (ext == "mkv") return QLatin1String("video/mkv"); + if (ext == "mp4") return QLatin1String("video/mp4"); + if (ext == "mpa") return QLatin1String("video/mpa"); + if (ext == "mpeg") return QLatin1String("video/mpeg"); + if (ext == "mpg") return QLatin1String("video/mpg"); + if (ext == "mpg4") return QLatin1String("video/mpg4"); + if (ext == "mpm") return QLatin1String("video/mpm"); + if (ext == "mpv") return QLatin1String("video/mpv"); + if (ext == "vfw") return QLatin1String("video/vfw"); + if (ext == "wm") return QLatin1String("video/wm"); + if (ext == "wmv") return QLatin1String("video/wmv"); + if (ext == "wmx") return QLatin1String("video/wmx"); + if (ext == "wvx") return QLatin1String("video/wvx"); + return QString(); +} + +QStringList BackendInfo::quickTimeMimeTypes(Scope scope) +{ + QStringList mimeTypes; + QStringList fileExtensions; + ARGUMENT_UNSUPPORTED(scope, Out, NORMAL_ERROR, mimeTypes) + + PhononAutoReleasePool pool; + NSArray *fileTypes = [QTMovie movieFileTypes:QTIncludeAllTypes]; + for (NSString *type in fileTypes){ + QString formattedType = QString::fromUtf8([type UTF8String]); + formattedType = formattedType.remove('\'').remove('.').toLower(); + QString audioMime = mimeForExtensionAudio(formattedType); + QString videoMime = mimeForExtensionVideo(formattedType); + if (!audioMime.isEmpty()) + mimeTypes << audioMime; + if (!videoMime.isEmpty()) + mimeTypes << videoMime; + if (audioMime.isEmpty() && videoMime.isEmpty()) + fileExtensions << QLatin1String("application/x-qt-") + formattedType; + } + mimeTypes.sort(); + fileExtensions.sort(); + return mimeTypes + fileExtensions; +} +#endif // QUICKTIME_C_API_AVAILABLE + +QStringList BackendInfo::quickTimeCompressionFormats() +{ + QStringList result; + +#ifdef QUICKTIME_C_API_AVAILABLE + + ComponentInstance component = 0; + OSStatus err = OpenADefaultComponent(StandardCompressionType, StandardCompressionSubTypeAudio, &component); + BACKEND_ASSERT3(err == noErr, "Could not open component for retrieving awailable compression formats", NORMAL_ERROR, result) + + UInt32 size; + err = QTGetComponentPropertyInfo(component, kQTPropertyClass_SCAudio, kQTSCAudioPropertyID_AvailableCompressionFormatNamesList, 0, &size,0); + BACKEND_ASSERT3(err == noErr, "Could not get awailable compression formats", NORMAL_ERROR, result) + + CFArrayRef formats[size]; + err = QTGetComponentProperty(component, kQTPropertyClass_SCAudio, kQTSCAudioPropertyID_AvailableCompressionFormatNamesList, size, &formats, &size); + BACKEND_ASSERT3(err == noErr, "Could not get awailable compression formats", NORMAL_ERROR, result) + + CFIndex count = CFArrayGetCount(*formats); + for (CFIndex i=0; i<count; ++i){ + const CFStringRef name = (const struct __CFString *) CFArrayGetValueAtIndex(*formats, i); + result << PhononCFString::toQString(name); + } + +#endif // QUICKTIME_C_API_AVAILABLE + return result; +} + + +QStringList BackendInfo::coreAudioCodecs(Scope scope) +{ + QStringList result; + UInt32 size; + OSStatus err; + OSType *formatIDs; + + OSType encodersOrDecoders = (scope == In) + ? kAudioFormatProperty_EncodeFormatIDs : kAudioFormatProperty_DecodeFormatIDs; + + err = AudioFormatGetPropertyInfo(encodersOrDecoders, 0, NULL, &size); + BACKEND_ASSERT3(err == noErr, "Could not get awailable decoders/encoders", NORMAL_ERROR, result) + + formatIDs = (OSType*)malloc(size); + UInt32 numFormats = size / sizeof(OSType); + err = AudioFormatGetProperty(encodersOrDecoders, 0, NULL, &size, formatIDs); + BACKEND_ASSERT(err == noErr, "Could not get awailable decoders/encoders", NORMAL_ERROR){ + free(formatIDs); + return result; + } + + for (UInt32 i=0; i<numFormats; ++i){ + AudioStreamBasicDescription absd; + memset(&absd, 0, sizeof(absd)); + absd.mFormatID = formatIDs[i]; + + CFStringRef name; + size = sizeof(CFStringRef); + err = AudioFormatGetProperty(kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name); + BACKEND_ASSERT(err == noErr, "Could not get awailable decoder/encoder names", NORMAL_ERROR){ + free(formatIDs); + return result; + } + result << PhononCFString::toQString(name); + } + free(formatIDs); + return result; +} + +QStringList BackendInfo::coreAudioFileTypes(Scope scope) +{ + QStringList result; + OSStatus err; + UInt32 propertySize; + + OSType readOrwrite = (scope == In) + ? kAudioFileGlobalInfo_ReadableTypes : kAudioFileGlobalInfo_WritableTypes; + + err = AudioFileGetGlobalInfoSize(readOrwrite, 0, NULL, &propertySize); + BACKEND_ASSERT3(err == noErr, "Could not get core audio file types", NORMAL_ERROR, result) + + OSType *types = (OSType*)malloc(propertySize); + err = AudioFileGetGlobalInfo(readOrwrite, 0, NULL, &propertySize, types); + BACKEND_ASSERT3(err == noErr, "Could not get core audio file types", NORMAL_ERROR, result) + + UInt32 numTypes = propertySize / sizeof(OSType); + for (UInt32 i=0; i<numTypes; ++i){ + CFStringRef name; + UInt32 outSize = sizeof(name); + err = AudioFileGetGlobalInfo(kAudioFileGlobalInfo_FileTypeName, sizeof(OSType), types+i, &outSize, &name); + BACKEND_ASSERT3(err == noErr, "Could not get core audio file type names", NORMAL_ERROR, result) + result << PhononCFString::toQString(name); + } + return result; +} + +}} + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/qt7/lgpl-2.1.txt b/src/3rdparty/phonon/qt7/lgpl-2.1.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/src/3rdparty/phonon/qt7/lgpl-2.1.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/3rdparty/phonon/qt7/lgpl-3.txt b/src/3rdparty/phonon/qt7/lgpl-3.txt new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/src/3rdparty/phonon/qt7/lgpl-3.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/src/3rdparty/phonon/qt7/medianode.h b/src/3rdparty/phonon/qt7/medianode.h new file mode 100644 index 0000000..595e4da --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianode.h @@ -0,0 +1,85 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MediaNode_H +#define Phonon_QT7_MediaNode_H + +#include <QtCore/QObject> +#include "backendheader.h" +#include "medianodeevent.h" +#include "audioconnection.h" +#include "videoframe.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioNode; + class AudioGraph; + class MediaObject; + class AudioConnection; + + class MediaNode : public QObject + { + Q_OBJECT + + public: + enum NodeDescriptionEnum { + AudioSource = 1, + AudioSink = 2, + VideoSource = 4, + VideoSink = 8, + AudioGraphNode = 16 + }; + Q_DECLARE_FLAGS(NodeDescription, NodeDescriptionEnum); + + MediaNode(NodeDescription description, QObject *parent); + MediaNode(NodeDescription description, AudioNode *audioPart, QObject *parent); + virtual ~MediaNode(); + + void setAudioNode(AudioNode *audioPart); + bool connectToSink(MediaNode *sink); + bool disconnectToSink(MediaNode *sink); + AudioConnection *getAudioConnectionToSink(MediaNode *sink); + + void notify(const MediaNodeEvent *event, bool propagate = true); + void sendEventToSinks(const MediaNodeEvent *event); + virtual void mediaNodeEvent(const MediaNodeEvent *event); + + virtual void updateVideo(VideoFrame &frame); + AudioGraph *m_audioGraph; + + AudioNode *m_audioNode; + QList<AudioConnection *> m_audioSinkList; + QList<AudioConnection *> m_audioSourceList; + QList<MediaNode *> m_videoSinkList; + + int availableAudioInputBus(); + int availableAudioOutputBus(); + + NodeDescription m_description; + MediaObject *m_owningMediaObject; + }; + + Q_DECLARE_OPERATORS_FOR_FLAGS(MediaNode::NodeDescription); + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_MediaNode_H diff --git a/src/3rdparty/phonon/qt7/medianode.mm b/src/3rdparty/phonon/qt7/medianode.mm new file mode 100644 index 0000000..00f8340 --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianode.mm @@ -0,0 +1,261 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "medianode.h" +#include "audiograph.h" +#include "audionode.h" +#include "backendheader.h" + +#include "mediaobject.h" +#include "audiooutput.h" +#include "quicktimevideoplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaNode::MediaNode(NodeDescription description, QObject *parent) + : QObject(parent), m_audioGraph(0), m_audioNode(0), m_description(description), m_owningMediaObject(0) +{ +} + +MediaNode::MediaNode(NodeDescription description, AudioNode *audioPart, QObject *parent) + : QObject(parent), m_audioGraph(0), m_audioNode(audioPart), m_description(description) +{ +} + +void MediaNode::setAudioNode(AudioNode *audioPart) +{ + if (m_audioNode) + delete m_audioNode; + m_audioNode = audioPart; +} + +MediaNode::~MediaNode() +{ + delete m_audioNode; + qDeleteAll(m_audioSinkList); +} + +AudioConnection *MediaNode::getAudioConnectionToSink(MediaNode *sink) +{ + AudioConnection *connection = 0; + for (int i=0; i<m_audioSinkList.size(); ++i){ + if (m_audioSinkList[i]->isBetween(this, sink)){ + connection = m_audioSinkList[i]; + break; + } + } + return connection; +} + +bool MediaNode::connectToSink(MediaNode *sink) +{ + if ((m_description & AudioSource) && (sink->m_description & AudioSink)){ + // Check that they don't belong to different graphs. If they do, but + // sink is not connected to any source, accept it: + if (m_owningMediaObject && sink->m_owningMediaObject + && m_owningMediaObject != sink->m_owningMediaObject + && !sink->m_audioSourceList.isEmpty()){ + return false; + } + + // Check that the connection doesn't already exists: + AudioConnection *connection = getAudioConnectionToSink(sink); + if (connection) + return true; + + // Check that there are awailable input/output busses: + int inputBus = sink->availableAudioInputBus(); + int outputBus = availableAudioOutputBus(); + if (inputBus >= sink->m_audioNode->m_maxInputBusses || outputBus >= m_audioNode->m_maxOutputBusses) + return false; + + // All OK. Create connection: + connection = new AudioConnection(this, outputBus, sink, inputBus); + m_audioSinkList << connection; + sink->m_audioSourceList << connection; + + if (m_audioNode->m_audioGraph) + m_audioNode->m_audioGraph->connectLate(connection); + + MediaNodeEvent event1(MediaNodeEvent::AudioSinkAdded, connection); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::AudioSourceAdded, connection); + sink->notify(&event2, false); + return true; + } + + if ((m_description & VideoSource) && (sink->m_description & VideoSink)){ + // Check that the connection doesn't already exists: + if (m_videoSinkList.contains(sink)) + return true; + + m_videoSinkList << sink; + MediaNodeEvent event1(MediaNodeEvent::VideoSinkAdded, sink); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::VideoSourceAdded, this); + sink->notify(&event2, false); + return true; + } + + return false; +} + +bool MediaNode::disconnectToSink(MediaNode *sink) +{ + if ((m_description & AudioSource) && (sink->m_description & AudioSink)){ + AudioConnection *connection = getAudioConnectionToSink(sink); + if (!connection) + return false; + + m_audioSinkList.removeOne(connection); + sink->m_audioSourceList.removeOne(connection); + + if (m_audioNode->m_audioGraph) + m_audioNode->m_audioGraph->disconnectLate(connection); + + MediaNodeEvent event1(MediaNodeEvent::AudioSinkRemoved, connection); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::AudioSourceRemoved, connection); + sink->notify(&event2, false); + + delete connection; + return true; + } + + if ((m_description & VideoSource) && (sink->m_description & VideoSink)){ + m_videoSinkList.removeOne(sink); + + MediaNodeEvent event1(MediaNodeEvent::VideoSinkRemoved, sink); + notify(&event1, false); + MediaNodeEvent event2(MediaNodeEvent::VideoSourceRemoved, this); + sink->notify(&event2, false); + return true; + } + + return false; +} + +int MediaNode::availableAudioInputBus() +{ + // Scan through all the connection <u>in</u> to this + // node, and find an awailable index: + int index = -1; + bool available = false; + + while (!available){ + ++index; + available = true; + for (int i=0; i<m_audioSourceList.size(); ++i){ + if (m_audioSourceList[i]->m_sinkInputBus == index){ + available = false; + break; + } + } + } + return index; +} + +int MediaNode::availableAudioOutputBus() +{ + // Scan through all the connection <u>out</u> from this + // node, and find an awailable index: + int bus = -1; + bool available = false; + + while (!available){ + ++bus; + available = true; + for (int i=0; i<m_audioSinkList.size(); ++i){ + if (m_audioSinkList[i]->m_sourceOutputBus == bus){ + available = false; + break; + } + } + } + return bus; +} + +void MediaNode::notify(const MediaNodeEvent *event, bool propagate) +{ + // Let subclass handle the event first: + mediaNodeEvent(event); + + switch(event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + if (m_audioNode){ + foreach(AudioConnection *connection, m_audioSinkList) + connection->invalidate(); + } + break; + case MediaNodeEvent::NewAudioGraph: + m_audioGraph = static_cast<AudioGraph *>(event->data()); + break; + case MediaNodeEvent::AudioSinkAdded: + case MediaNodeEvent::VideoSinkAdded: + if (m_owningMediaObject){ + MediaNodeEvent e1(MediaNodeEvent::SetMediaObject, m_owningMediaObject); + sendEventToSinks(&e1); + QRect videoRect = m_owningMediaObject->videoPlayer()->videoRect(); + MediaNodeEvent e2(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + sendEventToSinks(&e2); + } + break; + case MediaNodeEvent::SetMediaObject: + m_owningMediaObject = static_cast<MediaObject *>(event->data()); + break; + default: + break; + } + + // Inform the audio node as well: + if (m_audioNode) + m_audioNode->notify(event); + + // And perhaps the sinks: + if (propagate) + sendEventToSinks(event); +} + +void MediaNode::sendEventToSinks(const MediaNodeEvent *event) +{ + for (int i=0; i<m_audioSinkList.size(); ++i) + m_audioSinkList[i]->m_sink->notify(event); + for (int i=0; i<m_videoSinkList.size(); ++i) + m_videoSinkList[i]->notify(event); +} + +void MediaNode::updateVideo(VideoFrame &frame){ + for (int i=0; i<m_videoSinkList.size(); ++i) + m_videoSinkList[i]->updateVideo(frame); +} + +void MediaNode::mediaNodeEvent(const MediaNodeEvent */*event*/) +{ + // Override if needed. +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_medianode.cpp" + diff --git a/src/3rdparty/phonon/qt7/medianodeevent.h b/src/3rdparty/phonon/qt7/medianodeevent.h new file mode 100644 index 0000000..ad6e212 --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodeevent.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIANODEEVENT_H +#define Phonon_QT7_MEDIANODEEVENT_H + +#include <QtCore/qnamespace.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + + class MediaNodeEvent + { + public: + enum Type { + AudioGraphAboutToBeDeleted, + NewAudioGraph, + AudioGraphInitialized, + AudioGraphCannotPlay, + AboutToRestartAudioStream, + RestartAudioStreamRequest, + VideoSinkAdded, + VideoSinkRemoved, + AudioSinkAdded, + AudioSinkRemoved, + VideoSourceAdded, + VideoSourceRemoved, + AudioSourceAdded, + AudioSourceRemoved, + VideoOutputCountChanged, + VideoFrameSizeChanged, + SetMediaObject, + StartConnectionChange, + EndConnectionChange, + MediaPlaying + }; + + MediaNodeEvent(Type type, void *data = 0); + virtual ~MediaNodeEvent(); + inline Type type() const{ return eventType; }; + inline void* data() const { return eventData; }; + + private: + Type eventType; + void *eventData; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_MEDIANODEEVENT_H diff --git a/src/3rdparty/phonon/qt7/medianodeevent.mm b/src/3rdparty/phonon/qt7/medianodeevent.mm new file mode 100644 index 0000000..664fbbc --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodeevent.mm @@ -0,0 +1,37 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "medianodeevent.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaNodeEvent::MediaNodeEvent(Type type, void *data) : eventType(type), eventData(data) +{ +} + +MediaNodeEvent::~MediaNodeEvent() +{ +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/medianodevideopart.h b/src/3rdparty/phonon/qt7/medianodevideopart.h new file mode 100644 index 0000000..fb1f89c --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodevideopart.h @@ -0,0 +1,42 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIANODEVIDEOPART_H +#define Phonon_QT7_MEDIANODEVIDEOPART_H + +#include <QtCore/qnamespace.h> +#include "backendheader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class MediaNodeVideoPart + { + public: + MediaNodeVideoPart(); + virtual ~MediaNodeVideoPart(); + virtual void updateVideo(void *ciImage) = 0; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_MEDIANODEVIDEOPART_H diff --git a/src/3rdparty/phonon/qt7/medianodevideopart.mm b/src/3rdparty/phonon/qt7/medianodevideopart.mm new file mode 100644 index 0000000..da060da --- /dev/null +++ b/src/3rdparty/phonon/qt7/medianodevideopart.mm @@ -0,0 +1,37 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "medianodevideopart.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaNodeVideoPart::MediaNodeVideoPart() +{ +} + +MediaNodeVideoPart::~MediaNodeVideoPart() +{ +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/mediaobject.h b/src/3rdparty/phonon/qt7/mediaobject.h new file mode 100644 index 0000000..27949ec --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobject.h @@ -0,0 +1,171 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIAOBJECT_H +#define Phonon_QT7_MEDIAOBJECT_H + +#include <QtCore/QStringList> +#include <QtCore/QTime> +#include <phonon/mediaobjectinterface.h> +#include <phonon/addoninterface.h> + +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + class QuickTimeAudioPlayer; + class QuickTimeMetaData; + class AudioGraph; + class MediaObjectAudioNode; + + class MediaObject : public MediaNode, + public Phonon::MediaObjectInterface, public Phonon::AddonInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::MediaObjectInterface Phonon::AddonInterface) + + public: + MediaObject(QObject *parent); + ~MediaObject(); + + QStringList availableAudioStreams() const; + QStringList availableVideoStreams() const; + QStringList availableSubtitleStreams() const; + QString currentAudioStream(const QObject *audioPath) const; + QString currentVideoStream(const QObject *videoPath) const; + QString currentSubtitleStream(const QObject *videoPath) const; + + void setCurrentAudioStream(const QString &streamName,const QObject *audioPath); + void setCurrentVideoStream(const QString &streamName,const QObject *videoPath); + void setCurrentSubtitleStream(const QString &streamName,const QObject *videoPath); + + void play(); + void pause(); + void stop(); + void seek(qint64 milliseconds); + + qint32 tickInterval() const; + void setTickInterval(qint32 interval); + bool hasVideo() const; + bool isSeekable() const; + qint64 currentTime() const; + Phonon::State state() const; + + QString errorString() const; + Phonon::ErrorType errorType() const; + + qint64 totalTime() const; + MediaSource source() const; + void setSource(const MediaSource &); + void setNextSource(const MediaSource &source); + qint32 prefinishMark() const; + void setPrefinishMark(qint32); + qint32 transitionTime() const; + void setTransitionTime(qint32); + bool hasInterface(Interface interface) const; + QVariant interfaceCall(Interface interface, int command, const QList<QVariant> &arguments = QList<QVariant>()); + + QuickTimeVideoPlayer* videoPlayer() const; + QuickTimeAudioPlayer* audioPlayer() const; + + void setVolumeOnMovie(float volume); + bool setAudioDeviceOnMovie(int id); + + int videoOutputCount(); + + signals: + void stateChanged(Phonon::State,Phonon::State); + void tick(qint64); + void seekableChanged(bool); + void hasVideoChanged(bool); + void bufferStatus(int); + void finished(); + void aboutToFinish(); + void prefinishMarkReached(qint32); + void totalTimeChanged(qint64); + void metaDataChanged(QMultiMap<QString,QString>); + void currentSourceChanged(const MediaSource &newSource); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + bool event(QEvent *event); + + private: + enum AudioSystem {AS_Unset, AS_Video, AS_Graph, AS_Silent} m_audioSystem; + Phonon::State m_state; + + QuickTimeVideoPlayer *m_videoPlayer; + QuickTimeAudioPlayer *m_audioPlayer; + QuickTimeVideoPlayer *m_nextVideoPlayer; + QuickTimeAudioPlayer *m_nextAudioPlayer; + MediaObjectAudioNode *m_mediaObjectAudioNode; + QuickTimeMetaData *m_metaData; + + qint32 m_tickInterval; + qint32 m_transitionTime; + quint32 m_prefinishMark; + quint32 m_currentTime; + float m_percentageLoaded; + + int m_tickTimer; + int m_bufferTimer; + int m_rapidTimer; + + bool m_waitNextSwap; + int m_swapTimeLeft; + QTime m_swapTime; + + void synchAudioVideo(); + void updateCurrentTime(); + void swapCurrentWithNext(qint32 transitionTime); + bool setState(Phonon::State state); + void pause_internal(); + void play_internal(); + void setupAudioSystem(); + void updateTimer(int &timer, int interval); + void bufferAudioVideo(); + void updateRapidly(); + void updateCrossFade(); + void updateAudioBuffers(); + void updateLipSynch(int allowedOffset); + void updateVideoFrames(); + void updateBufferStatus(); + void setMute(bool mute); + void inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount); + void inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount); + void inspectGraph(); + bool isCrossFading(); + + QString m_errorString; + Phonon::ErrorType m_errorType; + bool checkForError(); + + int m_audioEffectCount; + int m_audioOutputCount; + int m_videoEffectCount; + int m_videoOutputCount; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_MEDIAOBJECT_H diff --git a/src/3rdparty/phonon/qt7/mediaobject.mm b/src/3rdparty/phonon/qt7/mediaobject.mm new file mode 100644 index 0000000..002c337 --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobject.mm @@ -0,0 +1,852 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <QtCore/QEvent> +#include "mediaobject.h" +#include "backendheader.h" +#include "videowidget.h" +#include "videoframe.h" +#include "audiooutput.h" +#include "quicktimevideoplayer.h" +#include "quicktimemetadata.h" +#include "audiograph.h" +#include "mediaobjectaudionode.h" +#include "quicktimeaudioplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaObject::MediaObject(QObject *parent) : MediaNode(AudioSource | VideoSource, parent) +{ + m_owningMediaObject = this; + m_state = Phonon::LoadingState; + + m_videoPlayer = new QuickTimeVideoPlayer(); + m_audioPlayer = new QuickTimeAudioPlayer(); + m_nextVideoPlayer = new QuickTimeVideoPlayer(); + m_nextAudioPlayer = new QuickTimeAudioPlayer(); + m_mediaObjectAudioNode = new MediaObjectAudioNode(m_audioPlayer, m_nextAudioPlayer); + setAudioNode(m_mediaObjectAudioNode); + + m_metaData = new QuickTimeMetaData(); + m_audioGraph = new AudioGraph(this); + + m_tickInterval = 0; + m_prefinishMark = 0; + m_currentTime = 0; + m_transitionTime = 0; + m_percentageLoaded = 0; + m_waitNextSwap = false; + m_audioEffectCount = 0; + m_audioOutputCount = 0; + m_videoEffectCount = 0; + m_videoOutputCount = 0; + m_audioSystem = AS_Unset; + m_errorType = Phonon::NoError; + + m_tickTimer = 0; + m_bufferTimer = 0; + m_rapidTimer = 0; + + checkForError(); +} + +MediaObject::~MediaObject() +{ + // m_mediaObjectAudioNode is owned by super class. + m_audioPlayer->unsetVideoPlayer(); + m_nextAudioPlayer->unsetVideoPlayer(); + delete m_videoPlayer; + delete m_nextVideoPlayer; + delete m_metaData; + checkForError(); +} + +bool MediaObject::setState(Phonon::State state) +{ + Phonon::State prevState = m_state; + m_state = state; + if (prevState != m_state){ + emit stateChanged(m_state, prevState); + if (m_state != state){ + // End-application did something + // upon receiving the signal. + return false; + } + } + return true; +} + +void MediaObject::inspectAudioGraphRecursive(AudioConnection *connection, int &effectCount, int &outputCount) +{ + if ((connection->m_sink->m_description & (AudioSource | AudioSink)) == (AudioSource | AudioSink)) + ++effectCount; + else if (connection->m_sink->m_description & AudioSink) + ++outputCount; + + for (int i=0; i<connection->m_sink->m_audioSinkList.size(); ++i) + inspectAudioGraphRecursive(connection->m_sink->m_audioSinkList[i], effectCount, outputCount); +} + +void MediaObject::inspectVideoGraphRecursive(MediaNode *node, int &effectCount, int &outputCount) +{ + if ((node->m_description & (VideoSource | VideoSink)) == (VideoSource | VideoSink)) + ++effectCount; + else if (node->m_description & VideoSink) + ++outputCount; + + for (int i=0; i<node->m_videoSinkList.size(); ++i) + inspectVideoGraphRecursive(node->m_videoSinkList[i], effectCount, outputCount); +} + +void MediaObject::inspectGraph() +{ + // Inspect the graph to check wether there are any + // effects or outputs connected. This will have + // influence on the audio system and video system that ends up beeing used: + int prevVideoOutputCount = m_videoOutputCount; + m_audioEffectCount = 0; + m_audioOutputCount = 0; + m_videoEffectCount = 0; + m_videoOutputCount = 0; + AudioConnection rootConnection(this); + inspectAudioGraphRecursive(&rootConnection, m_audioEffectCount, m_audioOutputCount); + inspectVideoGraphRecursive(this, m_videoEffectCount, m_videoOutputCount); + + if (m_videoOutputCount != prevVideoOutputCount){ + MediaNodeEvent e1(MediaNodeEvent::VideoOutputCountChanged, &m_videoOutputCount); + notify(&e1); + } +} + +void MediaObject::setupAudioSystem() +{ + // Select which audio system to use: + AudioSystem newAudioSystem = AS_Unset; + if (!m_audioOutputCount || !m_videoPlayer->canPlayMedia()){ + newAudioSystem = AS_Silent; + } else if (m_audioEffectCount == 0){ + newAudioSystem = AS_Video; + } else if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_4){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for Mac OS 10.3 and below", NORMAL_ERROR); + } else if (m_videoPlayer->isDrmProtected()){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for DRM protected media", NORMAL_ERROR); + } else if (m_audioGraph->graphCannotPlay()){ + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for the current codec", NORMAL_ERROR); +#ifdef QUICKTIME_C_API_AVAILABLE + } else { + newAudioSystem = AS_Graph; + } +#else + } else { + newAudioSystem = AS_Video; + SET_ERROR("Audio effects are not supported for the 64-bit version of the Phonon QT7 backend", NORMAL_ERROR); + } +#endif + + if (newAudioSystem == m_audioSystem) + return; + + // Enable selected audio system: + m_audioSystem = newAudioSystem; + switch (newAudioSystem){ + case AS_Silent: + m_audioGraph->stop(); + m_videoPlayer->enableAudio(false); + m_nextVideoPlayer->enableAudio(false); + m_audioPlayer->enableAudio(false); + m_nextAudioPlayer->enableAudio(false); + break; + case AS_Graph: + if (m_state == Phonon::PausedState) + m_audioGraph->prepare(); + else + m_audioGraph->start(); + // Starting the graph can lead to a recursive call + // telling us that we must direct audio through + // video. If that has happened, we must not proceed: + if (m_audioSystem != AS_Graph) + return; + m_videoPlayer->enableAudio(false); + m_nextVideoPlayer->enableAudio(false); + m_audioPlayer->enableAudio(true); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_nextAudioPlayer->enableAudio(true); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime()); + break; + case AS_Video: + case AS_Unset: + m_audioGraph->stop(); + m_videoPlayer->enableAudio(true); + m_nextVideoPlayer->enableAudio(true); + m_audioPlayer->enableAudio(false); + m_nextAudioPlayer->enableAudio(false); + m_videoPlayer->seek(m_audioPlayer->currentTime()); + m_nextVideoPlayer->seek(m_nextAudioPlayer->currentTime()); + break; + } +} + +void MediaObject::setSource(const MediaSource &source) +{ + IMPLEMENTED; + PhononAutoReleasePool pool; + setState(Phonon::LoadingState); + + // Save current state for event/signal handling below: + bool prevHasVideo = m_videoPlayer->hasVideo(); + qint64 prevTotalTime = totalTime(); + m_waitNextSwap = false; + + // Cancel cross-fade if any: + m_nextVideoPlayer->pause(); + m_nextAudioPlayer->pause(); + m_mediaObjectAudioNode->cancelCrossFade(); + + // Set new source: + m_audioPlayer->unsetVideoPlayer(); + m_videoPlayer->setMediaSource(source); + m_audioPlayer->setVideoPlayer(m_videoPlayer); + m_metaData->setVideo(m_videoPlayer); + + m_audioGraph->updateStreamSpecifications(); + m_nextAudioPlayer->unsetVideoPlayer(); + m_nextVideoPlayer->unsetVideo(); + m_currentTime = 0; + + // Emit/notify information about the new source: + QRect videoRect = m_videoPlayer->videoRect(); + MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + notify(&e1); + + // Clear video widgets: + VideoFrame emptyFrame; + updateVideo(emptyFrame); + + emit currentSourceChanged(source); + emit metaDataChanged(m_metaData->metaData()); + + if (prevHasVideo != m_videoPlayer->hasVideo()) + emit hasVideoChanged(m_videoPlayer->hasVideo()); + if (prevTotalTime != totalTime()) + emit totalTimeChanged(totalTime()); + if (checkForError()) + return; + if (!m_videoPlayer->isDrmAuthorized()) + SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR) + if (checkForError()) + return; + if (!m_videoPlayer->canPlayMedia()) + SET_ERROR("Cannot play media.", FATAL_ERROR) + + // The state might have changed from LoadingState + // as a response to an error state change. So we + // need to check it before stopping: + if (m_state == Phonon::LoadingState) + stop(); + + setupAudioSystem(); + checkForError(); +} + +void MediaObject::setNextSource(const MediaSource &source) +{ + IMPLEMENTED; + m_nextAudioPlayer->unsetVideoPlayer(); + m_nextVideoPlayer->setMediaSource(source); + m_nextAudioPlayer->setVideoPlayer(m_nextVideoPlayer); + checkForError(); +} + +void MediaObject::swapCurrentWithNext(qint32 transitionTime) +{ + PhononAutoReleasePool pool; + setState(Phonon::LoadingState); + // Save current state for event/signal handling below: + bool prevHasVideo = m_videoPlayer->hasVideo(); + qint64 prevTotalTime = totalTime(); + + qSwap(m_audioPlayer, m_nextAudioPlayer); + qSwap(m_videoPlayer, m_nextVideoPlayer); + m_mediaObjectAudioNode->startCrossFade(transitionTime); + m_audioGraph->updateStreamSpecifications(); + m_metaData->setVideo(m_videoPlayer); + + m_waitNextSwap = false; + m_currentTime = 0; + + // Emit/notify information about the new source: + QRect videoRect = m_videoPlayer->videoRect(); + MediaNodeEvent e1(MediaNodeEvent::VideoFrameSizeChanged, &videoRect); + notify(&e1); + + emit currentSourceChanged(m_videoPlayer->mediaSource()); + emit metaDataChanged(m_metaData->metaData()); + + if (prevHasVideo != m_videoPlayer->hasVideo()) + emit hasVideoChanged(m_videoPlayer->hasVideo()); + if (prevTotalTime != totalTime()) + emit totalTimeChanged(totalTime()); + if (checkForError()) + return; + if (!m_videoPlayer->isDrmAuthorized()) + SET_ERROR("This computer is not authorized to play current media (DRM protected).", FATAL_ERROR) + if (checkForError()) + return; + if (!m_videoPlayer->canPlayMedia()) + SET_ERROR("Cannot play next media.", FATAL_ERROR) + + setupAudioSystem(); + checkForError(); + if (m_state == Phonon::LoadingState){ + if (setState(Phonon::PlayingState)) + play_internal(); + checkForError(); + } +} + +void MediaObject::updateTimer(int &timer, int interval) +{ + if (timer) + killTimer(timer); + timer = 0; + if (interval >= 0) + timer = startTimer(interval); +} + +void MediaObject::play_internal() +{ + // Play main audio/video: + m_videoPlayer->play(); + m_audioPlayer->play(); + updateLipSynch(0); + // Play old audio/video to finish cross-fade: + if (m_nextVideoPlayer->currentTime() > 0){ + m_nextVideoPlayer->play(); + m_nextAudioPlayer->play(); + } + bufferAudioVideo(); + updateTimer(m_rapidTimer, 100); +} + +void MediaObject::pause_internal() +{ + m_audioGraph->stop(); + m_audioPlayer->pause(); + m_nextAudioPlayer->pause(); + m_videoPlayer->pause(); + m_nextVideoPlayer->pause(); + updateTimer(m_rapidTimer, -1); + updateTimer(m_bufferTimer, -1); + + if (m_waitNextSwap) + m_swapTimeLeft = m_swapTime.msecsTo(QTime::currentTime()); +} + +void MediaObject::play() +{ + IMPLEMENTED; + if (m_state == Phonon::PlayingState) + return; + if (m_waitNextSwap){ + // update swap time after pause: + m_swapTime = QTime::currentTime(); + m_swapTime.addMSecs(m_swapTimeLeft); + setState(Phonon::PlayingState); + return; + } + if (m_currentTime == m_videoPlayer->duration()) + return; + if (!m_videoPlayer->canPlayMedia()) + return; + if (!setState(Phonon::PlayingState)) + return; + if (m_audioSystem == AS_Graph){ + m_audioGraph->start(); + m_mediaObjectAudioNode->setMute(true); + } + // Inform the graph that we are about to play: + bool playing = true; + MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing); + notify(&e1); + // Start to play: + play_internal(); + m_mediaObjectAudioNode->setMute(false); + checkForError(); +} + +void MediaObject::pause() +{ + IMPLEMENTED; + if (m_state == Phonon::PausedState) + return; + if (!setState(Phonon::PausedState)) + return; + pause_internal(); + // Inform the graph that we are no longer playing: + bool playing = false; + MediaNodeEvent e1(MediaNodeEvent::MediaPlaying, &playing); + notify(&e1); + // But be prepared: + if (m_audioSystem == AS_Graph) + m_audioGraph->prepare(); + checkForError(); +} + +void MediaObject::stop() +{ + IMPLEMENTED; + if (m_state == Phonon::StoppedState) + return; + if (!setState(Phonon::StoppedState)) + return; + m_waitNextSwap = false; + m_nextVideoPlayer->unsetVideo(); + m_nextAudioPlayer->unsetVideoPlayer(); + pause_internal(); + seek(0); + checkForError(); +} + +void MediaObject::seek(qint64 milliseconds) +{ + IMPLEMENTED; + if (m_state == Phonon::ErrorState) + return; + + // Stop cross-fade if any: + m_nextVideoPlayer->unsetVideo(); + m_nextAudioPlayer->unsetVideoPlayer(); + m_mediaObjectAudioNode->cancelCrossFade(); + + // Seek to new position: + m_mediaObjectAudioNode->setMute(true); + m_videoPlayer->seek(milliseconds); + m_audioPlayer->seek(m_videoPlayer->currentTime()); + m_mediaObjectAudioNode->setMute(false); + + // Update time and cancel pending swap: + if (m_currentTime < m_videoPlayer->duration()) + m_waitNextSwap = false; + + updateCurrentTime(); + if (m_state != Phonon::PlayingState) + updateVideoFrames(); + checkForError(); +} + +QStringList MediaObject::availableAudioStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QStringList MediaObject::availableVideoStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QStringList MediaObject::availableSubtitleStreams() const +{ + NOT_IMPLEMENTED; + return QStringList(); +} + +QString MediaObject::currentAudioStream(const QObject */*audioPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +QString MediaObject::currentVideoStream(const QObject */*videoPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +QString MediaObject::currentSubtitleStream(const QObject */*videoPath*/) const +{ + NOT_IMPLEMENTED; + return QString(); +} + +void MediaObject::setCurrentAudioStream(const QString &/*streamName*/,const QObject */*audioPath*/) +{ + NOT_IMPLEMENTED; +} + +void MediaObject::setCurrentVideoStream(const QString &/*streamName*/,const QObject */*videoPath*/) +{ + NOT_IMPLEMENTED; +} + +void MediaObject::setCurrentSubtitleStream(const QString &/*streamName*/,const QObject */*videoPath*/) +{ + NOT_IMPLEMENTED; +} + +int MediaObject::videoOutputCount() +{ + return m_videoOutputCount; +} + +void MediaObject::synchAudioVideo() +{ + if (m_state != Phonon::PlayingState) + return; + if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) + return; + + seek(m_currentTime); + checkForError(); +} + +qint32 MediaObject::tickInterval() const +{ + IMPLEMENTED; + return m_tickInterval; +} + +void MediaObject::setTickInterval(qint32 interval) +{ + IMPLEMENTED; + m_tickInterval = interval; + if (m_tickInterval > 0) + m_tickTimer = startTimer(m_tickInterval); + else{ + killTimer(m_tickTimer); + m_tickTimer = 0; + } +} + +bool MediaObject::hasVideo() const +{ + IMPLEMENTED; + return m_videoPlayer ? m_videoPlayer->hasVideo() : false; +} + +bool MediaObject::isSeekable() const +{ + IMPLEMENTED; + return m_videoPlayer ? m_videoPlayer->isSeekable() : false; +} + +qint64 MediaObject::currentTime() const +{ + IMPLEMENTED_SILENT; + const_cast<MediaObject *>(this)->updateCurrentTime(); + return m_currentTime; +} + +void MediaObject::updateCurrentTime() +{ + quint64 lastUpdateTime = m_currentTime; + m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime(); + quint64 total = m_videoPlayer->duration(); + + // Check if it's time to emit aboutToFinish: + quint32 mark = qMax(quint64(0), qMin(total, total + m_transitionTime - 2000)); + if (lastUpdateTime < mark && mark <= m_currentTime) + emit aboutToFinish(); + + // Check if it's time to emit prefinishMarkReached: + mark = qMax(quint64(0), total - m_prefinishMark); + if (lastUpdateTime < mark && mark <= m_currentTime) + emit prefinishMarkReached(total - m_currentTime); + + if (m_nextVideoPlayer->state() == QuickTimeVideoPlayer::NoMedia){ + // There is no next source in que. + // Check if it's time to emit finished: + if (lastUpdateTime < m_currentTime && m_currentTime == total){ + emit finished(); + m_currentTime = (m_audioSystem == AS_Graph) ? m_audioPlayer->currentTime() : m_videoPlayer->currentTime(); + if (m_state == Phonon::PlayingState && m_currentTime == total) + pause(); + } + } else { + // We have a next source. + // Check if it's time to swap to next source: + mark = qMax(quint64(0), total + m_transitionTime); + if (m_waitNextSwap && m_state == Phonon::PlayingState && + m_transitionTime < m_swapTime.msecsTo(QTime::currentTime())){ + swapCurrentWithNext(0); + } else if (mark >= total){ + if (lastUpdateTime < total && total == m_currentTime){ + m_swapTime = QTime::currentTime(); + m_swapTime.addMSecs(mark - total); + m_waitNextSwap = true; + } + } else if (lastUpdateTime < mark && mark <= m_currentTime){ + swapCurrentWithNext(total - m_currentTime); + } + } +} + +qint64 MediaObject::totalTime() const +{ + IMPLEMENTED_SILENT; + return m_videoPlayer->duration(); +} + +Phonon::State MediaObject::state() const +{ + IMPLEMENTED; + return m_state; +} + +QString MediaObject::errorString() const +{ + IMPLEMENTED; + return m_errorString; +} + +Phonon::ErrorType MediaObject::errorType() const +{ + IMPLEMENTED; + return m_errorType; +} + +bool MediaObject::checkForError() +{ + int type = gGetErrorType(); + if (type == NO_ERROR) + return false; + + m_errorType = (type == NORMAL_ERROR) ? Phonon::NormalError : Phonon::FatalError; + m_errorString = gGetErrorString(); + pause_internal(); + gClearError(); + setState(Phonon::ErrorState); + return true; +} + +QuickTimeVideoPlayer* MediaObject::videoPlayer() const +{ + return m_videoPlayer; +} + +MediaSource MediaObject::source() const +{ + IMPLEMENTED; + return m_videoPlayer->mediaSource(); +} + +qint32 MediaObject::prefinishMark() const +{ + IMPLEMENTED; + return m_prefinishMark; +} + +void MediaObject::setPrefinishMark(qint32 mark) +{ + IMPLEMENTED; + m_prefinishMark = mark; +} + +qint32 MediaObject::transitionTime() const +{ + IMPLEMENTED; + return m_transitionTime; +} + +void MediaObject::setTransitionTime(qint32 transitionTime) +{ + IMPLEMENTED; + m_transitionTime = transitionTime; +} + +void MediaObject::setVolumeOnMovie(float volume) +{ + m_videoPlayer->setMasterVolume(volume); + m_nextVideoPlayer->setMasterVolume(volume); +} + +bool MediaObject::setAudioDeviceOnMovie(int id) +{ + m_nextVideoPlayer->setAudioDevice(id); + return m_videoPlayer->setAudioDevice(id); +} + +void MediaObject::updateCrossFade() +{ + m_mediaObjectAudioNode->updateCrossFade(m_currentTime); + // Clean-up previous movie if done fading: + if (m_mediaObjectAudioNode->m_fadeDuration == 0){ + if (m_nextVideoPlayer->isPlaying() || m_nextAudioPlayer->isPlaying()){ + m_nextVideoPlayer->unsetVideo(); + m_nextAudioPlayer->unsetVideoPlayer(); + } + } +} + +void MediaObject::updateBufferStatus() +{ + float percent = m_videoPlayer->percentageLoaded(); + if (percent != m_percentageLoaded){ + m_percentageLoaded = percent; + emit bufferStatus(m_percentageLoaded * 100); + } +} + +void MediaObject::updateAudioBuffers() +{ + // Schedule audio slices: + m_audioPlayer->scheduleAudioToGraph(); + m_nextAudioPlayer->scheduleAudioToGraph(); +} + +bool MediaObject::isCrossFading() +{ + return m_mediaObjectAudioNode->isCrossFading(); +} + +void MediaObject::updateVideoFrames() +{ + // Draw next frame if awailable: + if (m_videoPlayer->videoFrameChanged()){ + updateLipSynch(50); + VideoFrame frame(m_videoPlayer); + if (m_nextVideoPlayer->isPlaying() + && m_nextVideoPlayer->hasVideo() + && isCrossFading()){ + VideoFrame bgFrame(m_nextVideoPlayer); + frame.setBackgroundFrame(bgFrame); + frame.setBaseOpacity(m_mediaObjectAudioNode->m_volume1); + } + + // Send the frame through the graph: + updateVideo(frame); + checkForError(); + } +} + +void MediaObject::updateLipSynch(int allowedOffset) +{ + if (m_audioSystem != AS_Graph || !m_audioGraph->isRunning()) + return; + if (m_videoSinkList.isEmpty() || m_audioSinkList.isEmpty()) + return; + + if (m_videoPlayer->hasVideo()){ + qint64 diff = m_audioPlayer->currentTime() - m_videoPlayer->currentTime(); + if (-allowedOffset > diff || diff > allowedOffset) + m_audioPlayer->seek(m_videoPlayer->currentTime()); + } + + if (isCrossFading() && m_nextVideoPlayer->hasVideo()){ + qint64 diff = m_nextAudioPlayer->currentTime() - m_nextVideoPlayer->currentTime(); + if (-(allowedOffset*2) > diff || diff > (allowedOffset*2)) + m_nextAudioPlayer->seek(m_nextVideoPlayer->currentTime()); + } +} + +void MediaObject::bufferAudioVideo() +{ + long nextVideoUpdate = m_videoPlayer->hasVideo() ? 30 : INT_MAX; + long nextAudioUpdate = m_audioPlayer->regularTaskFrequency(); + updateAudioBuffers(); + updateVideoFrames(); + if (m_state == Phonon::PlayingState) + updateTimer(m_bufferTimer, qMin(nextVideoUpdate, nextAudioUpdate)); +} + +void MediaObject::updateRapidly() +{ + updateCurrentTime(); + updateCrossFade(); + updateBufferStatus(); +} + +void MediaObject::setMute(bool mute) +{ + m_mediaObjectAudioNode->setMute(mute); + m_videoPlayer->setMute(mute); + m_nextVideoPlayer->setMute(mute); +} + +void MediaObject::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::EndConnectionChange: + m_mediaObjectAudioNode->setMute(true); + inspectGraph(); + setupAudioSystem(); + synchAudioVideo(); + checkForError(); + m_mediaObjectAudioNode->setMute(false); + if (m_state == Phonon::PlayingState) + bufferAudioVideo(); + break; + case MediaNodeEvent::AudioGraphCannotPlay: + case MediaNodeEvent::AudioGraphInitialized: + if (m_state != Phonon::LoadingState){ + m_mediaObjectAudioNode->setMute(true); + setupAudioSystem(); + updateLipSynch(0); + checkForError(); + m_mediaObjectAudioNode->setMute(false); + } + break; + default: + break; + } +} + +bool MediaObject::event(QEvent *event) +{ + switch (event->type()){ + case QEvent::Timer: { + QTimerEvent *timerEvent = static_cast<QTimerEvent *>(event); + if (timerEvent->timerId() == m_rapidTimer) + updateRapidly(); + else if (timerEvent->timerId() == m_tickTimer) + emit tick(currentTime()); + else if (timerEvent->timerId() == m_bufferTimer) + bufferAudioVideo(); + } + break; + default: + break; + } + return QObject::event(event); +} + +bool MediaObject::hasInterface(Interface /*interface*/) const +{ + return false; +} + +QVariant MediaObject::interfaceCall(Interface /*interface*/, int /*command*/, const QList<QVariant> &/*arguments*/) +{ + return QVariant(); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_mediaobject.cpp" + diff --git a/src/3rdparty/phonon/qt7/mediaobjectaudionode.h b/src/3rdparty/phonon/qt7/mediaobjectaudionode.h new file mode 100644 index 0000000..7939aaa --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobjectaudionode.h @@ -0,0 +1,75 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_MEDIAOBJECTAUDIONODE_H +#define Phonon_QT7_MEDIAOBJECTAUDIONODE_H + +#include <QtCore/qnamespace.h> +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeAudioPlayer; + class AudioMixerAudioNode; + class AudioConnection; + + class MediaObjectAudioNode : public AudioNode + { + public: + MediaObjectAudioNode(QuickTimeAudioPlayer *player1, QuickTimeAudioPlayer *player2); + ~MediaObjectAudioNode(); + + // Overridden section from AudioNode: + void createAndConnectAUNodes(); + void createAudioUnits(); + void setGraph(AudioGraph *audioGraph); + AUNode getOutputAUNode(); + bool fillInStreamSpecification(AudioConnection *connection, ConnectionSide side); + bool setStreamSpecification(AudioConnection *connection, ConnectionSide side); + + void startCrossFade(qint64 duration); + void updateCrossFade(qint64 currentTime); + void cancelCrossFade(); + void setMute(bool mute); + bool isCrossFading(); + + QuickTimeAudioPlayer *m_player1; + QuickTimeAudioPlayer *m_player2; + AudioMixerAudioNode *m_mixer; + + AudioConnection *m_connection1; + AudioConnection *m_connection2; + + float m_fadeDuration; + float m_volume1; + float m_volume2; + float m_mute; + + float applyCurve(float volume); + void updateVolume(); + + void mediaNodeEvent(const MediaNodeEvent *event); + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_MEDIAOBJECTAUDIONODE_H diff --git a/src/3rdparty/phonon/qt7/mediaobjectaudionode.mm b/src/3rdparty/phonon/qt7/mediaobjectaudionode.mm new file mode 100644 index 0000000..39b0d4e --- /dev/null +++ b/src/3rdparty/phonon/qt7/mediaobjectaudionode.mm @@ -0,0 +1,209 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#import <QTKit/QTMovie.h> + +#include "mediaobjectaudionode.h" +#include "quicktimeaudioplayer.h" +#include "quicktimevideoplayer.h" +#include "audiomixer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +MediaObjectAudioNode::MediaObjectAudioNode(QuickTimeAudioPlayer *player1, QuickTimeAudioPlayer *player2) : AudioNode(0, 1) +{ + m_mute = false; + m_player1 = player1; + m_player2 = player2; + m_mixer = new AudioMixerAudioNode(); + + m_connection1 = new AudioConnection(m_player1, 0, m_mixer, 0); + m_connection2 = new AudioConnection(m_player2, 0, m_mixer, 1); + + m_fadeDuration = 0; +} + +MediaObjectAudioNode::~MediaObjectAudioNode() +{ + setGraph(0); + delete m_player1; + delete m_player2; + delete m_mixer; + delete m_connection1; + delete m_connection2; +} + +void MediaObjectAudioNode::createAndConnectAUNodes() +{ + DEBUG_AUDIO_GRAPH("(MediaObjectAudioNode" << int(this) << "createAndConnectAUNodes called)" ) + m_player1->createAndConnectAUNodes(); + m_player2->createAndConnectAUNodes(); + m_mixer->createAndConnectAUNodes(); + + m_connection1->connect(m_audioGraph); + m_connection2->connect(m_audioGraph); +} + +void MediaObjectAudioNode::createAudioUnits() +{ + DEBUG_AUDIO_GRAPH("(MediaObjectAudioNode" << int(this) << "createAudioUnits called)" ) + m_player1->createAudioUnits(); + m_player2->createAudioUnits(); + m_mixer->createAudioUnits(); +} + +void MediaObjectAudioNode::setGraph(AudioGraph *audioGraph) +{ + DEBUG_AUDIO_GRAPH("MediaObjectAudioNode" << int(this) << "is setting graph:" << int(audioGraph)) + m_audioGraph = audioGraph; + m_player1->setGraph(audioGraph); + m_player2->setGraph(audioGraph); + m_mixer->setGraph(audioGraph); +} + +AUNode MediaObjectAudioNode::getOutputAUNode() +{ + return m_mixer->getOutputAUNode(); +} + +bool MediaObjectAudioNode::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + DEBUG_AUDIO_STREAM("(MediaObjectAudioNode" << int(this) << "fillInStreamSpecification called, role = source)") + return m_mixer->fillInStreamSpecification(connection, side); + } else { + DEBUG_AUDIO_STREAM("(MediaObjectAudioNode" << int(this) << "fillInStreamSpecification called, role = sink)") + return (m_connection2->updateStreamSpecification() && m_connection1->updateStreamSpecification()); + } +} + +bool MediaObjectAudioNode::setStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (side == Source){ + DEBUG_AUDIO_STREAM("(MediaObjectAudioNode" << int(this) << "setStreamSpecification called, role = source)") + return m_mixer->setStreamSpecification(connection, side); + } + return true; +} + +void MediaObjectAudioNode::setMute(bool mute) +{ + m_mute = mute; + m_mixer->setVolume(m_mute ? 0 : m_volume1, m_connection1->m_sinkInputBus); + m_mixer->setVolume(m_mute ? 0 : m_volume2, m_connection2->m_sinkInputBus); +} + +void MediaObjectAudioNode::updateVolume() +{ + if (m_mute) + return; + + QuickTimeVideoPlayer *player1 = static_cast<QuickTimeAudioPlayer *>(m_connection1->m_sourceAudioNode)->videoPlayer(); + QuickTimeVideoPlayer *player2 = static_cast<QuickTimeAudioPlayer *>(m_connection2->m_sourceAudioNode)->videoPlayer(); + if (player1) + player1->setRelativeVolume(m_volume1); + if (player2) + player2->setRelativeVolume(m_volume2); + + m_mixer->setVolume(m_volume1, m_connection1->m_sinkInputBus); + m_mixer->setVolume(m_volume2, m_connection2->m_sinkInputBus); +} + +void MediaObjectAudioNode::startCrossFade(qint64 duration) +{ + m_fadeDuration = duration; + + // Swap: + AudioConnection *tmp = m_connection1; + m_connection1 = m_connection2; + m_connection2 = tmp; + + // Init volume: + if (m_fadeDuration > 0){ + m_volume1 = 0; + m_volume2 = 1; + } else { + m_volume1 = 1; + m_volume2 = 0; + } + updateVolume(); +} + +float MediaObjectAudioNode::applyCurve(float volume) +{ + float newValue = 0; + if (volume > 0) + newValue = float(0.5f * (2 + log10(volume))); + return newValue; +} + +void MediaObjectAudioNode::updateCrossFade(qint64 currentTime) +{ + // Assume that currentTime starts at 0 and progress. + if (m_fadeDuration > 0){ + float volume = float(currentTime) / float(m_fadeDuration); + if (volume >= 1){ + volume = 1; + m_fadeDuration = 0; + } + m_volume1 = applyCurve(volume); + m_volume2 = 1 - volume; + updateVolume(); + } +} + +bool MediaObjectAudioNode::isCrossFading() +{ + return (m_fadeDuration > 0); +} + +void MediaObjectAudioNode::cancelCrossFade() +{ + m_fadeDuration = 0; + m_volume1 = 1; + m_volume2 = 0; + updateVolume(); +} + +void MediaObjectAudioNode::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + m_connection1->invalidate(); + m_connection2->invalidate(); + break; + case MediaNodeEvent::AudioGraphCannotPlay: + case MediaNodeEvent::AudioGraphInitialized: + updateVolume(); + break; + default: + break; + } + + m_player1->mediaNodeEvent(event); + m_player2->mediaNodeEvent(event); + m_mixer->mediaNodeEvent(event); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/quicktimeaudioplayer.h b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.h new file mode 100644 index 0000000..25ddb5e --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.h @@ -0,0 +1,112 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMEAUDIOPLAYER_H +#define Phonon_QT7_QUICKTIMEAUDIOPLAYER_H + +#include "backendheader.h" + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QString> +#include "audionode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class AudioGraph; + class MediaNodeEvent; + class QuickTimeVideoPlayer; + + class QuickTimeAudioPlayer : public AudioNode + { + public: + enum State {Playing, Paused, NoMedia, NoState}; + + QuickTimeAudioPlayer(); + virtual ~QuickTimeAudioPlayer(); + + void play(); + void pause(); + void seek(quint64 milliseconds); + void enableAudio(bool enable); + bool audioEnabled(); + void flush(); + + void setVideoPlayer(QuickTimeVideoPlayer *videoPlayer); + void unsetVideoPlayer(); + + bool hasAudio(); + bool isPlaying(); + void scheduleAudioToGraph(); + long regularTaskFrequency(); + quint64 currentTime(); + QString currentTimeString(); + QuickTimeVideoPlayer *videoPlayer(); + + ComponentDescription getAudioNodeDescription() const; + void initializeAudioUnit(); + bool fillInStreamSpecification(AudioConnection *connection, ConnectionSide side); + void mediaNodeEvent(const MediaNodeEvent *event); + + static bool soundPlayerIsAwailable(); + + private: + void initSoundExtraction(); + void newGraphNotification(); + void allocateSoundSlices(); + void scheduleSoundSlices(); + + State m_state; + QuickTimeVideoPlayer *m_videoPlayer; + +#ifdef QUICKTIME_C_API_AVAILABLE + MovieAudioExtractionRef m_audioExtractionRef; +#endif + + ScheduledAudioSlice *m_sliceList; + AudioChannelLayout *m_audioChannelLayout; + UInt32 m_audioChannelLayoutSize; + AudioStreamBasicDescription m_audioStreamDescription; + + bool m_discrete; + bool m_playerUnitStarted; + bool m_audioExtractionComplete; + bool m_audioEnabled; + bool m_audioUnitIsReset; + + long m_samplesRemaining; + int m_sliceCount; + int m_maxExtractionPacketCount; + + Float64 m_sampleTimeStamp; + quint64 m_startTime; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMEAUDIOPLAYER_H diff --git a/src/3rdparty/phonon/qt7/quicktimeaudioplayer.mm b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.mm new file mode 100644 index 0000000..aefec02 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimeaudioplayer.mm @@ -0,0 +1,493 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#import <QTKit/QTMovie.h> + +#include "quicktimeaudioplayer.h" +#include "quicktimevideoplayer.h" +#include "audiograph.h" +#include "medianodeevent.h" +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QuickTimeAudioPlayer::QuickTimeAudioPlayer() : AudioNode(0, 1) +{ + m_state = NoMedia; + m_videoPlayer = 0; + m_audioChannelLayout = 0; + m_sliceList = 0; + m_sliceCount = 30; + m_maxExtractionPacketCount = 4096; + m_audioExtractionComplete = false; + m_audioEnabled = true; + m_samplesRemaining = -1; + m_startTime = 0; + m_sampleTimeStamp = 0; + m_audioUnitIsReset = true; + +#ifdef QUICKTIME_C_API_AVAILABLE + m_audioExtractionRef = 0; +#endif +} + +QuickTimeAudioPlayer::~QuickTimeAudioPlayer() +{ + unsetVideoPlayer(); +} + +void QuickTimeAudioPlayer::unsetVideoPlayer() +{ + if (m_audioUnit){ + OSStatus err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); + BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit when unsetting movie", FATAL_ERROR) + } + +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_audioExtractionRef && m_videoPlayer && m_videoPlayer->hasMovie()) + MovieAudioExtractionEnd(m_audioExtractionRef); + m_audioExtractionRef = 0; +#endif + + if (m_audioChannelLayout){ + free(m_audioChannelLayout); + m_audioChannelLayout = 0; + } + + if (m_sliceList){ + for (int i=0; i<m_sliceCount; i++) + free(m_sliceList[i].mBufferList); + free(m_sliceList); + m_sliceList = 0; + } + + m_videoPlayer = 0; + m_audioExtractionComplete = false; + m_samplesRemaining = -1; + m_sampleTimeStamp = 0; + m_state = NoMedia; +} + +void QuickTimeAudioPlayer::enableAudio(bool enable) +{ + // Remember to seek after enabling audio. + if (enable == m_audioEnabled) + return; + + m_audioEnabled = enable; + if (!enable) + flush(); +} + +bool QuickTimeAudioPlayer::audioEnabled() +{ + return m_audioEnabled; +} + +void QuickTimeAudioPlayer::setVideoPlayer(QuickTimeVideoPlayer *videoPlayer) +{ + unsetVideoPlayer(); + if (videoPlayer && videoPlayer->hasMovie()){ + m_videoPlayer = videoPlayer; + initSoundExtraction(); + allocateSoundSlices(); + m_state = Paused; + seek(0); + } +} + +QuickTimeVideoPlayer *QuickTimeAudioPlayer::videoPlayer() +{ + return m_videoPlayer; +} + +void QuickTimeAudioPlayer::scheduleAudioToGraph() +{ + if (!m_videoPlayer || !m_audioEnabled || m_audioExtractionComplete || m_state != Playing) + return; + + // Schedule audio slices, and detect if everything went OK. + // If not, flag the need for another audio system, but let + // the end app know about it: + gClearError(); + scheduleSoundSlices(); + if (gGetErrorType() != NO_ERROR){ + gClearError(); + if (m_audioGraph) + m_audioGraph->setStatusCannotPlay(); + } +} + +void QuickTimeAudioPlayer::flush() +{ + // Empty scheduled audio data, so playback + // will stop. Call seek to refill data again. + if (m_audioUnit){ + m_startTime = currentTime(); + OSStatus err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); + BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit on pause", FATAL_ERROR) + m_audioUnitIsReset = true; + } +} + +void QuickTimeAudioPlayer::pause() +{ + m_state = Paused; + flush(); +} + +void QuickTimeAudioPlayer::play() +{ + m_state = Playing; + if (!m_audioEnabled) + return; + if (m_audioUnitIsReset) + seek(m_startTime); + else + scheduleAudioToGraph(); +} + +bool QuickTimeAudioPlayer::isPlaying() +{ + return m_videoPlayer && m_state == Playing; +} + +void QuickTimeAudioPlayer::seek(quint64 milliseconds) +{ + if (!m_videoPlayer || !m_videoPlayer->hasMovie()) + return; + if (milliseconds > m_videoPlayer->duration()) + milliseconds = m_videoPlayer->duration(); + if (!m_audioUnitIsReset && milliseconds == currentTime()) + return; + + m_startTime = milliseconds; + + // Since the graph may be running (advancing time), there is + // no point in seeking if were not going to play immidiatly: + if (m_state != Playing) + return; + if (!m_audioUnit) + return; + if (!m_audioEnabled || !m_videoPlayer->isSeekable()) + return; + + // Reset (and stop playing): + OSStatus err; + if (!m_audioUnitIsReset){ + err = AudioUnitReset(m_audioUnit, kAudioUnitScope_Global, 0); + BACKEND_ASSERT2(err == noErr, "Could not reset audio player unit before seek", FATAL_ERROR) + } + m_sampleTimeStamp = 0; + for (int i = 0; i < m_sliceCount; i++) + m_sliceList[i].mFlags = kScheduledAudioSliceFlag_Complete; + + // Start to play again immidiatly: + AudioTimeStamp timeStamp; + memset(&timeStamp, 0, sizeof(timeStamp)); + timeStamp.mFlags = kAudioTimeStampSampleTimeValid; + timeStamp.mSampleTime = -1; + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_ScheduleStartTimeStamp, kAudioUnitScope_Global, + 0, &timeStamp, sizeof(timeStamp)); + BACKEND_ASSERT2(err == noErr, "Could not set schedule start time stamp on audio player unit", FATAL_ERROR) + + // Seek back to 'now' in the movie: + TimeRecord timeRec; + timeRec.scale = m_videoPlayer->timeScale(); + timeRec.base = 0; + timeRec.value.hi = 0; + timeRec.value.lo = (milliseconds / 1000.0f) * timeRec.scale; + +#ifdef QUICKTIME_C_API_AVAILABLE + err = MovieAudioExtractionSetProperty(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_CurrentTime, + sizeof(TimeRecord), &timeRec); + BACKEND_ASSERT2(err == noErr, "Could not set current time on audio player unit", FATAL_ERROR) +#endif + + float durationLeftSec = float(m_videoPlayer->duration() - milliseconds) / 1000.0f; + m_samplesRemaining = (durationLeftSec > 0) ? (durationLeftSec * m_audioStreamDescription.mSampleRate) : -1; + m_audioExtractionComplete = false; + m_audioUnitIsReset = false; + scheduleAudioToGraph(); + +} + +quint64 QuickTimeAudioPlayer::currentTime() +{ + if (!m_audioUnit){ + if (m_videoPlayer) + return m_videoPlayer->currentTime(); + else + return m_startTime; + } + + Float64 currentUnitTime = getTimeInSamples(kAudioUnitProperty_CurrentPlayTime); + if (currentUnitTime == -1) + currentUnitTime = 0; + + quint64 cTime = quint64(m_startTime + + float(currentUnitTime / float(m_audioStreamDescription.mSampleRate)) * 1000.0f); + return (m_videoPlayer && cTime > m_videoPlayer->duration()) ? m_videoPlayer->duration() : cTime; +} + +QString QuickTimeAudioPlayer::currentTimeString() +{ + return QuickTimeVideoPlayer::timeToString(currentTime()); +} + +bool QuickTimeAudioPlayer::hasAudio() +{ + if (!m_videoPlayer) + return false; + + return m_videoPlayer->hasAudio(); +} + +bool QuickTimeAudioPlayer::soundPlayerIsAwailable() +{ + QuickTimeAudioPlayer player; + ComponentDescription d = player.getAudioNodeDescription(); + return FindNextComponent(0, &d); +} + +ComponentDescription QuickTimeAudioPlayer::getAudioNodeDescription() const +{ + ComponentDescription description; + description.componentType = kAudioUnitType_Generator; + description.componentSubType = kAudioUnitSubType_ScheduledSoundPlayer; + description.componentManufacturer = kAudioUnitManufacturer_Apple; + description.componentFlags = 0; + description.componentFlagsMask = 0; + return description; +} + +void QuickTimeAudioPlayer::initializeAudioUnit() +{ +} + +bool QuickTimeAudioPlayer::fillInStreamSpecification(AudioConnection *connection, ConnectionSide side) +{ + if (!m_videoPlayer){ + if (side == Source) + DEBUG_AUDIO_STREAM("QuickTimeAudioPlayer" << int(this) << "is source, but has no movie to use for stream spec fill.") + return true; + } + + if (side == Source){ + DEBUG_AUDIO_STREAM("QuickTimeAudioPlayer" << int(this) << "is source, and fills in stream spec from movie.") + connection->m_sourceStreamDescription = m_audioStreamDescription; + connection->m_sourceChannelLayout = (AudioChannelLayout *) malloc(m_audioChannelLayoutSize); + memcpy(connection->m_sourceChannelLayout, m_audioChannelLayout, m_audioChannelLayoutSize); + connection->m_sourceChannelLayoutSize = m_audioChannelLayoutSize; + connection->m_hasSourceSpecification = true; + } + return true; +} + +long QuickTimeAudioPlayer::regularTaskFrequency(){ + if (!m_audioEnabled || !m_audioUnit || (m_audioGraph && m_audioGraph->graphCannotPlay())) + return INT_MAX; + + // Calculate how much audio in + // milliseconds our slices can hold: + int packetNeedPerSecond = m_audioStreamDescription.mSampleRate / m_maxExtractionPacketCount; + long bufferTimeLengthSec = float(m_sliceCount) / float(packetNeedPerSecond); + // Make sure we also get some time to fill the + // buffer, so divide the time by two: + return (bufferTimeLengthSec * (1000 / 2)); +} + +void QuickTimeAudioPlayer::initSoundExtraction() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + // Initilize the extraction: + OSStatus err = noErr; + err = MovieAudioExtractionBegin([m_videoPlayer->qtMovie() quickTimeMovie], 0, &m_audioExtractionRef); + BACKEND_ASSERT2(err == noErr, "Could not start audio extraction on audio player unit", FATAL_ERROR) + m_discrete = false; +#if 0 + // Extract all channels as descrete: + err = MovieAudioExtractionSetProperty(audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, + sizeof (discrete), + &discrete); + BACKEND_ASSERT2(err == noErr, "Could not set channels discrete on audio player unit", FATAL_ERROR) +#endif + + // Get the size of the audio channel layout (may include offset): + err = MovieAudioExtractionGetPropertyInfo(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + 0, &m_audioChannelLayoutSize, 0); + BACKEND_ASSERT2(err == noErr, "Could not get channel layout size from audio extraction", FATAL_ERROR) + + // Allocate memory for the layout + m_audioChannelLayout = (AudioChannelLayout *) calloc(1, m_audioChannelLayoutSize); + BACKEND_ASSERT2(m_audioChannelLayout, "Could not allocate memory for channel layout on audio player unit", FATAL_ERROR) + + // Get the layout: + err = MovieAudioExtractionGetProperty(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + m_audioChannelLayoutSize, m_audioChannelLayout, 0); + BACKEND_ASSERT2(err == noErr, "Could not get channel layout from audio extraction", FATAL_ERROR) + + // Get audio stream description: + err = MovieAudioExtractionGetProperty(m_audioExtractionRef, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof(m_audioStreamDescription), &m_audioStreamDescription, 0); + BACKEND_ASSERT2(err == noErr, "Could not get audio stream description from audio extraction", FATAL_ERROR) + +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeAudioPlayer::allocateSoundSlices() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + // m_sliceList will contain a specified number of ScheduledAudioSlice-s that each can + // carry audio from extraction, and be scheduled for playback at an audio unit. + // Each ScheduledAudioSlice will contain several audio buffers, one for each sound channel. + // Each buffer will carry (at most) a specified number of sound packets, and each packet can + // contain one or more frames. + + // Create a list of ScheduledAudioSlices: + m_sliceList = (ScheduledAudioSlice *) calloc(m_sliceCount, sizeof(ScheduledAudioSlice)); + BACKEND_ASSERT2(m_sliceList, "Could not allocate memory for audio slices", FATAL_ERROR) + bzero(m_sliceList, m_sliceCount * sizeof(ScheduledAudioSlice)); + + // Calculate the size of the different structures needed: + int packetsBufferSize = m_maxExtractionPacketCount * m_audioStreamDescription.mBytesPerPacket; + int channels = m_audioStreamDescription.mChannelsPerFrame; + int audioBufferListSize = int(sizeof(AudioBufferList) + (channels-1) * sizeof(AudioBuffer)); + int mallocSize = audioBufferListSize + (packetsBufferSize * m_audioStreamDescription.mChannelsPerFrame); + + // Round off to Altivec sizes: + packetsBufferSize = int(((packetsBufferSize + 15) / 16) * 16); + audioBufferListSize = int(((audioBufferListSize + 15) / 16) * 16); + + for (int sliceIndex = 0; sliceIndex < m_sliceCount; ++sliceIndex){ + // Create the memory chunk for this audio slice: + AudioBufferList *audioBufferList = (AudioBufferList*) calloc(1, mallocSize); + BACKEND_ASSERT2(audioBufferList, "Could not allocate memory for audio buffer list", FATAL_ERROR) + + // The AudioBufferList contains an AudioBuffer for each channel in the audio stream: + audioBufferList->mNumberBuffers = m_audioStreamDescription.mChannelsPerFrame; + for (uint i = 0; i < audioBufferList->mNumberBuffers; ++i){ + audioBufferList->mBuffers[i].mNumberChannels = 1; + audioBufferList->mBuffers[i].mData = (char *) audioBufferList + audioBufferListSize + (i * packetsBufferSize); + audioBufferList->mBuffers[i].mDataByteSize = packetsBufferSize; + } + + m_sliceList[sliceIndex].mBufferList = audioBufferList; + m_sliceList[sliceIndex].mNumberFrames = m_maxExtractionPacketCount; + m_sliceList[sliceIndex].mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid; + m_sliceList[sliceIndex].mCompletionProcUserData = 0; + m_sliceList[sliceIndex].mCompletionProc = 0; + m_sliceList[sliceIndex].mFlags = kScheduledAudioSliceFlag_Complete; + m_sliceList[sliceIndex].mReserved = 0; + } + +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeAudioPlayer::scheduleSoundSlices() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + PhononAutoReleasePool pool; + // For each completed (or never used) slice, fill and schedule it. + for (int sliceIndex = 0; sliceIndex < m_sliceCount; ++sliceIndex){ + if (m_sliceList[sliceIndex].mFlags & kScheduledAudioSliceFlag_Complete){ + if (m_samplesRemaining == 0) + m_audioExtractionComplete = true; + + if (!m_audioExtractionComplete){ + // Determine how many samples to read: + int samplesCount = m_samplesRemaining; + if ((samplesCount > m_maxExtractionPacketCount) || (samplesCount == -1)) + samplesCount = m_maxExtractionPacketCount; + m_sliceList[sliceIndex].mTimeStamp.mSampleTime = m_sampleTimeStamp; + + // Reset buffer sizes: + int byteSize = samplesCount * m_audioStreamDescription.mBytesPerPacket; + for (uint i = 0; i < m_sliceList[sliceIndex].mBufferList->mNumberBuffers; ++i) + m_sliceList[sliceIndex].mBufferList->mBuffers[i].mDataByteSize = byteSize; + + // Do the extraction: + UInt32 flags = 0; + UInt32 samplesRead = samplesCount; + OSStatus err = MovieAudioExtractionFillBuffer( + m_audioExtractionRef, &samplesRead, m_sliceList[sliceIndex].mBufferList, &flags); + BACKEND_ASSERT2(err == noErr, "Could not fill audio buffers from audio extraction", FATAL_ERROR) + m_audioExtractionComplete = (flags & kQTMovieAudioExtractionComplete); + + // Play the slice: + if (samplesRead != 0 && m_audioUnit != 0){ + m_sliceList[sliceIndex].mNumberFrames = samplesRead; + err = AudioUnitSetProperty(m_audioUnit, + kAudioUnitProperty_ScheduleAudioSlice, kAudioUnitScope_Global, + 0, &m_sliceList[sliceIndex], sizeof(ScheduledAudioSlice)); + BACKEND_ASSERT2(err == noErr, "Could not schedule audio buffers on audio unit", FATAL_ERROR) + } else + m_sliceList[sliceIndex].mFlags = kScheduledAudioSliceFlag_Complete; + + // Move the window: + m_sampleTimeStamp += samplesRead; + if (m_samplesRemaining != -1) + m_samplesRemaining -= samplesRead; + } + } + } + +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeAudioPlayer::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::AudioGraphAboutToBeDeleted: + case MediaNodeEvent::AboutToRestartAudioStream: + case MediaNodeEvent::StartConnectionChange: + m_startTime = currentTime(); + break; + case MediaNodeEvent::AudioGraphInitialized: + case MediaNodeEvent::RestartAudioStreamRequest: + case MediaNodeEvent::EndConnectionChange: + if (m_state == Playing) + seek(m_startTime); + break; + default: + break; + } +} + +}} + +QT_END_NAMESPACE + diff --git a/src/3rdparty/phonon/qt7/quicktimemetadata.h b/src/3rdparty/phonon/qt7/quicktimemetadata.h new file mode 100644 index 0000000..d524183 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimemetadata.h @@ -0,0 +1,67 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMEMETADATA_H +#define Phonon_QT7_QUICKTIMEMETADATA_H + +#include "backendheader.h" +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QString> + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + class QuickTimeMetaData + { + public: + QuickTimeMetaData(); + virtual ~QuickTimeMetaData(); + + void setVideo(QuickTimeVideoPlayer *videoPlayer); + QMultiMap<QString, QString> metaData(); + + private: + QMultiMap<QString, QString> m_metaData; + bool m_movieChanged; + QuickTimeVideoPlayer *m_videoPlayer; + void readMetaData(); + +#ifdef QUICKTIME_C_API_AVAILABLE + QString stripCopyRightSymbol(const QString &key); + QString convertQuickTimeKeyToUserKey(const QString &key); + OSStatus readMetaValue(QTMetaDataRef, QTMetaDataItem, QTPropertyClass, QTPropertyID, QTPropertyValuePtr *, ByteCount *); + UInt32 getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item); + QString getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id); + void readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result); +#endif // QUICKTIME_C_API_AVAILABLE + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMEMETADATA_H diff --git a/src/3rdparty/phonon/qt7/quicktimemetadata.mm b/src/3rdparty/phonon/qt7/quicktimemetadata.mm new file mode 100644 index 0000000..4ae3e2c --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimemetadata.mm @@ -0,0 +1,187 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#import <QTKit/QTMovie.h> + +#include "quicktimemetadata.h" +#include "quicktimevideoplayer.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QuickTimeMetaData::QuickTimeMetaData() +{ + m_videoPlayer = 0; + m_movieChanged = false; +} + +QuickTimeMetaData::~QuickTimeMetaData() +{ +} + +void QuickTimeMetaData::setVideo(QuickTimeVideoPlayer *videoPlayer) +{ + m_videoPlayer = videoPlayer; + m_movieChanged = true; + m_metaData.clear(); +} + +#ifdef QUICKTIME_C_API_AVAILABLE + +QString QuickTimeMetaData::stripCopyRightSymbol(const QString &key) +{ + return key.right(key.length()-1); +} + +QString QuickTimeMetaData::convertQuickTimeKeyToUserKey(const QString &key) +{ + if (key == QLatin1String("com.apple.quicktime.displayname")) + return QLatin1String("nam"); + else if (key == QLatin1String("com.apple.quicktime.album")) + return QLatin1String("alb"); + else if (key == QLatin1String("com.apple.quicktime.artist")) + return QLatin1String("ART"); + else + return QLatin1String("???"); +} + +OSStatus QuickTimeMetaData::readMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, QTPropertyClass propClass, + QTPropertyID id, QTPropertyValuePtr *value, ByteCount *size) +{ + QTPropertyValueType type; + ByteCount propSize; + UInt32 propFlags; + OSStatus err = QTMetaDataGetItemPropertyInfo(metaDataRef, item, propClass, id, &type, &propSize, &propFlags); + BACKEND_ASSERT3(err == noErr, "Could not read meta data value size", NORMAL_ERROR, err) + + *value = malloc(propSize); + + err = QTMetaDataGetItemProperty(metaDataRef, item, propClass, id, propSize, *value, size); + BACKEND_ASSERT3(err == noErr, "Could not read meta data value", NORMAL_ERROR, err) + + if (type == 'code' || type == 'itsk' || type == 'itlk') { + // convert from native endian to big endian + OSTypePtr pType = (OSTypePtr)*value; + *pType = EndianU32_NtoB(*pType); + } + + return err; +} + +UInt32 QuickTimeMetaData::getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item) +{ + QTPropertyValuePtr value = 0; + ByteCount ignore = 0; + OSStatus err = readMetaValue( + metaDataRef, item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_DataType, &value, &ignore); + BACKEND_ASSERT3(err == noErr, "Could not read meta data type", NORMAL_ERROR, 0) + UInt32 type = *((UInt32 *) value); + if (value) + free(value); + return type; +} + +QString QuickTimeMetaData::getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id) +{ + QTPropertyValuePtr value = 0; + ByteCount size = 0; + OSStatus err = readMetaValue(metaDataRef, item, kPropertyClass_MetaDataItem, id, &value, &size); + BACKEND_ASSERT3(err == noErr, "Could not read meta data item", NORMAL_ERROR, QString()) + BACKEND_ASSERT3(value != 0, "Could not read meta data item", NORMAL_ERROR, QString()) + + QString string; + UInt32 dataType = getMetaType(metaDataRef, item); + switch (dataType){ + case kQTMetaDataTypeUTF8: + case kQTMetaDataTypeMacEncodedText: + string = PhononCFString::toQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF8, false)); + break; + case kQTMetaDataTypeUTF16BE: + string = PhononCFString::toQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF16BE, false)); + break; + default: + break; + } + + if (value) + free(value); + return string; +} + +void QuickTimeMetaData::readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap<QString, QString> &result) +{ + QTMetaDataItem item = kQTMetaDataItemUninitialized; + OSStatus err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + while (err == noErr){ + QString key = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Key); + if (format == kQTMetaDataStorageFormatQuickTime) + key = convertQuickTimeKeyToUserKey(key); + else + key = stripCopyRightSymbol(key); + + if (!result.contains(key)){ + QString val = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Value); + result.insert(key, val); + } + err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + } +} + +#endif // QUICKTIME_C_API_AVAILABLE + +void QuickTimeMetaData::readMetaData() +{ + if (!m_videoPlayer) + return; + QMultiMap<QString, QString> metaMap; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTMetaDataRef metaDataRef; + OSStatus err = QTCopyMovieMetaData([m_videoPlayer->qtMovie() quickTimeMovie], &metaDataRef); + BACKEND_ASSERT2(err == noErr, "Could not read QuickTime meta data", NORMAL_ERROR) + + readFormattedData(metaDataRef, kQTMetaDataStorageFormatUserData, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatQuickTime, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatiTunes, metaMap); +#else + NSString *name = [m_videoPlayer->qtMovie() attributeForKey:@"QTMovieDisplayNameAttribute"]; + metaMap.insert(QLatin1String("nam"), QString::fromUtf8([name UTF8String])); +#endif // QUICKTIME_C_API_AVAILABLE + + m_metaData.insert(QLatin1String("ARTIST"), metaMap.value(QLatin1String("ART"))); + m_metaData.insert(QLatin1String("ALBUM"), metaMap.value(QLatin1String("alb"))); + m_metaData.insert(QLatin1String("TITLE"), metaMap.value(QLatin1String("nam"))); + m_metaData.insert(QLatin1String("DATE"), metaMap.value(QLatin1String("day"))); + m_metaData.insert(QLatin1String("GENRE"), metaMap.value(QLatin1String("gnre"))); + m_metaData.insert(QLatin1String("TRACKNUMBER"), metaMap.value(QLatin1String("trk"))); + m_metaData.insert(QLatin1String("DESCRIPTION"), metaMap.value(QLatin1String("des"))); +} + +QMultiMap<QString, QString> QuickTimeMetaData::metaData() +{ + if (m_videoPlayer && m_videoPlayer->hasMovie() && m_movieChanged) + readMetaData(); + return m_metaData; +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/quicktimestreamreader.h b/src/3rdparty/phonon/qt7/quicktimestreamreader.h new file mode 100644 index 0000000..0e7590c --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimestreamreader.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMESTREAMREADER_H +#define Phonon_QT7_QUICKTIMESTREAMREADER_H + +#include <phonon/mediasource.h> +#include <phonon/streaminterface.h> +#include <QtCore/QReadWriteLock> + +#ifndef QT_MAC_USE_COCOA +#include <QuickTime/Movies.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeStreamReader : public QObject, Phonon::StreamInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::StreamInterface) + + public: + QuickTimeStreamReader(const Phonon::MediaSource &source); + ~QuickTimeStreamReader(); + + int readData(long offset, long size, void *data); + bool readAllData(); + QByteArray *pointerToData(); + void writeData(const QByteArray &data); + void endOfData(); + void setStreamSize(qint64 newSize); + qint64 streamSize() const; + void setStreamSeekable(bool s); + bool streamSeekable() const; + void setCurrentPos(qint64 pos); + qint64 currentPos() const; + int currentBufferSize() const; +#ifndef QT_MAC_USE_COCOA + Movie movieRef(); +#endif + + QByteArray m_buffer; + mutable QReadWriteLock m_lock; + bool m_seekable; + qint64 m_pos; + qint64 m_size; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMESTREAMREADER_H diff --git a/src/3rdparty/phonon/qt7/quicktimestreamreader.mm b/src/3rdparty/phonon/qt7/quicktimestreamreader.mm new file mode 100644 index 0000000..7131616 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimestreamreader.mm @@ -0,0 +1,137 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "backendheader.h" +#include "quicktimestreamreader.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +QuickTimeStreamReader::QuickTimeStreamReader(const Phonon::MediaSource &source) +{ + connectToSource(source); +} + +QuickTimeStreamReader::~QuickTimeStreamReader() +{ +} + +bool QuickTimeStreamReader::readAllData() +{ + int oldSize = m_buffer.size(); + while (m_buffer.size() < m_size){ + needData(); + if (oldSize == currentBufferSize()) + BACKEND_ASSERT3(oldSize != currentBufferSize(), + "Could not create new movie from IO stream. Not enough free memory to preload the whole movie.", + FATAL_ERROR, false) + oldSize = m_buffer.size(); + } + return true; +} + +QByteArray *QuickTimeStreamReader::pointerToData() +{ + return &m_buffer; +} + +int QuickTimeStreamReader::readData(long offset, long size, void *data) +{ +// QReadLocker readLocker(&m_lock); + if (streamSize() != 1 && offset + size > streamSize()){ + size = streamSize() - offset; + } + + if (currentPos() - currentBufferSize() != offset) + setCurrentPos(offset); + + int oldSize = currentBufferSize(); + while (currentBufferSize() < int(size)) { + needData(); + if (oldSize == currentBufferSize()) + break; + oldSize = currentBufferSize(); + } + + int bytesRead = qMin(currentBufferSize(), int(size)); +// QWriteLocker writeLocker(&m_lock); + qMemCopy(data, m_buffer.data(), bytesRead); + m_buffer = m_buffer.mid(bytesRead); + + return bytesRead; +} + +void QuickTimeStreamReader::writeData(const QByteArray &data) +{ + QWriteLocker locker(&m_lock); + m_pos += data.size(); + m_buffer += data; +} + +void QuickTimeStreamReader::endOfData() +{ +} + +void QuickTimeStreamReader::setStreamSize(qint64 newSize) +{ + m_size = newSize; +} + +qint64 QuickTimeStreamReader::streamSize() const +{ + return m_size; +} + +void QuickTimeStreamReader::setStreamSeekable(bool s) +{ + m_seekable = s; +} + +bool QuickTimeStreamReader::streamSeekable() const +{ + return m_seekable; +} + +void QuickTimeStreamReader::setCurrentPos(qint64 pos) +{ + QWriteLocker locker(&m_lock); + m_pos = pos; + seekStream(pos); + m_buffer.clear(); +} + +qint64 QuickTimeStreamReader::currentPos() const +{ + return m_pos; +} + +int QuickTimeStreamReader::currentBufferSize() const +{ + QReadLocker locker(&m_lock); + return m_buffer.size(); +} + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_quicktimestreamreader.cpp" + diff --git a/src/3rdparty/phonon/qt7/quicktimevideoplayer.h b/src/3rdparty/phonon/qt7/quicktimevideoplayer.h new file mode 100644 index 0000000..0b3aec2 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimevideoplayer.h @@ -0,0 +1,167 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_QUICKTIMEVIDEOPLAYER_H +#define Phonon_QT7_QUICKTIMEVIDEOPLAYER_H + +#include "backendheader.h" + +#import <QTKit/QTDataReference.h> +#import <QTKit/QTMovie.h> + +#include <phonon/mediasource.h> +#include <Carbon/Carbon.h> +#include <QtCore/QString> +#include <QtOpenGL/QGLPixelBuffer> +#include "videoframe.h" + +QT_BEGIN_NAMESPACE + +class QGLContext; + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeStreamReader; + class VideoRenderWidgetQTMovieView; + + class QuickTimeVideoPlayer : QObject + { + public: + enum StateEnum { + Playing = 0x1, + Paused = 0x2, + NoMedia = 0x4, + }; + Q_DECLARE_FLAGS(State, StateEnum); + + QuickTimeVideoPlayer(); + virtual ~QuickTimeVideoPlayer(); + + void setMediaSource(const MediaSource &source); + MediaSource mediaSource() const; + void unsetVideo(); + + void play(); + void pause(); + void seek(quint64 milliseconds); + + bool videoFrameChanged(); + CVOpenGLTextureRef currentFrameAsCVTexture(); + GLuint currentFrameAsGLTexture(); + void *currentFrameAsCIImage(); + QImage currentFrameAsQImage(); + QRect videoRect() const; + + quint64 duration() const; + quint64 currentTime() const; + long timeScale() const; + QString currentTimeString(); + + void setColors(qreal brightness = 0, qreal contrast = 1, qreal hue = 0, qreal saturation = 1); + void setMasterVolume(float volume); + void setRelativeVolume(float volume); + void setVolume(float masterVolume, float relativeVolume); + void setMute(bool mute); + void enableAudio(bool enable); + bool audioEnabled(); + bool setAudioDevice(int id); + void setPlaybackRate(float rate); + QTMovie *qtMovie() const; + + float playbackRate() const; + float prefferedPlaybackRate() const; + + QuickTimeVideoPlayer::State state() const; + + bool hasAudio() const; + bool hasVideo() const; + bool hasMovie() const; + bool canPlayMedia() const; + bool isPlaying() const; + bool isSeekable() const; + bool isDrmProtected() const; + bool isDrmAuthorized() const; + + bool preRollMovie(qint64 startTime = 0); + float percentageLoaded(); + quint64 timeLoaded(); + + static QString timeToString(quint64 ms); + + // Help functions when drawing to more that one widget in cocoa 64: + void *m_primaryRenderingTarget; + void setPrimaryRenderingTarget(NSObject *target); + + void *primaryRenderingCIImage(); + void setPrimaryRenderingCIImage(void *ciImage); + + private: + QTMovie *m_QTMovie; + State m_state; + QGLPixelBuffer *m_QImagePixelBuffer; + + bool m_playbackRateSat; + bool m_isDrmProtected; + bool m_isDrmAuthorized; + bool m_mute; + bool m_audioEnabled; + bool m_hasVideo; + float m_masterVolume; + float m_relativeVolume; + float m_playbackRate; + quint64 m_currentTime; + MediaSource m_mediaSource; + void *m_primaryRenderingCIImage; + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTVisualContextRef m_visualContext; +#endif + VideoFrame m_currentFrame; + QuickTimeStreamReader *m_streamReader; + + void createVisualContext(); + void openMovieFromCurrentMediaSource(); + void openMovieFromDataRef(QTDataReference *dataRef); + void openMovieFromFile(); + void openMovieFromUrl(); + void openMovieFromStream(); + void openMovieFromData(QByteArray *data, char *fileType); + void openMovieFromDataGuessType(QByteArray *data); + QString mediaSourcePath(); + bool codecExistsAccordingToSuffix(const QString &fileName); + + void setError(NSError *error); + bool errorOccured(); + void readProtection(); + void checkIfVideoAwailable(); + bool movieNotLoaded(); + void waitStatePlayable(); + }; + + Q_DECLARE_OPERATORS_FOR_FLAGS(QuickTimeVideoPlayer::State); + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_QUICKTIMEVIDEOPLAYER_H diff --git a/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm b/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm new file mode 100644 index 0000000..3f76132 --- /dev/null +++ b/src/3rdparty/phonon/qt7/quicktimevideoplayer.mm @@ -0,0 +1,955 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "quicktimevideoplayer.h" +#include "mediaobject.h" +#include "videowidget.h" +#include "audiodevice.h" +#include "quicktimestreamreader.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QEventLoop> +#include <QtCore/QFileInfo> +#include <QtCore/QUrl> +#include <QtOpenGL/QGLContext> + +#import <QTKit/QTTrack.h> +#import <QTKit/QTMedia.h> +#import <QuartzCore/CIContext.h> +#import <QuartzCore/CIFilter.h> + +#ifdef QUICKTIME_C_API_AVAILABLE + #include <QuickTime/QuickTime.h> + #undef check // avoid name clash; + #include <AGL/agl.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +// Defined in videowidget.cpp: +QGLWidget *PhononSharedQGLWidget(); + +QuickTimeVideoPlayer::QuickTimeVideoPlayer() : QObject(0) +{ + m_state = NoMedia; + m_mediaSource = MediaSource(); + m_QTMovie = 0; + m_streamReader = 0; + m_playbackRate = 1.0f; + m_masterVolume = 1.0f; + m_relativeVolume = 1.0f; + m_currentTime = 0; + m_mute = false; + m_audioEnabled = false; + m_hasVideo = false; + m_playbackRateSat = false; + m_isDrmProtected = false; + m_isDrmAuthorized = true; + m_primaryRenderingTarget = 0; + m_primaryRenderingCIImage = 0; + m_QImagePixelBuffer = 0; + +#ifdef QUICKTIME_C_API_AVAILABLE + OSStatus err = EnterMovies(); + BACKEND_ASSERT2(err == noErr, "Could not initialize QuickTime", FATAL_ERROR) + createVisualContext(); +#endif +} + +QuickTimeVideoPlayer::~QuickTimeVideoPlayer() +{ + unsetVideo(); + [(NSObject*)m_primaryRenderingTarget release]; + m_primaryRenderingTarget = 0; +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_visualContext) + CFRelease(m_visualContext); +#endif +} + +void QuickTimeVideoPlayer::createVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + PhononSharedQGLWidget()->makeCurrent(); + + PhononAutoReleasePool pool; + CGLContextObj cglContext = CGLGetCurrentContext(); + NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; + CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>([nsglPixelFormat CGLPixelFormatObj]); + BACKEND_ASSERT2(cglContext, "Could not get current CoreVideo GL context (OpenGL)", FATAL_ERROR) + BACKEND_ASSERT2(cglPixelFormat, "Could not get current CoreVideo pixel format (OpenGL)", FATAL_ERROR) + + CFTypeRef keys[] = { kQTVisualContextWorkingColorSpaceKey }; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)keys, + (const void **)&colorSpace, 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext, + cglPixelFormat, textureContextAttributes, &m_visualContext); + CFRelease(textureContextAttributes); + BACKEND_ASSERT2(err == noErr, "Could not create visual context (OpenGL)", FATAL_ERROR) +#endif // QUICKTIME_C_API_AVAILABLE +} + +bool QuickTimeVideoPlayer::videoFrameChanged() +{ + if (!m_QTMovie || !m_hasVideo) + return false; + +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_primaryRenderingTarget) + return true; + if (!m_visualContext) + return false; + + QTVisualContextTask(m_visualContext); + return QTVisualContextIsNewImageAvailable(m_visualContext, 0); + +#elif defined(QT_MAC_USE_COCOA) + return true; + +#else + return false; +#endif +} + +CVOpenGLTextureRef QuickTimeVideoPlayer::currentFrameAsCVTexture() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + if (!m_visualContext) + return 0; + CVOpenGLTextureRef texture = 0; + OSStatus err = QTVisualContextCopyImageForTime(m_visualContext, 0, 0, &texture); + BACKEND_ASSERT3(err == noErr, "Could not copy image for time in QuickTime player", FATAL_ERROR, 0) + return texture; + +#else + return 0; +#endif +} + +QImage QuickTimeVideoPlayer::currentFrameAsQImage() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + QGLContext *prevContext = const_cast<QGLContext *>(QGLContext::currentContext()); + CVOpenGLTextureRef texture = currentFrameAsCVTexture(); + GLenum target = CVOpenGLTextureGetTarget(texture); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + + if (!m_QImagePixelBuffer){ + m_QImagePixelBuffer = new QGLPixelBuffer(videoRect().size(), QGLFormat::defaultFormat(), PhononSharedQGLWidget()); + m_QImagePixelBuffer->makeCurrent(); + glEnable(target); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + } else { + m_QImagePixelBuffer->makeCurrent(); + } + + CVOpenGLTextureGetCleanTexCoords(texture, upperLeft, upperRight, lowerRight, lowerLeft); + glBindTexture(target, CVOpenGLTextureGetName(texture)); + glBegin(GL_QUADS); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(-1, 1); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(1, 1); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(1, -1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(-1, -1); + glEnd(); + + QImage image = m_QImagePixelBuffer->toImage(); + CVOpenGLTextureRelease(texture); + // Because of QuickTime, m_QImagePixelBuffer->doneCurrent() will fail. + // So we store, and restore, the context our selves: + prevContext->makeCurrent(); + return image; +#else + CIImage *img = (CIImage *)currentFrameAsCIImage(); + if (!img) + return QImage(); + + NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; + CGRect bounds = [img extent]; + QImage qImg([bitmap bitmapData], bounds.size.width, bounds.size.height, QImage::Format_ARGB32); + QImage swapped = qImg.rgbSwapped(); + [bitmap release]; + [img release]; + return swapped; +#endif +} + +void QuickTimeVideoPlayer::setPrimaryRenderingCIImage(void *ciImage) +{ + [(CIImage *)m_primaryRenderingCIImage release]; + m_primaryRenderingCIImage = ciImage; + [(CIImage *)m_primaryRenderingCIImage retain]; +} + +void QuickTimeVideoPlayer::setPrimaryRenderingTarget(NSObject *target) +{ + [(NSObject*)m_primaryRenderingTarget release]; + m_primaryRenderingTarget = target; + [(NSObject*)m_primaryRenderingTarget retain]; +} + +void *QuickTimeVideoPlayer::primaryRenderingCIImage() +{ + return m_primaryRenderingCIImage; +} + +void *QuickTimeVideoPlayer::currentFrameAsCIImage() +{ + if (!m_QTMovie) + return 0; + +#if defined(QT_MAC_USE_COCOA) + if (m_primaryRenderingCIImage){ + CIImage *img = (CIImage *)m_primaryRenderingCIImage; + if (m_brightness || m_contrast || m_saturation){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + } + if (m_hue){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + } + return [img retain]; + } +#endif + +#ifdef QUICKTIME_C_API_AVAILABLE + CVOpenGLTextureRef cvImg = currentFrameAsCVTexture(); + CIImage *img = [[CIImage alloc] initWithCVImageBuffer:cvImg]; + CVOpenGLTextureRelease(cvImg); + return img; +#else + return 0; +#endif +} + +GLuint QuickTimeVideoPlayer::currentFrameAsGLTexture() +{ + CIImage *img = (CIImage *)currentFrameAsCIImage(); + if (!img) + return 0; + + NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithCIImage:img]; + GLuint texName = 0; + glPixelStorei(GL_UNPACK_ROW_LENGTH, [bitmap pixelsWide]); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(1, &texName); + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texName); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + int samplesPerPixel = [bitmap samplesPerPixel]; + if (![bitmap isPlanar] && (samplesPerPixel == 3 || samplesPerPixel == 4)){ + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, + samplesPerPixel == 4 ? GL_RGBA8 : GL_RGB8, + [bitmap pixelsWide], [bitmap pixelsHigh], + 0, samplesPerPixel == 4 ? GL_RGBA : GL_RGB, + GL_UNSIGNED_BYTE, [bitmap bitmapData]); + } else { + // Handle other bitmap formats. + } + + [bitmap release]; + [img release]; + return texName; +} + +void QuickTimeVideoPlayer::setMasterVolume(float volume) +{ + setVolume(volume, m_relativeVolume); +} + +void QuickTimeVideoPlayer::setRelativeVolume(float volume) +{ + setVolume(m_masterVolume, volume); +} + +void QuickTimeVideoPlayer::setVolume(float masterVolume, float relativeVolume) +{ + m_masterVolume = masterVolume; + m_relativeVolume = relativeVolume; + if (!m_QTMovie || !m_audioEnabled || m_mute) + return; + [m_QTMovie setVolume:(m_masterVolume * m_relativeVolume)]; +} + +void QuickTimeVideoPlayer::setMute(bool mute) +{ + m_mute = mute; + if (!m_QTMovie || m_state != Playing || !m_audioEnabled) + return; + + // Work-around bug that happends if you set/unset mute + // before movie is playing, and audio is not played + // through graph. Then audio is delayed. + [m_QTMovie setMuted:mute]; + [m_QTMovie setVolume:(mute ? 0 : m_masterVolume * m_relativeVolume)]; +} + +void QuickTimeVideoPlayer::enableAudio(bool enable) +{ + m_audioEnabled = enable; + if (!m_QTMovie || m_state != Playing) + return; + + // Work-around bug that happends if you set/unset mute + // before movie is playing, and audio is not played + // through graph. Then audio is delayed. + [m_QTMovie setMuted:(!enable || m_mute)]; + [m_QTMovie setVolume:((!enable || m_mute) ? 0 : m_masterVolume * m_relativeVolume)]; +} + +bool QuickTimeVideoPlayer::audioEnabled() +{ + return m_audioEnabled; +} + +bool QuickTimeVideoPlayer::setAudioDevice(int id) +{ + if (!m_QTMovie) + return false; + +#ifdef QUICKTIME_C_API_AVAILABLE + // The following code will not work for some media codecs that + // typically mingle audio/video frames (e.g mpeg). + CFStringRef idString = PhononCFString::toCFStringRef(AudioDevice::deviceUID(id)); + QTAudioContextRef context; + QTAudioContextCreateForAudioDevice(kCFAllocatorDefault, idString, 0, &context); + OSStatus err = SetMovieAudioContext([m_QTMovie quickTimeMovie], context); + CFRelease(context); + if (err != noErr) + return false; + return true; +#else + Q_UNUSED(id); + return false; +#endif +} + +void QuickTimeVideoPlayer::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation) +{ + if (!m_QTMovie) + return; + + // 0 is default value for the colors + // in phonon, so adjust scale: + contrast += 1; + saturation += 1; + + m_brightness = brightness; + m_contrast = contrast; + m_hue = hue; + m_saturation = saturation; + +#ifdef QUICKTIME_C_API_AVAILABLE + Float32 value; + value = brightness; + SetMovieVisualBrightness([m_QTMovie quickTimeMovie], value, 0); + value = contrast; + SetMovieVisualContrast([m_QTMovie quickTimeMovie], value, 0); + value = hue; + SetMovieVisualHue([m_QTMovie quickTimeMovie], value, 0); + value = saturation; + SetMovieVisualSaturation([m_QTMovie quickTimeMovie], value, 0); +#endif +} + +QRect QuickTimeVideoPlayer::videoRect() const +{ + if (!m_QTMovie) + return QRect(); + + PhononAutoReleasePool pool; + NSSize size = [[m_QTMovie attributeForKey:@"QTMovieCurrentSizeAttribute"] sizeValue]; + return QRect(0, 0, size.width, size.height); +} + +void QuickTimeVideoPlayer::unsetVideo() +{ + if (!m_QTMovie) + return; + + [m_QTMovie release]; + m_QTMovie = 0; + delete m_streamReader; + m_streamReader = 0; + m_currentTime = 0; + m_state = NoMedia; + m_isDrmProtected = false; + m_isDrmAuthorized = true; + m_mediaSource = MediaSource(); + [(CIImage *)m_primaryRenderingCIImage release]; + m_primaryRenderingCIImage = 0; + delete m_QImagePixelBuffer; + m_QImagePixelBuffer = 0; +} + +QuickTimeVideoPlayer::State QuickTimeVideoPlayer::state() const +{ + return m_state; +} + +quint64 QuickTimeVideoPlayer::timeLoaded() +{ + if (!m_QTMovie) + return 0; +#ifdef QUICKTIME_C_API_AVAILABLE + TimeValue value; + GetMaxLoadedTimeInMovie([m_QTMovie quickTimeMovie], &value); + quint64 loaded = static_cast<quint64>(float(value) / float(GetMovieTimeScale([m_QTMovie quickTimeMovie])) * 1000.0f); + return (loaded == INT_MAX) ? 0 : loaded; +#else + return 0; +#endif +} + +float QuickTimeVideoPlayer::percentageLoaded() +{ + if (!m_QTMovie || !isSeekable()) + return 0; +#ifdef QUICKTIME_C_API_AVAILABLE + TimeValue loaded; + GetMaxLoadedTimeInMovie([m_QTMovie quickTimeMovie], &loaded); + float duration = GetMovieDuration([m_QTMovie quickTimeMovie]); + return duration ? float(loaded) / duration : 0; +#else + return 0; +#endif +} + +void QuickTimeVideoPlayer::waitStatePlayable() +{ +#if defined(QT_MAC_USE_COCOA) + long state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue]; + while (state != QTMovieLoadStateError && state < QTMovieLoadStatePlayable) + state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue]; +#elif defined(QUICKTIME_C_API_AVAILABLE) + long state = GetMovieLoadState([m_QTMovie quickTimeMovie]); + while (state != kMovieLoadStateError && state < kMovieLoadStatePlayable){ + MoviesTask(0, 0); + state = GetMovieLoadState([m_QTMovie quickTimeMovie]); + } +#endif +} + +bool QuickTimeVideoPlayer::movieNotLoaded() +{ + if (!m_QTMovie) + return true; + +#if defined(QT_MAC_USE_COCOA) + long state = [[m_QTMovie attributeForKey:@"QTMovieLoadStateAttribute"] longValue]; + return state == QTMovieLoadStateError; +#elif defined(QUICKTIME_C_API_AVAILABLE) + long state = GetMovieLoadState([m_QTMovie quickTimeMovie]); + return state == kMovieLoadStateError; +#endif +} + +void QuickTimeVideoPlayer::setError(NSError *error) +{ + if (!error) + return; + QString desc = QString::fromUtf8([[error localizedDescription] UTF8String]); + if (desc == "The file is not a movie file.") + desc = QLatin1String("Could not decode media source."); + else if (desc == "A necessary data reference could not be resolved."){ + if (codecExistsAccordingToSuffix(mediaSourcePath())) + desc = QLatin1String("Could not locate media source."); + else + desc = QLatin1String("Could not decode media source."); + } else if (desc == "You do not have sufficient permissions for this operation.") + desc = QLatin1String("Could not open media source."); + SET_ERROR(desc, FATAL_ERROR) +} + +bool QuickTimeVideoPlayer::errorOccured() +{ + if (gGetErrorType() != NO_ERROR){ + return true; + } else if (movieNotLoaded()){ + SET_ERROR("Could not open media source.", FATAL_ERROR) + return true; + } + return false; +} + +bool QuickTimeVideoPlayer::codecExistsAccordingToSuffix(const QString &fileName) +{ + PhononAutoReleasePool pool; + NSArray *fileTypes = [QTMovie movieFileTypes:QTIncludeAllTypes]; + for (uint i=0; i<[fileTypes count]; ++i){ + NSString *type = [fileTypes objectAtIndex:i]; + QString formattedType = QString::fromUtf8([type UTF8String]); + formattedType.remove('\'').remove('.'); + if (fileName.endsWith(QChar('.') + formattedType, Qt::CaseInsensitive)) + return true; + } + return false; +} + +void QuickTimeVideoPlayer::setMediaSource(const MediaSource &mediaSource) +{ + PhononAutoReleasePool pool; + unsetVideo(); + m_mediaSource = mediaSource; + if (mediaSource.type() == MediaSource::Empty || mediaSource.type() == MediaSource::Invalid){ + m_state = NoMedia; + return; + } + openMovieFromCurrentMediaSource(); + if (errorOccured()){ + unsetVideo(); + return; + } + +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_visualContext) + SetMovieVisualContext([m_QTMovie quickTimeMovie], m_visualContext); +#endif + + waitStatePlayable(); + if (errorOccured()){ + unsetVideo(); + return; + } + + readProtection(); + preRollMovie(); + if (errorOccured()){ + unsetVideo(); + return; + } + + if (!m_playbackRateSat) + m_playbackRate = prefferedPlaybackRate(); + checkIfVideoAwailable(); + enableAudio(m_audioEnabled); + setMute(m_mute); + setVolume(m_masterVolume, m_relativeVolume); + pause(); +} + +void QuickTimeVideoPlayer::openMovieFromCurrentMediaSource() +{ + switch (m_mediaSource.type()){ + case MediaSource::LocalFile: + openMovieFromFile(); + break; + case MediaSource::Url: + openMovieFromUrl(); + break; + case MediaSource::Disc: + CASE_UNSUPPORTED("Could not open media source.", FATAL_ERROR) + break; + case MediaSource::Stream: + openMovieFromStream(); + break; + case MediaSource::Empty: + case MediaSource::Invalid: + break; + } +} + +QString QuickTimeVideoPlayer::mediaSourcePath() +{ + switch (m_mediaSource.type()){ + case MediaSource::LocalFile:{ + QFileInfo fileInfo(m_mediaSource.fileName()); + return fileInfo.isSymLink() ? fileInfo.symLinkTarget() : fileInfo.canonicalFilePath(); + break;} + case MediaSource::Url: + return m_mediaSource.url().toEncoded(); + break; + default: + break; + } + return QString(); +} + +void QuickTimeVideoPlayer::openMovieFromDataRef(QTDataReference *dataRef) +{ + PhononAutoReleasePool pool; + NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys: + dataRef, QTMovieDataReferenceAttribute, + [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute, + [NSNumber numberWithBool:YES], QTMovieIsActiveAttribute, + [NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute, + [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute, + nil]; + + NSError *err = 0; + m_QTMovie = [[QTMovie movieWithAttributes:attr error:&err] retain]; + if (err){ + [m_QTMovie release]; + m_QTMovie = 0; + setError(err); + } +} + +void QuickTimeVideoPlayer::openMovieFromData(QByteArray *data, char *fileType) +{ + PhononAutoReleasePool pool; + NSString *type = [NSString stringWithUTF8String:fileType]; + NSData *nsData = [NSData dataWithBytesNoCopy:data->data() length:data->size() freeWhenDone:NO]; + QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToData:nsData name:type MIMEType:@""]; + openMovieFromDataRef(dataRef); +} + +void QuickTimeVideoPlayer::openMovieFromDataGuessType(QByteArray *data) +{ + // It turns out to be better to just try the standard file types rather + // than using e.g [QTMovie movieFileTypes:QTIncludeCommonTypes]. Some + // codecs *think* they can decode the stream, and crash... +#define TryOpenMovieWithCodec(type) gClearError(); \ + openMovieFromData(data, "."type); \ + if (m_QTMovie) return; + + TryOpenMovieWithCodec("avi"); + TryOpenMovieWithCodec("mp4"); + TryOpenMovieWithCodec("m4p"); + TryOpenMovieWithCodec("m1s"); + TryOpenMovieWithCodec("mp3"); + TryOpenMovieWithCodec("mpeg"); + TryOpenMovieWithCodec("mov"); + TryOpenMovieWithCodec("ogg"); + TryOpenMovieWithCodec("wav"); + TryOpenMovieWithCodec("wmv"); +#undef TryOpenMovieWithCodec(type) +} + +void QuickTimeVideoPlayer::openMovieFromFile() +{ + NSString *nsFilename = (NSString *)PhononCFString::toCFStringRef(mediaSourcePath()); + QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToFile:nsFilename]; + openMovieFromDataRef(dataRef); +} + +void QuickTimeVideoPlayer::openMovieFromUrl() +{ + PhononAutoReleasePool pool; + NSString *urlString = (NSString *)PhononCFString::toCFStringRef(mediaSourcePath()); + NSURL *url = [NSURL URLWithString: urlString]; + QTDataReference *dataRef = [QTDataReference dataReferenceWithReferenceToURL:url]; + openMovieFromDataRef(dataRef); +} + +void QuickTimeVideoPlayer::openMovieFromStream() +{ + m_streamReader = new QuickTimeStreamReader(m_mediaSource); + if (!m_streamReader->readAllData()) + return; + openMovieFromDataGuessType(m_streamReader->pointerToData()); +} + +MediaSource QuickTimeVideoPlayer::mediaSource() const +{ + return m_mediaSource; +} + +QTMovie *QuickTimeVideoPlayer::qtMovie() const +{ + return m_QTMovie; +} + +void QuickTimeVideoPlayer::setPlaybackRate(float rate) +{ + PhononAutoReleasePool pool; + m_playbackRateSat = true; + m_playbackRate = rate; + if (m_QTMovie) + [m_QTMovie setRate:m_playbackRate]; +} + +float QuickTimeVideoPlayer::playbackRate() const +{ + return m_playbackRate; +} + +quint64 QuickTimeVideoPlayer::currentTime() const +{ + if (!m_QTMovie || m_state == Paused) + return m_currentTime; + + PhononAutoReleasePool pool; + QTTime qtTime = [m_QTMovie currentTime]; + quint64 t = static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); + const_cast<QuickTimeVideoPlayer *>(this)->m_currentTime = t; + return m_currentTime; +} + +long QuickTimeVideoPlayer::timeScale() const +{ + if (!m_QTMovie) + return 0; + + PhononAutoReleasePool pool; + return [[m_QTMovie attributeForKey:@"QTMovieTimeScaleAttribute"] longValue]; +} + +QString QuickTimeVideoPlayer::timeToString(quint64 ms) +{ + int sec = ms/1000; + int min = sec/60; + int hour = min/60; + return QString(QLatin1String("%1:%2:%3:%4")).arg(hour%60).arg(min%60).arg(sec%60).arg(ms%1000); +} + +QString QuickTimeVideoPlayer::currentTimeString() +{ + return timeToString(currentTime()); +} + +quint64 QuickTimeVideoPlayer::duration() const +{ + if (!m_QTMovie) + return 0; + + PhononAutoReleasePool pool; + QTTime qtTime = [m_QTMovie duration]; + return static_cast<quint64>(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); +} + +void QuickTimeVideoPlayer::play() +{ + if (!canPlayMedia()) + return; + + PhononAutoReleasePool pool; + m_state = Playing; + enableAudio(m_audioEnabled); + setMute(m_mute); + [m_QTMovie setRate:m_playbackRate]; +} + +void QuickTimeVideoPlayer::pause() +{ + if (!canPlayMedia()) + return; + + PhononAutoReleasePool pool; + currentTime(); + m_state = Paused; + + if (isSeekable()) + [m_QTMovie setRate:0]; + else // pretend to be paused: + [m_QTMovie setMuted:0]; +} + +void QuickTimeVideoPlayer::seek(quint64 milliseconds) +{ + if (!canPlayMedia() || !isSeekable() || milliseconds == currentTime()) + return; + if (milliseconds > duration()) + milliseconds = duration(); + + PhononAutoReleasePool pool; + QTTime newQTTime = [m_QTMovie currentTime]; + newQTTime.timeValue = (milliseconds / 1000.0f) * newQTTime.timeScale; + [m_QTMovie setCurrentTime:newQTTime]; + + // The movie might not have been able to seek + // to the exact point we told it to. So set + // the current time according to what the movie says: + newQTTime = [m_QTMovie currentTime]; + m_currentTime = static_cast<quint64> + (float(newQTTime.timeValue) / float(newQTTime.timeScale) * 1000.0f); + + if (m_state == Paused){ + // We need (for reasons unknown) to task + // the movie twize to make sure that + // a subsequent call to frameAsCvTexture + // returns the correct frame: +#ifdef QUICKTIME_C_API_AVAILABLE + MoviesTask(0, 0); + MoviesTask(0, 0); +#elif defined(QT_MAC_USE_COCOA) + qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); +#endif + } +} + +bool QuickTimeVideoPlayer::canPlayMedia() const +{ + if (!m_QTMovie) + return false; + return m_isDrmAuthorized; +} + +bool QuickTimeVideoPlayer::isPlaying() const +{ + return m_state == Playing; +} + +bool QuickTimeVideoPlayer::isSeekable() const +{ + return canPlayMedia() && (duration()-1) != INT_MAX; +} + +float QuickTimeVideoPlayer::prefferedPlaybackRate() const +{ + if (!m_QTMovie) + return 0; + + PhononAutoReleasePool pool; + return [[m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; +} + +#ifdef QUICKTIME_C_API_AVAILABLE +void MoviePrePrerollCompleteCallBack(Movie /*theMovie*/, OSErr /*thePrerollErr*/, void * /*userData*/) +{ + // QuickTimeVideoPlayer *player = static_cast<QuickTimeVideoPlayer *>(userData); +} +#endif + +bool QuickTimeVideoPlayer::preRollMovie(qint64 startTime) +{ + if (!canPlayMedia()) + return false; + +#ifdef QUICKTIME_C_API_AVAILABLE + if (PrePrerollMovie([m_QTMovie quickTimeMovie], startTime, FloatToFixed(m_playbackRate), + 0 /*MoviePrePrerollCompleteCallBack*/, this) != noErr) // No callback means wait (synch) + return false; + + if (PrerollMovie([m_QTMovie quickTimeMovie], startTime, FloatToFixed(m_playbackRate)) != noErr) + return false; + + return true; +#else + Q_UNUSED(startTime); + return false; +#endif +} + +bool QuickTimeVideoPlayer::hasAudio() const +{ + if (!m_QTMovie) + return false; + + PhononAutoReleasePool pool; + return [[m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES; +} + +bool QuickTimeVideoPlayer::hasVideo() const +{ + return m_hasVideo; +} + +bool QuickTimeVideoPlayer::hasMovie() const +{ + return m_QTMovie != 0; +} + +void QuickTimeVideoPlayer::checkIfVideoAwailable() +{ + PhononAutoReleasePool pool; + m_hasVideo = [[m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES; +} + +bool QuickTimeVideoPlayer::isDrmProtected() const +{ + return m_isDrmProtected; +} + +bool QuickTimeVideoPlayer::isDrmAuthorized() const +{ + return m_isDrmAuthorized; +} +/* +void QuickTimeVideoPlayer::movieCodecIsMPEG() +{ + NSArray *tracks = [m_QTMovie tracks]; + for (QTTrack *track in tracks) + if ([[track media] hasCharacteristic:QTMediaTypeMPEG]) + return true; + return false; +} +*/ + +static void QtGetTrackProtection(QTTrack *track, bool &isDrmProtected, bool &isDrmAuthorized) +{ + isDrmProtected = false; + isDrmAuthorized = true; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTMedia *media = [track media]; + MediaHandler mediaHandler = GetMediaHandler([media quickTimeMedia]); + if (mediaHandler){ + // Regardless, skip message boxes pointing to iTunes regarding DRM: + Boolean boolFalse = false; + QTSetComponentProperty(mediaHandler, + kQTPropertyClass_DRM, kQTDRMPropertyID_InteractWithUser, + sizeof(boolFalse), &boolFalse); + + // Check track: + Boolean value; + OSStatus err = QTGetComponentProperty(mediaHandler, + kQTPropertyClass_DRM, kQTDRMPropertyID_IsProtected, + sizeof(value), &value, 0); + isDrmProtected = (err == noErr) ? bool(value) : false; + err = QTGetComponentProperty(mediaHandler, + kQTPropertyClass_DRM, kQTDRMPropertyID_IsAuthorized, + sizeof(value), &value, 0); + isDrmAuthorized = (err == noErr) ? bool(value) : true; + } +#else + Q_UNUSED(track); +#endif // QUICKTIME_C_API_AVAILABLE +} + +void QuickTimeVideoPlayer::readProtection() +{ + m_isDrmProtected = false; + m_isDrmAuthorized = true; + + NSArray *tracks = [m_QTMovie tracks]; + for (uint i=0; i<[tracks count]; ++i){ + QTTrack *track = [tracks objectAtIndex:i]; + bool isDrmProtected = false; + bool isDrmAuthorized = true; + QtGetTrackProtection(track, isDrmProtected, isDrmAuthorized); + if (isDrmProtected) + m_isDrmProtected = true; + if (!isDrmAuthorized) + m_isDrmAuthorized = false; + } +} + +}} + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/videoeffect.h b/src/3rdparty/phonon/qt7/videoeffect.h new file mode 100644 index 0000000..9189a6a --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoeffect.h @@ -0,0 +1,63 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_VIDEOEFFECT_H +#define Phonon_QT7_VIDEOEFFECT_H + +#include <QtCore/QList> +#include <QtCore/QString> +#include <phonon/effectinterface.h> +#include "medianode.h" +#include "videoframe.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ + class EffectParameter; + +namespace QT7 +{ + class MediaNodeEvent; + + class VideoEffect : public MediaNode, public Phonon::EffectInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::EffectInterface) + + public: + VideoEffect(int effectId, QObject *parent); + virtual ~VideoEffect(); + + QList<EffectParameter> parameters() const; + QVariant parameterValue(const EffectParameter &) const; + void setParameterValue(const EffectParameter &, const QVariant &newValue); + + void updateVideo(VideoFrame &frame); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + + private: + int effectId; + void *ciFilter; + QString filterName; + }; +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_VIDEOEFFECT_H diff --git a/src/3rdparty/phonon/qt7/videoeffect.mm b/src/3rdparty/phonon/qt7/videoeffect.mm new file mode 100644 index 0000000..c33a919 --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoeffect.mm @@ -0,0 +1,76 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "videoeffect.h" +#include "backendheader.h" +#include "objc_help.h" +#include <phonon/effect.h> +#include <phonon/effectparameter.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +VideoEffect::VideoEffect(int effectId, QObject *parent) : MediaNode(VideoSink | VideoSource, 0, parent), effectId(effectId) +{ + ciFilter = objc_createCiFilter(effectId); + if (ciFilter) + filterName = objc_getCiFilterInfo()->filterDisplayNames[effectId]; +} + +VideoEffect::~VideoEffect() +{ + if (ciFilter) + objc_releaseCiFilter(ciFilter); +} + +QList<EffectParameter> VideoEffect::parameters() const +{ + IMPLEMENTED; + return objc_getCiFilterParameterInfo(ciFilter).parameters; +} + +QVariant VideoEffect::parameterValue(const EffectParameter ¶meter) const +{ + IMPLEMENTED; + return objc_getCiFilterParameter(ciFilter, parameter.id()); +} + +void VideoEffect::setParameterValue(const EffectParameter ¶meter, const QVariant &newValue) +{ + IMPLEMENTED; + objc_setCiFilterParameter(ciFilter, parameter.id(), newValue); +} + +void VideoEffect::mediaNodeEvent(const MediaNodeEvent */*event*/) +{ +} + +void VideoEffect::updateVideo(VideoFrame &frame){ + frame.applyCoreImageFilter(ciFilter); + MediaNode::updateVideo(frame); +} + +}} + +QT_END_NAMESPACE + +#include "moc_videoeffect.cpp" + diff --git a/src/3rdparty/phonon/qt7/videoframe.h b/src/3rdparty/phonon/qt7/videoframe.h new file mode 100644 index 0000000..5069607 --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoframe.h @@ -0,0 +1,98 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_VIDEOFRAME_H +#define Phonon_QT7_VIDEOFRAME_H + +#import <QuartzCore/CVOpenGLTexture.h> +#import <AppKit/NSImage.h> +#undef check // avoid name clash; + +#include <QtCore/QRect> +#include <QtGui/QPainter> +#include <QtGui/QImage> + +QT_BEGIN_NAMESPACE + +class QGLContext; + +namespace Phonon +{ +namespace QT7 +{ + class QuickTimeVideoPlayer; + class QNSBitmapImage; + + class VideoFrame + { + public: + VideoFrame(); + VideoFrame(QuickTimeVideoPlayer *videoPlayer); + VideoFrame(const VideoFrame& frame); + void operator=(const VideoFrame& frame); + ~VideoFrame(); + + + CVOpenGLTextureRef cachedCVTexture() const; + void *cachedCIImage() const; + GLuint glTextureRef() const; + + void drawQImage(QPainter *p, const QRect &rect) const; + void drawCIImage(const CGRect &rect, float opacity = 1.0f) const; + void drawCIImage(const QRect &rect, float opacity = 1.0f) const; + void drawCVTexture(const QRect &rect, float opacity = 1.0f) const; + void drawGLTexture(const QRect &rect, float opacity = 1.0f) const; + + void applyCoreImageFilter(void *filter); + void setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation); + bool hasColorAdjustments(); + void setBaseOpacity(qreal opacity); + void setBackgroundFrame(const VideoFrame &frame); + + bool isEmpty(); + QRect frameRect() const; + QuickTimeVideoPlayer *videoPlayer(); + + void retain() const; + void release() const; + + static CGRect QRectToCGRect(const QRect & qrect); + + private: + CVOpenGLTextureRef m_cachedCVTextureRef; + void *m_cachedCIImage; + QImage m_cachedQImage; + NSBitmapImageRep *m_cachedNSBitmap; + + QuickTimeVideoPlayer *m_videoPlayer; + VideoFrame *m_backgroundFrame; + + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + qreal m_opacity; + + void initMembers(); + void copyMembers(const VideoFrame& frame); + void invalidateImage() const; + }; + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE +#endif // Phonon_QT7_VIDEOFRAME_H diff --git a/src/3rdparty/phonon/qt7/videoframe.mm b/src/3rdparty/phonon/qt7/videoframe.mm new file mode 100644 index 0000000..92a3cd5 --- /dev/null +++ b/src/3rdparty/phonon/qt7/videoframe.mm @@ -0,0 +1,378 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "videoframe.h" +#include "quicktimevideoplayer.h" +#import <QuartzCore/CIFilter.h> +#import <QuartzCore/CIContext.h> + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + + VideoFrame::VideoFrame() + { + initMembers(); + } + + VideoFrame::VideoFrame(QuickTimeVideoPlayer *videoPlayer) + { + initMembers(); + m_videoPlayer = videoPlayer; + } + + VideoFrame::VideoFrame(const VideoFrame& frame) + { + copyMembers(frame); + retain(); + } + + void VideoFrame::operator=(const VideoFrame& frame) + { + if (this == &frame) + return; + + release(); + copyMembers(frame); + retain(); + } + + void VideoFrame::initMembers() + { + m_cachedCVTextureRef = 0; + m_cachedCIImage = 0; + m_cachedNSBitmap = 0; + m_videoPlayer = 0; + m_brightness = 0; + m_contrast = 0; + m_hue = 0; + m_saturation = 0; + m_opacity = 1; + m_backgroundFrame = 0; + } + + void VideoFrame::copyMembers(const VideoFrame& frame) + { + m_cachedCVTextureRef = frame.m_cachedCVTextureRef; + m_cachedCIImage = frame.m_cachedCIImage; + m_cachedQImage = frame.m_cachedQImage; + m_cachedNSBitmap = frame.m_cachedNSBitmap; + m_videoPlayer = frame.m_videoPlayer; + m_brightness = frame.m_brightness; + m_contrast = frame.m_contrast; + m_hue = frame.m_hue; + m_saturation = frame.m_saturation; + m_opacity = frame.m_opacity; + m_backgroundFrame = frame.m_backgroundFrame; + } + + VideoFrame::~VideoFrame() + { + release(); + } + + QuickTimeVideoPlayer *VideoFrame::videoPlayer() + { + return m_videoPlayer; + } + + void VideoFrame::setBackgroundFrame(const VideoFrame &frame) + { + m_backgroundFrame = new VideoFrame(frame); + } + + QRect VideoFrame::frameRect() const + { + return m_videoPlayer->videoRect(); + } + + CVOpenGLTextureRef VideoFrame::cachedCVTexture() const + { + if (!m_cachedCVTextureRef && m_videoPlayer){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = m_videoPlayer->currentFrameAsCVTexture(); + } + return m_cachedCVTextureRef; + } + + void *VideoFrame::cachedCIImage() const + { + if (!m_cachedCIImage && m_videoPlayer){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + (const_cast<VideoFrame *>(this))->m_cachedCIImage = m_videoPlayer->currentFrameAsCIImage(); + } + return m_cachedCIImage; + } + + GLuint VideoFrame::glTextureRef() const + { + return CVOpenGLTextureGetName(cachedCVTexture()); + } + + void VideoFrame::setColors(qreal brightness, qreal contrast, qreal hue, qreal saturation) + { + if (m_backgroundFrame) + m_backgroundFrame->setColors(brightness, contrast, hue, saturation); + if (m_brightness == brightness + && m_contrast == contrast + && m_hue == hue + && m_saturation == saturation) + return; + + m_brightness = brightness; + m_contrast = contrast; + m_hue = hue; + m_saturation = saturation; + + invalidateImage(); + } + + CGRect VideoFrame::QRectToCGRect(const QRect & qrect) + { + CGRect cgrect; + cgrect.origin.x = qrect.x(); + cgrect.origin.y = qrect.y() + qrect.height(); + cgrect.size.width = qrect.width(); + cgrect.size.height = -qrect.height(); + return cgrect; + } + + bool VideoFrame::hasColorAdjustments() + { + return (m_brightness || m_contrast || m_saturation || m_hue); + } + + void VideoFrame::setBaseOpacity(qreal opacity) + { + m_opacity = opacity; + } + + void VideoFrame::drawQImage(QPainter *p, const QRect &rect) const + { + if (!m_videoPlayer) + return; +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_cachedQImage.isNull()){ + m_videoPlayer->setColors(m_brightness, m_contrast, m_hue, m_saturation); + (const_cast<VideoFrame *>(this))->m_cachedQImage = m_videoPlayer->currentFrameAsQImage(); + } +#else + // Since cocoa-64 doesn't give us OpenGL textures directly, the process of converting + // CIImges into QImages takes time. We could still call m_videoPlayer->currentFrameAsQImage(), + // but because of bitmap memory management issues, and the fact that we need to swap red and + // blue, we can optimize the process a bit here since we are going to draw immidiatly: + CIImage *img = (CIImage*)cachedCIImage(); + if (!img) + return; + + if (!m_cachedNSBitmap){ + (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = + [[NSBitmapImageRep alloc] initWithCIImage:img]; + CGRect bounds = [img extent]; + int w = bounds.size.width; + int h = bounds.size.height; + (const_cast<VideoFrame *>(this))->m_cachedQImage = + QImage([m_cachedNSBitmap bitmapData], w, h, QImage::Format_ARGB32); + // Swap red and blue (same as QImage::rgbSwapped, but without copy) + for (int i=0; i<h; ++i) { + uint *p = (uint*) m_cachedQImage.scanLine(i); + uint *end = p + w; + while (p < end) { + *p = ((*p << 16) & 0xff0000) | ((*p >> 16) & 0xff) | (*p & 0xff00ff00); + p++; + } + } + } +#endif + p->drawImage(rect, m_cachedQImage); + } + + void VideoFrame::drawCIImage(const QRect &rect, float opacity) const + { + drawCIImage(QRectToCGRect(rect), opacity); + } + + void VideoFrame::drawCIImage(const CGRect &rect, float opacity) const + { + Q_UNUSED(opacity); + CIImage *img = (CIImage *) cachedCIImage(); + if (!img) + return; + + CIContext* ciContext = [[NSGraphicsContext currentContext] CIContext]; + [ciContext drawImage:img inRect:rect fromRect:[img extent]]; + } + + void VideoFrame::drawCVTexture(const QRect &rect, float opacity) const + { + if (!m_videoPlayer) + return; + if (m_backgroundFrame) + m_backgroundFrame->drawCVTexture(rect, opacity); + + CVOpenGLTextureRef texRef = cachedCVTexture(); + if (!texRef) + return; + + glPushMatrix(); + glDisable(GL_CULL_FACE); + GLenum target = CVOpenGLTextureGetTarget(texRef); + glEnable(target); + + opacity *= m_opacity; + if (opacity < 1){ + glEnable(GL_BLEND); + glColor4f(1, 1, 1, opacity); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glColor3f(1, 1, 1); + glDisable(GL_BLEND); + } + + glBindTexture(target, CVOpenGLTextureGetName(texRef)); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + CVOpenGLTextureGetCleanTexCoords(texRef, lowerLeft, lowerRight, upperRight, upperLeft); + + glBegin(GL_QUADS); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(rect.topLeft().x(), rect.topLeft().y()); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); + glEnd(); + glPopMatrix(); + } + + void VideoFrame::drawGLTexture(const QRect &rect, float opacity) const + { + if (!m_videoPlayer) + return; + if (m_backgroundFrame) + m_backgroundFrame->drawGLTexture(rect, opacity); + + GLuint texture = m_videoPlayer->currentFrameAsGLTexture(); + if (!texture) + return; + + glPushMatrix(); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_RECTANGLE_EXT); + + opacity *= m_opacity; + if (opacity < 1){ + glEnable(GL_BLEND); + glColor4f(1, 1, 1, opacity); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glColor3f(1, 1, 1); + glDisable(GL_BLEND); + } + + glBindTexture(GL_TEXTURE_RECTANGLE_EXT, texture); + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + QRect videoRect = m_videoPlayer->videoRect(); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + lowerLeft[0] = 0; + lowerLeft[1] = videoRect.height(); + lowerRight[0] = videoRect.width(); + lowerRight[1] = videoRect.height(); + upperRight[0] = videoRect.width(); + upperRight[1] = 0; + upperLeft[0] = 0; + upperLeft[1] = 0; + + glBegin(GL_QUADS); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(rect.topLeft().x(), rect.topLeft().y()); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); + glEnd(); + glPopMatrix(); + + + // FOR NOW. FREE THE TEXTURE: + glDeleteTextures(1, &texture); + } + + bool VideoFrame::isEmpty() + { + return (m_videoPlayer == 0); + } + + void VideoFrame::invalidateImage() const + { + if (m_cachedCVTextureRef){ + CVOpenGLTextureRelease(m_cachedCVTextureRef); + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; + } + if (m_cachedCIImage){ + [(CIImage *) m_cachedCIImage release]; + (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; + } + if (m_cachedNSBitmap){ + [m_cachedNSBitmap release]; + (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; + } + (const_cast<VideoFrame *>(this))-> m_cachedQImage = QImage(); + } + + void VideoFrame::retain() const + { + if (m_cachedCVTextureRef) + CVOpenGLTextureRetain(m_cachedCVTextureRef); + if (m_cachedCIImage) + [(CIImage *) m_cachedCIImage retain]; + if (m_backgroundFrame) + m_backgroundFrame->retain(); + if (m_cachedNSBitmap) + [m_cachedNSBitmap retain]; + } + + void VideoFrame::release() const + { + if (m_cachedCVTextureRef) + CVOpenGLTextureRelease(m_cachedCVTextureRef); + if (m_cachedCIImage) + [(CIImage *) m_cachedCIImage release]; + if (m_backgroundFrame) + m_backgroundFrame->release(); + if (m_cachedNSBitmap) + [m_cachedNSBitmap release]; + + (const_cast<VideoFrame *>(this))->m_backgroundFrame = 0; + (const_cast<VideoFrame *>(this))->m_cachedCVTextureRef = 0; + (const_cast<VideoFrame *>(this))->m_cachedCIImage = 0; + (const_cast<VideoFrame *>(this))->m_cachedNSBitmap = 0; + } + +}} //namespace Phonon::QT7 + +QT_END_NAMESPACE diff --git a/src/3rdparty/phonon/qt7/videowidget.h b/src/3rdparty/phonon/qt7/videowidget.h new file mode 100644 index 0000000..8d084ec --- /dev/null +++ b/src/3rdparty/phonon/qt7/videowidget.h @@ -0,0 +1,71 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef Phonon_QT7_VIDEOWIDGET_H +#define Phonon_QT7_VIDEOWIDGET_H + +#include <QtGui/QPaintEngine> +#include <phonon/videowidgetinterface.h> +#include "medianode.h" + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + class MediaNodeEvent; + class VideoRenderWidget; + + class VideoWidget : public MediaNode, public Phonon::VideoWidgetInterface + { + Q_OBJECT + Q_INTERFACES(Phonon::VideoWidgetInterface) + + public: + VideoWidget(QObject *parent); + virtual ~VideoWidget(); + + Phonon::VideoWidget::AspectRatio aspectRatio() const; + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio); + qreal brightness() const; + void setBrightness(qreal); + Phonon::VideoWidget::ScaleMode scaleMode() const; + void setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode); + qreal contrast() const; + void setContrast(qreal); + qreal hue() const; + void setHue(qreal); + qreal saturation() const; + void setSaturation(qreal); + + QWidget *widget(); + + void updateVideo(VideoFrame &frame); + + protected: + void mediaNodeEvent(const MediaNodeEvent *event); + + private: + VideoRenderWidget *m_videoRenderWidget; + }; + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#endif // Phonon_QT7_VIDEOWIDGET_H diff --git a/src/3rdparty/phonon/qt7/videowidget.mm b/src/3rdparty/phonon/qt7/videowidget.mm new file mode 100644 index 0000000..0600268 --- /dev/null +++ b/src/3rdparty/phonon/qt7/videowidget.mm @@ -0,0 +1,885 @@ +/* This file is part of the KDE project. + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2.1 or 3 of the License. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <QtCore/qglobal.h> +#ifdef QT_MAC_USE_COCOA +#import <QTKit/QTMovieLayer.h> +#endif + +#include "videowidget.h" +#include "backendheader.h" +#include "quicktimevideoplayer.h" +#include "medianode.h" +#include "medianodeevent.h" +#include "mediaobject.h" + +#include <QtOpenGL/QGLWidget> +#include <QtCore/QTime> +#include <QtCore/QEvent> +#include <QtCore/QCoreApplication> + +#import <AppKit/NSImage.h> +#import <QTKit/QTMovieView.h> + +///////////////////////////////////////////////////////////////////////////////////////// + +#ifdef QT_MAC_USE_COCOA // Rendering to a QTMovieView can only be done in Cocoa + +#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} + +@interface SharedQTMovieView : QTMovieView +{ +@private + Phonon::QT7::QuickTimeVideoPlayer *m_player; + QList<QWidget *> *m_parents; + QWidget *m_window; + QRect *m_drawRect; + bool m_newImageReady; + bool m_usingWindow; +} + +- (SharedQTMovieView *) init; +- (void) registerParent:(QWidget *)parent; +- (void) unregisterParent:(QWidget *)parent; +- (void) setDrawRect:(QRect &)rect; +- (void) drawVideoFrame:(Phonon::QT7::VideoFrame &)frame forWidget:(QWidget *)widget shareImages:(bool)share; +- (void) useOffscreenWindow:(bool)offscreen; +- (void) applyDrawRectOnSelf; +@end + +///////////////////////////////////////////////////////////////////////////////////////// + +@implementation SharedQTMovieView + +- (SharedQTMovieView *) init +{ + self = [super initWithFrame:NSZeroRect]; + if (self){ + [self setControllerVisible:NO]; + m_parents = new QList<QWidget *>(); + m_drawRect = new QRect(0, 0, 1, 1); + [self applyDrawRectOnSelf]; + m_usingWindow = false; + } + return self; +} + +- (void) dealloc +{ + Phonon::QT7::PhononAutoReleasePool pool; + delete m_window; + delete m_drawRect; + delete m_parents; + [super dealloc]; +} + +- (void) applyDrawRectOnSelf +{ + NSRect nsrect; + nsrect.origin.x = m_drawRect->x(); + nsrect.origin.y = m_drawRect->y(); + nsrect.size.width = m_drawRect->width(); + nsrect.size.height = m_drawRect->height(); + [self setFrame:nsrect]; +} + +- (void) setDrawRect:(QRect &)rect +{ + *m_drawRect = rect; + if (!m_usingWindow) + [self applyDrawRectOnSelf]; +} + +- (void) waitForFrame +{ + if (m_usingWindow){ + QTMovie *movie = [self movie]; + if (movie){ + // CIImages are expected, but not received. + // Try to wait a couple of seconds for them: + m_newImageReady = false; + float rate = [movie rate]; + if (!rate) + [movie setRate:1]; + QTime t; t.start(); + while (!m_newImageReady && t.elapsed() < 2000) + ; + [movie setRate:rate]; + } + } +} + +- (void) useOffscreenWindow:(bool)offscreen +{ + if (offscreen == m_usingWindow) + return; + if (offscreen){ + if (!m_window){ + m_window = new QWidget; + m_window->setWindowOpacity(0.0); + m_window->show(); + m_window->hide(); + } + m_usingWindow = true; + [self setDelegate:self]; + [self waitForFrame]; + foreach(QWidget *w, *m_parents) + w->repaint(); + qApp->processEvents(); + [self removeFromSuperview]; + [(NSView *)m_window->winId() addSubview:self]; + } else if (!m_parents->isEmpty()){ + m_usingWindow = false; + [self removeFromSuperview]; + [(NSView*)m_parents->first()->winId() addSubview:self]; + [self setDelegate:0]; + [self setDrawRect:*m_drawRect]; + } +} + +- (void) drawVideoFrame:(Phonon::QT7::VideoFrame &)frame forWidget:(QWidget *)widget shareImages:(bool)share; +{ + // Detect if the video that produces the frame has changed: + Phonon::QT7::QuickTimeVideoPlayer *player = frame.videoPlayer(); + if (player && player->qtMovie() != [self movie]){ + m_player = player; + [self setMovie:player->qtMovie()]; + [self waitForFrame]; + } + + [self useOffscreenWindow:(share || m_parents->size() > 1)]; + if (m_usingWindow) + widget->update(); +} + +// Override this method so that the movie doesn't stop if +// the window becomes invisible +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + Q_UNUSED(newWindow); +} + +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img +{ + // This method is called from QTMovieView just + // before the image will be drawn. + Q_UNUSED(view); + m_player->setPrimaryRenderingCIImage(img); + m_newImageReady = true; + return img; +} + +- (void) registerParent:(QWidget *)parent +{ + if (m_parents->contains(parent)) + return; + m_parents->append(parent); + if (m_parents->size() == 1){ + Phonon::QT7::PhononAutoReleasePool pool; + m_usingWindow = true; + [self applyDrawRectOnSelf]; + [self useOffscreenWindow:NO]; + } +} + +- (void) unregisterParent:(QWidget *)parent +{ + m_parents->removeAll(parent); + if (m_parents->size() == 1) + [self applyDrawRectOnSelf]; +} + +VIDEO_TRANSPARENT(mouseDown); +VIDEO_TRANSPARENT(mouseDragged); +VIDEO_TRANSPARENT(mouseUp); +VIDEO_TRANSPARENT(mouseMoved); +VIDEO_TRANSPARENT(mouseEntered); +VIDEO_TRANSPARENT(mouseExited); +VIDEO_TRANSPARENT(rightMouseDown); +VIDEO_TRANSPARENT(rightMouseDragged); +VIDEO_TRANSPARENT(rightMouseUp); +VIDEO_TRANSPARENT(otherMouseDown); +VIDEO_TRANSPARENT(otherMouseDragged); +VIDEO_TRANSPARENT(otherMouseUp); +VIDEO_TRANSPARENT(keyDown); +VIDEO_TRANSPARENT(keyUp); +VIDEO_TRANSPARENT(scrollWheel) + +@end + +#endif // QT_MAC_USE_COCOA + +///////////////////////////////////////////////////////////////////////////////////////// + +QT_BEGIN_NAMESPACE + +namespace Phonon +{ +namespace QT7 +{ + +class IVideoRenderDrawWidget +{ +public: + virtual ~IVideoRenderDrawWidget(){} + virtual void setVideoFrame(VideoFrame &) = 0; + virtual void setDrawFrameRect(const QRect &) = 0; + virtual void updateVideoOutputCount(int){} + virtual void setMovieIsPaused(bool){} +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +QGLWidget *PhononSharedQGLWidget(){ + static QGLWidget *glWidget = 0; + if (!glWidget) + glWidget = new QGLWidget(); + return glWidget; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class RenderOpenGL : public QGLWidget, public IVideoRenderDrawWidget +{ +public: + VideoFrame m_currentFrame; + QRect m_drawFrameRect; + + RenderOpenGL(QWidget *parent, const QGLFormat &format, const QSize &size) : QGLWidget(format, parent, PhononSharedQGLWidget()) + { + resize(size); + setAutoFillBackground(false); + show(); + } + + void initializeGL() + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + } + + void resizeGL(int w, int h) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, GLsizei(w), GLsizei(h)); + glOrtho(0, GLsizei(w), 0, GLsizei(h), -1, 1); + updateGL(); + } + + void paintGL() + { + glClear(GL_COLOR_BUFFER_BIT); + m_currentFrame.drawCVTexture(m_drawFrameRect); + } + + void setVideoFrame(VideoFrame &frame) + { + m_currentFrame = frame; + makeCurrent(); + paintGL(); + swapBuffers(); + } + + void setDrawFrameRect(const QRect &rect) + { + m_drawFrameRect = rect; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class RenderQTMovieView : public QWidget, public IVideoRenderDrawWidget +{ +public: +#if defined(QT_MAC_USE_COCOA) + QRect m_drawRect; + VideoFrame m_videoFrame; + SharedQTMovieView *m_currentView; + bool m_setDrawRectPending; + bool m_share; + + RenderQTMovieView(bool share, QWidget *parent, const QSize &size=QSize()) : QWidget(parent), m_currentView(0) + { + m_setDrawRectPending = true; + m_share = share; + setAutoFillBackground(false); + if (share){ + // In 'share' mode, this widget will only make sure + // that CIIImages are produced, and not actually + // draw anything: + hide(); + } else { + resize(size); + show(); + } + } + + ~RenderQTMovieView() + { + [m_currentView unregisterParent:this]; + } + + void showEvent(QShowEvent *) + { + if (m_share) + return; + [m_currentView registerParent:this]; + } + + void hideEvent(QHideEvent *) + { + if (m_share) + return; + [m_currentView unregisterParent:this]; + } + + void paintEvent(QPaintEvent *) + { + if (m_share) + return; + QPainter p(this); + p.fillRect(rect(), Qt::black); + m_videoFrame.drawCIImage(m_drawRect); + } + + void updateVideoOutputCount(int count) + { + Q_UNUSED(count); + } + + void setMovieIsPaused(bool paused) + { + Q_UNUSED(paused); + } + + void setVideoFrame(VideoFrame &frame) + { + m_videoFrame = frame; + + if (!m_videoFrame.isEmpty()){ + Phonon::QT7::QuickTimeVideoPlayer *player = m_videoFrame.videoPlayer(); + if (!player->m_primaryRenderingTarget){ + // First movie view. Create the shared resource: + SharedQTMovieView *view = [[[SharedQTMovieView alloc] init] autorelease]; + player->setPrimaryRenderingTarget(view); + } + + SharedQTMovieView *view = static_cast<SharedQTMovieView *>(player->m_primaryRenderingTarget); + if (!m_share && view != m_currentView){ + [m_currentView unregisterParent:this]; + m_currentView = view; + [m_currentView registerParent:this]; + } + + [view drawVideoFrame:m_videoFrame forWidget:this shareImages:m_share || m_videoFrame.hasColorAdjustments()]; + + if (m_setDrawRectPending){ + m_setDrawRectPending = false; + [view setDrawRect:m_drawRect]; + } + } + } + + void setDrawFrameRect(const QRect &rect) + { + m_drawRect = rect; + Phonon::QT7::QuickTimeVideoPlayer *player = m_videoFrame.videoPlayer(); + if (player && player->m_primaryRenderingTarget){ + SharedQTMovieView *view = static_cast<SharedQTMovieView *>(player->m_primaryRenderingTarget); + [view setDrawRect:m_drawRect]; + } else + m_setDrawRectPending = true; + } + +#else // QT_MAC_USE_COCOA == false + RenderQTMovieView(bool, QWidget *, const QSize& = QSize()){} + void setVideoFrame(VideoFrame &){} + void setDrawFrameRect(const QRect &){} +#endif +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class RenderQTMovieLayer : public QWidget, public IVideoRenderDrawWidget +{ +public: +#ifdef QT_MAC_USE_COCOA + QTMovieLayer *m_movieLayer; + + RenderQTMovieLayer(QWidget *parent, const QSize&) : QWidget(parent) + { + PhononAutoReleasePool pool; + setAutoFillBackground(false); + m_movieLayer = 0; + [(NSView *)winId() setWantsLayer:YES]; + } + + void setVideoFrame(VideoFrame &frame) + { + QuickTimeVideoPlayer *player = frame.videoPlayer(); + if (!player || player->qtMovie() == [m_movieLayer movie]) + return; + + if (m_movieLayer) + [m_movieLayer setMovie:player->qtMovie()]; + else { + m_movieLayer = [QTMovieLayer layerWithMovie:player->qtMovie()]; + [(NSView *)winId() setLayer:m_movieLayer]; + } + } + + void setDrawFrameRect(const QRect &rect) + { + CGRect frame = m_movieLayer.frame; + frame.origin.x = rect.x(); + frame.origin.y = rect.y(); + frame.size.width = rect.width(); + frame.size.height = rect.height(); + m_movieLayer.frame = frame; + } + +#else // QT_MAC_USE_COCOA == false + RenderQTMovieLayer(QWidget *, const QSize&){} + void setVideoFrame(VideoFrame &){} + void setDrawFrameRect(const QRect &){} +#endif +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +class VideoRenderWidget : public QWidget +{ +public: + enum RenderSystem { RS_NoRendering = 0, + RS_QGLWidget = 1, + RS_QPainter = 2, + RS_CIImage = 3, + RS_CVTexture = 4, + RS_QImage = 5, + RS_QTMovieView = 6, + RS_QTMovieLayer = 7 + } m_renderSystem; + + VideoFrame m_currentFrame; + QRect m_movieFrameRect; + QRect m_drawFrameRect; + Phonon::VideoWidget::ScaleMode m_scaleMode; + Phonon::VideoWidget::AspectRatio m_aspect; + IVideoRenderDrawWidget *m_renderDrawWidget; + + qreal m_brightness; + qreal m_contrast; + qreal m_hue; + qreal m_saturation; + qreal m_opacity; + + VideoRenderWidget() : QWidget(0), + m_scaleMode(Phonon::VideoWidget::FitInView), m_aspect(Phonon::VideoWidget::AspectRatioAuto) + { + PhononAutoReleasePool pool; + m_brightness = 0; + m_contrast = 0; + m_hue = 0; + m_saturation = 0; + m_opacity = 1; + m_renderDrawWidget = 0; + m_renderSystem = RS_NoRendering; + + setAutoFillBackground(false); + updateDrawFrameRect(); + } + + RenderSystem selectBestRenderSystem(){ + if (!isVisible()) + return RS_NoRendering; + else if (window() && window()->testAttribute(Qt::WA_DontShowOnScreen)) + return RS_QPainter; + else { +#ifdef QUICKTIME_C_API_AVAILABLE + return RS_QGLWidget; +#else + return RS_QTMovieView; +#endif + } + } + + void setRenderSystem(RenderSystem renderSystem){ + PhononAutoReleasePool pool; + static QString userSystem = qgetenv("PHONON_RENDER_SYSTEM"); + if (!userSystem.isEmpty()) + renderSystem = RenderSystem(userSystem.toInt()); + + if (m_renderSystem == renderSystem) + return; + + m_renderSystem = renderSystem; + if (m_renderDrawWidget){ + delete m_renderDrawWidget; + m_renderDrawWidget = 0; + } + + switch (m_renderSystem){ + case RS_QGLWidget:{ + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Vertical sync (avoid tearing) + m_renderDrawWidget = new RenderOpenGL(this, format, size()); + break;} + case RS_QTMovieView:{ + m_renderDrawWidget = new RenderQTMovieView(false, this, size()); + break;} + case RS_QTMovieLayer:{ + m_renderDrawWidget = new RenderQTMovieLayer(this, size()); + break;} + case RS_QPainter: + case RS_CIImage: + case RS_CVTexture: + case RS_QImage: +#ifndef QUICKTIME_C_API_AVAILABLE + // On cocoa-64, let QTMovieView produce + // video frames for us: + m_renderDrawWidget = new RenderQTMovieView(true, this); +#endif + break; + case RS_NoRendering: + break; + } + + if (m_renderDrawWidget){ + m_renderDrawWidget->setVideoFrame(m_currentFrame); + m_renderDrawWidget->setDrawFrameRect(m_drawFrameRect); + } + } + + QSize sizeHint() const + { + return m_movieFrameRect.size(); + } + + bool event(QEvent *event) + { + switch (event->type()){ + // Try to detect if one of this objects + // anchestors might have changed: + case QEvent::Resize:{ + PhononAutoReleasePool pool; + updateDrawFrameRect(); + if (m_renderDrawWidget) + dynamic_cast<QWidget *>(m_renderDrawWidget)->resize(size()); + break; } + case QEvent::Paint:{ + PhononAutoReleasePool pool; + float opacity = parentWidget() ? parentWidget()->windowOpacity() : 1; + switch (m_renderSystem){ + case RS_QPainter:{ + QPainter p(this); + p.fillRect(rect(), Qt::black); + if (p.paintEngine()->type() == QPaintEngine::OpenGL) + m_currentFrame.drawCVTexture(m_drawFrameRect, opacity); + else + m_currentFrame.drawQImage(&p, m_drawFrameRect); + break; } + case RS_CIImage: + m_currentFrame.drawCIImage(m_drawFrameRect, opacity); + break; + case RS_CVTexture: + m_currentFrame.drawCVTexture(m_drawFrameRect, opacity); + break; + case RS_QImage:{ + QPainter p(this); + p.fillRect(rect(), Qt::black); + m_currentFrame.drawQImage(&p, m_drawFrameRect); + break; } + case RS_QGLWidget: + case RS_QTMovieView: + case RS_QTMovieLayer: + // draw in separate widget + break; + case RS_NoRendering: + QPainter p(this); + p.fillRect(rect(), Qt::black); + break; + } + break; } + default: + break; + } + + return QWidget::event(event); + } + + void setVideoFrame(VideoFrame &frame) + { + PhononAutoReleasePool pool; + m_currentFrame = frame; + m_currentFrame.setColors(m_brightness, m_contrast, m_hue, m_saturation); + + if (m_renderDrawWidget) + m_renderDrawWidget->setVideoFrame(m_currentFrame); + + setRenderSystem(selectBestRenderSystem()); + switch (m_renderSystem){ + case RS_QGLWidget: + case RS_QTMovieView: + case RS_QTMovieLayer: + case RS_NoRendering: + break; + case RS_CIImage: + case RS_CVTexture: + case RS_QImage: + case RS_QPainter: + repaint(); + break; + } + } + + void updateVideoFrame() + { + setVideoFrame(m_currentFrame); + } + + void setMovieRect(const QRect &mrect) + { + if (mrect == m_movieFrameRect) + return; + m_movieFrameRect = mrect; + updateDrawFrameRect(); + updateGeometry(); + if (isVisible()) + qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers); + } + + void setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode) + { + m_scaleMode = scaleMode; + updateDrawFrameRect(); + updateVideoFrame(); + repaint(); + } + + void setAspectRatio(Phonon::VideoWidget::AspectRatio aspect) + { + m_aspect = aspect; + updateDrawFrameRect(); + updateVideoFrame(); + repaint(); + } + + void updateVideoOutputCount(int count) + { + if (m_renderDrawWidget) + m_renderDrawWidget->updateVideoOutputCount(count); + } + + void setMovieIsPaused(bool paused) + { + if (m_renderDrawWidget) + m_renderDrawWidget->setMovieIsPaused(paused); + } + + void updateDrawFrameRect() + { + if (m_movieFrameRect.width() <= 0 || m_movieFrameRect.height() <= 0) + m_movieFrameRect = QRect(0, 0, 640, 480); + + // Set m_drawFrameRect to be the size of the smallest possible + // rect conforming to the aspect and containing the whole frame: + switch(m_aspect){ + case Phonon::VideoWidget::AspectRatioWidget: + m_drawFrameRect = rect(); + break; + case Phonon::VideoWidget::AspectRatio4_3: + m_drawFrameRect = scaleToAspect(m_movieFrameRect, 4, 3); + break; + case Phonon::VideoWidget::AspectRatio16_9: + m_drawFrameRect = scaleToAspect(m_movieFrameRect, 16, 9); + break; + case Phonon::VideoWidget::AspectRatioAuto: + default: + m_drawFrameRect = m_movieFrameRect; + break; + } + + // Scale m_drawFrameRect to fill the widget + // without breaking aspect: + int widgetWidth = rect().width(); + int widgetHeight = rect().height(); + int frameWidth = widgetWidth; + int frameHeight = m_drawFrameRect.height() * float(widgetWidth) / float(m_drawFrameRect.width()); + + switch(m_scaleMode){ + case Phonon::VideoWidget::ScaleAndCrop: + if (frameHeight < widgetHeight){ + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + case Phonon::VideoWidget::FitInView: + default: + if (frameHeight > widgetHeight){ + frameWidth *= float(widgetHeight) / float(frameHeight); + frameHeight = widgetHeight; + } + break; + } + + m_drawFrameRect.setSize(QSize(frameWidth, frameHeight)); + m_drawFrameRect.moveTo((widgetWidth - frameWidth) / 2.0f, (widgetHeight - frameHeight) / 2.0f); + + if (m_renderDrawWidget) + m_renderDrawWidget->setDrawFrameRect(m_drawFrameRect); + } + + QRect scaleToAspect(QRect srcRect, int w, int h) + { + int width = srcRect.width(); + int height = srcRect.width() * (float(h) / float(w)); + if (height > srcRect.height()){ + height = srcRect.height(); + width = srcRect.height() * (float(w) / float(h)); + } + return QRect(0, 0, width, height); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +VideoWidget::VideoWidget(QObject *parent) : MediaNode(VideoSink, parent) +{ + m_videoRenderWidget = new VideoRenderWidget(); +} + +VideoWidget::~VideoWidget() +{ + delete m_videoRenderWidget; +} + +QWidget *VideoWidget::widget() +{ + IMPLEMENTED; + return m_videoRenderWidget; +} + +Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_aspect; +} + +void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspect) +{ + IMPLEMENTED; + m_videoRenderWidget->setAspectRatio(aspect); +} + +Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_scaleMode; +} + +void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode) +{ + IMPLEMENTED; + m_videoRenderWidget->setScaleMode(scaleMode); +} + +qreal VideoWidget::brightness() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_brightness; +} + +void VideoWidget::setBrightness(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_brightness = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +qreal VideoWidget::contrast() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_contrast; +} + +void VideoWidget::setContrast(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_contrast = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +qreal VideoWidget::hue() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_hue; +} + +void VideoWidget::setHue(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_hue = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +qreal VideoWidget::saturation() const +{ + IMPLEMENTED; + return m_videoRenderWidget->m_saturation; +} + +void VideoWidget::setSaturation(qreal value) +{ + IMPLEMENTED; + m_videoRenderWidget->m_saturation = value; + if (m_owningMediaObject && m_owningMediaObject->state() == Phonon::PausedState) + m_videoRenderWidget->updateVideoFrame(); +} + +void VideoWidget::mediaNodeEvent(const MediaNodeEvent *event) +{ + switch (event->type()){ + case MediaNodeEvent::VideoFrameSizeChanged: + m_videoRenderWidget->setMovieRect(*static_cast<QRect *>(event->data())); + break; + case MediaNodeEvent::VideoOutputCountChanged: + m_videoRenderWidget->updateVideoOutputCount(*static_cast<int *>(event->data())); + break; + case MediaNodeEvent::MediaPlaying: + m_videoRenderWidget->setMovieIsPaused(!(*static_cast<bool *>(event->data()))); + break; + default: + break; + } +} + +void VideoWidget::updateVideo(VideoFrame &frame){ + PhononAutoReleasePool pool; + m_videoRenderWidget->setVideoFrame(frame); + MediaNode::updateVideo(frame); +} + +}} // namespace Phonon::QT7 + +QT_END_NAMESPACE + +#include "moc_videowidget.cpp" |