phonon video effects

Christoph Pfister christophpfister at gmail.com
Sun Oct 4 18:52:52 CEST 2009


hi guys

i've written a patch to support video effects in phonon (similar to
the existing audio effects), for example useful for deinterlacing.
unfortunately, the (ex-)trolls want support for at least three
backends (my patch provides only support for xine) to let it into the
qt phonon tree ... anybody interested in extending this patch?

thanks,

christoph
-------------- next part --------------
Index: phonon/backendcapabilities.h
===================================================================
--- phonon/backendcapabilities.h	(Revision 1030914)
+++ phonon/backendcapabilities.h	(Arbeitskopie)
@@ -172,13 +172,15 @@
     PHONON_EXPORT QList<EffectDescription> availableAudioEffects();
 #endif //QT_NO_PHONON_EFFECT
 
-//X     /**
-//X      * Returns descriptions for the video effects the backend supports.
-//X      *
-//X      * \return A list of VideoEffectDescription objects that give a name and
-//X      * description for every supported video effect.
-//X      */
-//X     PHONON_EXPORT QList<EffectDescription> availableVideoEffects();
+    /**
+     * Returns descriptions for the video effects the backend supports.
+     *
+     * \return A list of EffectDescription objects that give a name and
+     * description for every supported video effect.
+     */
+#ifndef QT_NO_PHONON_EFFECT
+    PHONON_EXPORT QList<EffectDescription> availableVideoEffects();
+#endif //QT_NO_PHONON_EFFECT
 
     /**
      * Returns descriptions for the audio codecs the backend supports.
Index: phonon/objectdescription.h
===================================================================
--- phonon/objectdescription.h	(Revision 1030914)
+++ phonon/objectdescription.h	(Arbeitskopie)
@@ -76,8 +76,10 @@
          * devices even when they are unplugged and provide a unique identifier
          * that can make backends use the same identifiers.
          */
-        AudioCaptureDeviceType
+        AudioCaptureDeviceType,
 
+        VideoEffectType
+
         //VideoOutputDeviceType,
         //VideoCaptureDeviceType,
         //AudioCodecType,
@@ -188,6 +190,13 @@
             return ObjectDescription<T>(QExplicitlySharedDataPointer<ObjectDescriptionData>(ObjectDescriptionData::fromIndex(T, index)));
         }
 
+        /** \internal
+         * This function is needed because video and audio effects share the EffectDescription class.
+         */
+        static inline ObjectDescription<T> fromIndex(int index, ObjectDescriptionType type) { //krazy:exclude=inline
+            return ObjectDescription<T>(QExplicitlySharedDataPointer<ObjectDescriptionData>(ObjectDescriptionData::fromIndex(type, index)));
+        }
+
         /**
          * Returns \c true if this ObjectDescription describes the same
          * as \p otherDescription; otherwise returns \c false.
Index: phonon/backendcapabilities.cpp
===================================================================
--- phonon/backendcapabilities.cpp	(Revision 1030914)
+++ phonon/backendcapabilities.cpp	(Arbeitskopie)
@@ -110,6 +110,21 @@
 }
 #endif //QT_NO_PHONON_EFFECT
 
+#ifndef QT_NO_PHONON_EFFECT
+QList<EffectDescription> BackendCapabilities::availableVideoEffects()
+{
+    BackendInterface *backendIface = qobject_cast<BackendInterface *>(Factory::backend());
+    QList<EffectDescription> ret;
+    if (backendIface) {
+        QList<int> deviceIndexes = backendIface->objectDescriptionIndexes(Phonon::VideoEffectType);
+        foreach (int i, deviceIndexes) {
+            ret.append(EffectDescription::fromIndex(i, Phonon::VideoEffectType));
+        }
+    }
+    return ret;
+}
+#endif //QT_NO_PHONON_EFFECT
+
 } // namespace Phonon
 
 QT_END_NAMESPACE
Index: phonon/effect.cpp
===================================================================
--- phonon/effect.cpp	(Revision 1030914)
+++ phonon/effect.cpp	(Arbeitskopie)
@@ -119,9 +119,8 @@
     Q_ASSERT(m_backendObject);
 
     // set up attributes
-    const QList<EffectParameter> parameters = pINTERFACE_CALL(parameters());
-    foreach (const EffectParameter &p, parameters) {
-        pINTERFACE_CALL(setParameterValue(p, parameterValues[p]));
+    for (QHash<EffectParameter, QVariant>::const_iterator it = parameterValues.constBegin(); it != parameterValues.constEnd(); ++it) {
+        pINTERFACE_CALL(setParameterValue(it.key(), it.value()));
     }
 }
 
Index: xine/effect.h
===================================================================
--- xine/effect.h	(Revision 1030914)
+++ xine/effect.h	(Arbeitskopie)
@@ -44,11 +44,14 @@
         EffectXT(const char *name);
         ~EffectXT();
         xine_audio_port_t *audioPort() const;
+        xine_video_port_t *videoPort() const;
         xine_post_out_t *audioOutputPort() const;
+        xine_post_out_t *videoOutputPort() const;
         void rewireTo(SourceNodeXT *source);
         virtual void createInstance();
     protected:
         xine_audio_port_t *fakeAudioPort();
+        xine_video_port_t *fakeVideoPort();
 
         xine_post_t *m_plugin;
         xine_post_api_t *m_pluginApi;
@@ -57,10 +60,12 @@
         void ensureInstance();
 
         xine_audio_port_t *m_fakeAudioPort;
+        xine_video_port_t *m_fakeVideoPort;
         mutable QMutex m_mutex;
         const char *m_pluginName;
         char *m_pluginParams;
         QList<Phonon::EffectParameter> m_parameterList;
+        bool m_isVideoPlugin;
 };
 
 class Effect : public QObject, public EffectInterface, public SinkNode, public SourceNode
Index: xine/xinestream.h
===================================================================
--- xine/xinestream.h	(Revision 1030914)
+++ xine/xinestream.h	(Arbeitskopie)
@@ -207,7 +207,6 @@
 
         xine_stream_t *m_stream;
         xine_event_queue_t *m_event_queue;
-        xine_post_t *m_deinterlacer;
         mutable XineEngine m_xine;
         mutable xine_audio_port_t *m_nullAudioPort;
         mutable xine_video_port_t *m_nullVideoPort;
Index: xine/xinestream.cpp
===================================================================
--- xine/xinestream.cpp	(Revision 1030914)
+++ xine/xinestream.cpp	(Arbeitskopie)
@@ -143,7 +143,6 @@
     SourceNodeXT("MediaObject"),
     m_stream(0),
     m_event_queue(0),
-    m_deinterlacer(0),
     m_xine(Backend::xineEngineForStream()),
     m_nullAudioPort(0),
     m_nullVideoPort(0),
@@ -180,9 +179,6 @@
 XineStream::~XineStream()
 {
     Q_ASSERT(QThread::currentThread() == XineThread::instance());
-    if (m_deinterlacer) {
-        xine_post_dispose(m_xine, m_deinterlacer);
-    }
     if(m_event_queue) {
         xine_event_dispose_queue(m_event_queue);
         m_event_queue = 0;
@@ -288,60 +284,7 @@
         return false;
     }
     debug() << Q_FUNC_INFO << "xine_open succeeded for m_mrl =" << m_mrl.constData();
-    const bool needDeinterlacer =
-        (m_mrl.startsWith("dvd:/") && Backend::deinterlaceDVD()) ||
-        (m_mrl.startsWith("vcd:/") && Backend::deinterlaceVCD()) ||
-        (m_mrl.startsWith("file:/") && Backend::deinterlaceFile());
-    if (m_deinterlacer) {
-        if (!needDeinterlacer) {
-            xine_post_dispose(m_xine, m_deinterlacer);
-            m_deinterlacer = 0;
-        }
-    } else if (needDeinterlacer) {
-        xine_video_port_t *videoPort = 0;
-        Q_ASSERT(m_mediaObject);
-        QSet<SinkNode *> sinks = m_mediaObject->sinks();
-        foreach (SinkNode *sink, sinks) {
-            Q_ASSERT(sink->threadSafeObject());
-            if (sink->threadSafeObject()->videoPort()) {
-                Q_ASSERT(videoPort == 0);
-                videoPort = sink->threadSafeObject()->videoPort();
-            }
-        }
-        if (!videoPort) {
-            debug() << Q_FUNC_INFO << "creating xine_stream with null video port";
-            videoPort = nullVideoPort();
-        }
-        m_deinterlacer = xine_post_init(m_xine, "tvtime", 1, 0, &videoPort);
-        if (m_deinterlacer) {
-            // set method
-            xine_post_in_t *paraInput = xine_post_input(m_deinterlacer, "parameters");
-            Q_ASSERT(paraInput);
-            Q_ASSERT(paraInput->data);
-            xine_post_api_t *api = reinterpret_cast<xine_post_api_t *>(paraInput->data);
-            xine_post_api_descr_t *desc = api->get_param_descr();
-            char *pluginParams = static_cast<char *>(malloc(desc->struct_size));
-            api->get_parameters(m_deinterlacer, pluginParams);
-            for (int i = 0; desc->parameter[i].type != POST_PARAM_TYPE_LAST; ++i) {
-                xine_post_api_parameter_t &p = desc->parameter[i];
-                if (p.type == POST_PARAM_TYPE_INT && 0 == strcmp(p.name, "method")) {
-                    int *value = reinterpret_cast<int *>(pluginParams + p.offset);
-                    *value = Backend::deinterlaceMethod();
-                    break;
-                }
-            }
-            api->set_parameters(m_deinterlacer, pluginParams);
-            free(pluginParams);
 
-            // connect to xine_stream_t
-            xine_post_in_t *x = xine_post_input(m_deinterlacer, "video");
-            Q_ASSERT(x);
-            xine_post_out_t *videoOutputPort = xine_get_video_source(m_stream);
-            Q_ASSERT(videoOutputPort);
-            xine_post_wire(videoOutputPort, x);
-        }
-    }
-
     m_lastTimeUpdate.tv_sec = 0;
     xine_get_pos_length(m_stream, 0, &m_currentTime, &m_totalTime);
     getStreamInfo();
@@ -1417,10 +1360,6 @@
         return true;
     case Event::UnloadCommand:
         ev->accept();
-        if (m_deinterlacer) {
-            xine_post_dispose(m_xine, m_deinterlacer);
-            m_deinterlacer = 0;
-        }
         if(m_event_queue) {
             xine_event_dispose_queue(m_event_queue);
             m_event_queue = 0;
@@ -1697,9 +1636,6 @@
     if (!m_stream) {
         return 0;
     }
-    if (m_deinterlacer) {
-        return xine_post_output(m_deinterlacer, "deinterlaced video");
-    }
     return xine_get_video_source(m_stream);
 }
 
Index: xine/effect.cpp
===================================================================
--- xine/effect.cpp	(Revision 1030914)
+++ xine/effect.cpp	(Arbeitskopie)
@@ -33,6 +33,10 @@
 
 xine_audio_port_t *EffectXT::audioPort() const
 {
+    if (m_isVideoPlugin) {
+        return 0;
+    }
+
     const_cast<EffectXT *>(this)->ensureInstance();
     Q_ASSERT(m_plugin);
     Q_ASSERT(m_plugin->audio_input);
@@ -40,6 +44,19 @@
     return m_plugin->audio_input[0];
 }
 
+xine_video_port_t *EffectXT::videoPort() const
+{
+    if (!m_isVideoPlugin) {
+        return 0;
+    }
+
+    const_cast<EffectXT *>(this)->ensureInstance();
+    Q_ASSERT(m_plugin);
+    Q_ASSERT(m_plugin->video_input);
+    Q_ASSERT(m_plugin->video_input[0]);
+    return m_plugin->video_input[0];
+}
+
 xine_post_out_t *EffectXT::audioOutputPort() const
 {
     const_cast<EffectXT *>(this)->ensureInstance();
@@ -49,15 +66,37 @@
     return x;
 }
 
+xine_post_out_t *EffectXT::videoOutputPort() const
+{
+    const_cast<EffectXT *>(this)->ensureInstance();
+    Q_ASSERT(m_plugin);
+    const char *const *portNames = xine_post_list_outputs(m_plugin);
+    Q_ASSERT(portNames);
+    Q_ASSERT(portNames[0]);
+    xine_post_out_t *x = xine_post_output(m_plugin, portNames[0]);
+    Q_ASSERT(x);
+    return x;
+}
+
 void EffectXT::rewireTo(SourceNodeXT *source)
 {
-    if (!source->audioOutputPort()) {
-        return;
+    if (m_isVideoPlugin) {
+        if (!source->videoOutputPort()) {
+            return;
+        }
+        ensureInstance();
+        xine_post_in_t *x = xine_post_input(m_plugin, "video");
+        Q_ASSERT(x);
+        xine_post_wire(source->videoOutputPort(), x);
+    } else {
+        if (!source->audioOutputPort()) {
+            return;
+        }
+        ensureInstance();
+        xine_post_in_t *x = xine_post_input(m_plugin, "audio in");
+        Q_ASSERT(x);
+        xine_post_wire(source->audioOutputPort(), x);
     }
-    ensureInstance();
-    xine_post_in_t *x = xine_post_input(m_plugin, "audio in");
-    Q_ASSERT(x);
-    xine_post_wire(source->audioOutputPort(), x);
 }
 
 // lazy initialization
@@ -79,6 +118,14 @@
     return m_fakeAudioPort;
 }
 
+xine_video_port_t *EffectXT::fakeVideoPort()
+{
+    if (!m_fakeVideoPort) {
+        m_fakeVideoPort = xine_open_video_driver(m_xine, "none", XINE_VISUAL_TYPE_NONE, 0);
+    }
+    return m_fakeVideoPort;
+}
+
 void EffectXT::createInstance()
 {
     debug() << Q_FUNC_INFO << "m_pluginName =" << m_pluginName;
@@ -88,8 +135,14 @@
         return;
     }
 
-    fakeAudioPort();
-    m_plugin = xine_post_init(m_xine, m_pluginName, 1, &m_fakeAudioPort, 0);
+    if (m_isVideoPlugin) {
+        fakeVideoPort();
+        m_plugin = xine_post_init(m_xine, m_pluginName, 1, 0, &m_fakeVideoPort);
+    } else {
+        fakeAudioPort();
+        m_plugin = xine_post_init(m_xine, m_pluginName, 1, &m_fakeAudioPort, 0);
+    }
+
     xine_post_in_t *paraInput = xine_post_input(m_plugin, "parameters");
     if (!paraInput) {
         return;
@@ -154,13 +207,26 @@
     SourceNode(static_cast<EffectXT *>(SinkNode::threadSafeObject().data()))
 {
     K_XT(Effect);
-    const char *const *postPlugins = xine_list_post_plugins_typed(xt->m_xine, XINE_POST_TYPE_AUDIO_FILTER);
-    if (effectId >= 0x7F000000) {
+
+    if ((effectId & 0xff000000) == 0x7E000000) {
+        const char *const *postPlugins = xine_list_post_plugins_typed(xt->m_xine, XINE_POST_TYPE_VIDEO_FILTER);
+        effectId -= 0x7E000000;
+        for(int i = 0; postPlugins[i]; ++i) {
+            if (i == effectId) {
+                // found it
+                xt->m_pluginName = postPlugins[i];
+                xt->m_isVideoPlugin = true;
+                break;
+            }
+        }
+    } else if ((effectId & 0xff000000) == 0x7F000000) {
+        const char *const *postPlugins = xine_list_post_plugins_typed(xt->m_xine, XINE_POST_TYPE_AUDIO_FILTER);
         effectId -= 0x7F000000;
         for(int i = 0; postPlugins[i]; ++i) {
             if (i == effectId) {
                 // found it
                 xt->m_pluginName = postPlugins[i];
+                xt->m_isVideoPlugin = false;
                 break;
             }
         }
@@ -173,8 +239,8 @@
 }
 
 EffectXT::EffectXT(const char *name)
-    : SourceNodeXT("Effect"), SinkNodeXT("Effect"), m_plugin(0), m_pluginApi(0), m_fakeAudioPort(0),
-    m_pluginName(name), m_pluginParams(0)
+    : SourceNodeXT("Effect"), SinkNodeXT("Effect"), m_plugin(0), m_pluginApi(0), m_fakeAudioPort(0), m_fakeVideoPort(0),
+    m_pluginName(name), m_pluginParams(0), m_isVideoPlugin(false)
 {
     m_xine = Backend::xine();
 }
@@ -189,6 +255,10 @@
             xine_close_audio_driver(m_xine, m_fakeAudioPort);
             m_fakeAudioPort = 0;
         }
+        if (m_fakeVideoPort) {
+            xine_close_video_driver(m_xine, m_fakeVideoPort);
+            m_fakeVideoPort = 0;
+        }
     }
     free(m_pluginParams);
     m_pluginParams = 0;
@@ -202,12 +272,14 @@
 
 MediaStreamTypes Effect::inputMediaStreamTypes() const
 {
-    return Phonon::Xine::Audio;
+    K_XT(const Effect);
+    return (xt->m_isVideoPlugin ? Phonon::Xine::Video : Phonon::Xine::Audio);
 }
 
 MediaStreamTypes Effect::outputMediaStreamTypes() const
 {
-    return Phonon::Xine::Audio;
+    K_XT(const Effect);
+    return (xt->m_isVideoPlugin ? Phonon::Xine::Video : Phonon::Xine::Audio);
 }
 
 QList<EffectParameter> Effect::parameters() const
@@ -337,9 +409,11 @@
         xt2->m_plugin = xt->m_plugin;
         xt2->m_pluginApi = xt->m_pluginApi;
         xt2->m_fakeAudioPort = xt->m_fakeAudioPort;
+        xt2->m_fakeVideoPort = xt->m_fakeVideoPort;
         xt->m_plugin = 0;
         xt->m_pluginApi = 0;
         xt->m_fakeAudioPort = 0;
+        xt->m_fakeVideoPort = 0;
         KeepReference<> *keep = new KeepReference<>;
         keep->addObject(static_cast<SinkNodeXT *>(xt2));
         keep->ready();
Index: xine/backend.cpp
===================================================================
--- xine/backend.cpp	(Revision 1030914)
+++ xine/backend.cpp	(Arbeitskopie)
@@ -237,11 +237,16 @@
             const char *const *postPlugins = xine_list_post_plugins_typed(m_xine, XINE_POST_TYPE_AUDIO_FILTER);
             for (int i = 0; postPlugins[i]; ++i)
                 list << 0x7F000000 + i;
-            /*const char *const *postVPlugins = xine_list_post_plugins_typed(m_xine, XINE_POST_TYPE_VIDEO_FILTER);
-            for (int i = 0; postVPlugins[i]; ++i) {
+        }
+        break;
+    case Phonon::VideoEffectType:
+        {
+            const char *const *postPlugins = xine_list_post_plugins_typed(m_xine, XINE_POST_TYPE_VIDEO_FILTER);
+            for (int i = 0; postPlugins[i]; ++i) {
                 list << 0x7E000000 + i;
-            } */
+            }
         }
+        break;
     case Phonon::AudioChannelType:
     case Phonon::SubtitleType:
         {
@@ -312,14 +317,20 @@
                     break;
                 }
             }
-            /*const char *const *postVPlugins = xine_list_post_plugins_typed(m_xine, XINE_POST_TYPE_VIDEO_FILTER);
-            for (int i = 0; postVPlugins[i]; ++i) {
+        }
+        break;
+    case Phonon::VideoEffectType:
+        {
+            const char *const *postPlugins = xine_list_post_plugins_typed(m_xine, XINE_POST_TYPE_VIDEO_FILTER);
+            for (int i = 0; postPlugins[i]; ++i) {
                 if (0x7E000000 + i == index) {
                     ret.insert("name", QLatin1String(postPlugins[i]));
+                    ret.insert("description", QLatin1String(xine_get_post_plugin_description(m_xine, postPlugins[i])));
                     break;
                 }
-            } */
+            }
         }
+        break;
     case Phonon::AudioChannelType:
     case Phonon::SubtitleType:
         {


More information about the Phonon-backends mailing list