| 1 | /*  This file is part of the KDE project.
 | 
|---|
| 2 | 
 | 
|---|
| 3 | Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
 | 
|---|
| 4 | 
 | 
|---|
| 5 | This library is free software: you can redistribute it and/or modify
 | 
|---|
| 6 | it under the terms of the GNU Lesser General Public License as published by
 | 
|---|
| 7 | the Free Software Foundation, either version 2.1 or 3 of the License.
 | 
|---|
| 8 | 
 | 
|---|
| 9 | This library is distributed in the hope that it will be useful,
 | 
|---|
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 12 | GNU Lesser General Public License for more details.
 | 
|---|
| 13 | 
 | 
|---|
| 14 | You should have received a copy of the GNU Lesser General Public License
 | 
|---|
| 15 | along with this library.  If not, see <http://www.gnu.org/licenses/>.
 | 
|---|
| 16 | */
 | 
|---|
| 17 | 
 | 
|---|
| 18 | #include "fakesource.h"
 | 
|---|
| 19 | #include "iodevicereader.h"
 | 
|---|
| 20 | #include "qaudiocdreader.h"
 | 
|---|
| 21 | 
 | 
|---|
| 22 | #include "mediagraph.h"
 | 
|---|
| 23 | #include "mediaobject.h"
 | 
|---|
| 24 | 
 | 
|---|
| 25 | 
 | 
|---|
| 26 | #include <QtCore/QUrl>
 | 
|---|
| 27 | #include <QtCore/QDebug>
 | 
|---|
| 28 | 
 | 
|---|
| 29 | #include <qnetwork.h>
 | 
|---|
| 30 | 
 | 
|---|
| 31 | 
 | 
|---|
| 32 | QT_BEGIN_NAMESPACE
 | 
|---|
| 33 | 
 | 
|---|
| 34 | namespace Phonon
 | 
|---|
| 35 | {
 | 
|---|
| 36 |     namespace DS9
 | 
|---|
| 37 |     {
 | 
|---|
| 38 |         //description of a connection
 | 
|---|
| 39 |         struct GraphConnection
 | 
|---|
| 40 |         {
 | 
|---|
| 41 |             Filter output;
 | 
|---|
| 42 |             int outputOffset;
 | 
|---|
| 43 |             Filter input;
 | 
|---|
| 44 |             int inputOffset;
 | 
|---|
| 45 |         };
 | 
|---|
| 46 | 
 | 
|---|
| 47 |         static QList<GraphConnection> getConnections(Filter source)
 | 
|---|
| 48 |         {
 | 
|---|
| 49 |             QList<GraphConnection> ret;
 | 
|---|
| 50 |             int outOffset = 0;
 | 
|---|
| 51 |             const QList<OutputPin> outputs = BackendNode::pins(source, PINDIR_OUTPUT);
 | 
|---|
| 52 |             for (int i = 0; i < outputs.count(); ++i) {
 | 
|---|
| 53 |                 InputPin input;
 | 
|---|
| 54 |                 if (outputs.at(i)->ConnectedTo(input.pparam()) == S_OK) {
 | 
|---|
| 55 |                     PIN_INFO info;
 | 
|---|
| 56 |                     input->QueryPinInfo(&info);
 | 
|---|
| 57 |                     Filter current(info.pFilter);
 | 
|---|
| 58 |                     if (current) {
 | 
|---|
| 59 |                         //this is a valid connection
 | 
|---|
| 60 |                         const int inOffset = BackendNode::pins(current, PINDIR_INPUT).indexOf(input);
 | 
|---|
| 61 |                         const GraphConnection connection = {source, outOffset, current, inOffset};
 | 
|---|
| 62 |                         ret += connection;
 | 
|---|
| 63 |                         ret += getConnections(current); //get subsequent connections
 | 
|---|
| 64 |                     }
 | 
|---|
| 65 |                 }
 | 
|---|
| 66 |                 outOffset++;
 | 
|---|
| 67 |             }
 | 
|---|
| 68 |             return ret;
 | 
|---|
| 69 |         }
 | 
|---|
| 70 |                 
 | 
|---|
| 71 | 
 | 
|---|
| 72 | /*
 | 
|---|
| 73 |         static HRESULT saveToFile(Graph graph, const QString &filepath)
 | 
|---|
| 74 |         {
 | 
|---|
| 75 |             const WCHAR wszStreamName[] = L"ActiveMovieGraph";
 | 
|---|
| 76 |             HRESULT hr;
 | 
|---|
| 77 |             ComPointer<IStorage> storage;
 | 
|---|
| 78 | 
 | 
|---|
| 79 |             // First, create a document file that will hold the GRF file
 | 
|---|
| 80 |             hr = StgCreateDocfile((OLECHAR*)filepath.utf16(),
 | 
|---|
| 81 |                 STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE |
 | 
|---|
| 82 |                 STGM_SHARE_EXCLUSIVE,
 | 
|---|
| 83 |                 0, storage.pparam());
 | 
|---|
| 84 | 
 | 
|---|
| 85 |             if (FAILED(hr)) {
 | 
|---|
| 86 |                 return hr;
 | 
|---|
| 87 |             }
 | 
|---|
| 88 | 
 | 
|---|
| 89 |             // Next, create a stream to store.
 | 
|---|
| 90 |             ComPointer<IStream> stream;
 | 
|---|
| 91 |             hr = storage->CreateStream(wszStreamName,
 | 
|---|
| 92 |                 STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
 | 
|---|
| 93 |                 0, 0, stream.pparam());
 | 
|---|
| 94 | 
 | 
|---|
| 95 |             if (FAILED(hr)) {
 | 
|---|
| 96 |                 return hr;
 | 
|---|
| 97 |             }
 | 
|---|
| 98 | 
 | 
|---|
| 99 |             // The IpersistStream::Save method converts a stream into a persistent object.
 | 
|---|
| 100 |             ComPointer<IPersistStream> persist(graph, IID_IPersistStream);
 | 
|---|
| 101 |             hr = persist->Save(stream, TRUE);
 | 
|---|
| 102 |             if (SUCCEEDED(hr)) {
 | 
|---|
| 103 |                 hr = storage->Commit(STGC_DEFAULT);
 | 
|---|
| 104 |             }
 | 
|---|
| 105 | 
 | 
|---|
| 106 |             return hr;
 | 
|---|
| 107 |         }
 | 
|---|
| 108 | */
 | 
|---|
| 109 | 
 | 
|---|
| 110 |         MediaGraph::MediaGraph(MediaObject *mo, short index) :
 | 
|---|
| 111 |             m_graph(CLSID_FilterGraph, IID_IGraphBuilder),
 | 
|---|
| 112 |             m_fakeSource(new FakeSource()),
 | 
|---|
| 113 |             m_hasVideo(false), m_hasAudio(false), m_connectionsDirty(false), 
 | 
|---|
| 114 |             m_isStopping(false), m_isSeekable(false), m_result(S_OK),
 | 
|---|
| 115 |             m_index(index), m_renderId(0), m_seekId(0),
 | 
|---|
| 116 |             m_currentTime(0), m_totalTime(0), m_mediaObject(mo)
 | 
|---|
| 117 |         {
 | 
|---|
| 118 |             m_mediaControl = ComPointer<IMediaControl>(m_graph, IID_IMediaControl);
 | 
|---|
| 119 |             Q_ASSERT(m_mediaControl);
 | 
|---|
| 120 |             m_mediaSeeking = ComPointer<IMediaSeeking>(m_graph, IID_IMediaSeeking);
 | 
|---|
| 121 |             Q_ASSERT(m_mediaSeeking);
 | 
|---|
| 122 | 
 | 
|---|
| 123 |             HRESULT hr = m_graph->AddFilter(m_fakeSource, 0);
 | 
|---|
| 124 |             if (m_mediaObject->catchComError(hr)) {
 | 
|---|
| 125 |                 return;
 | 
|---|
| 126 |             }
 | 
|---|
| 127 |         }
 | 
|---|
| 128 | 
 | 
|---|
| 129 |         MediaGraph::~MediaGraph()
 | 
|---|
| 130 |         {
 | 
|---|
| 131 |         }
 | 
|---|
| 132 | 
 | 
|---|
| 133 |         short MediaGraph::index() const
 | 
|---|
| 134 |         {
 | 
|---|
| 135 |             return m_index;
 | 
|---|
| 136 |         }
 | 
|---|
| 137 | 
 | 
|---|
| 138 |         void MediaGraph::grabNode(BackendNode *node)
 | 
|---|
| 139 |         {
 | 
|---|
| 140 |             grabFilter(node->filter(m_index));
 | 
|---|
| 141 |         }
 | 
|---|
| 142 | 
 | 
|---|
| 143 |         void MediaGraph::grabFilter(Filter filter)
 | 
|---|
| 144 |         {
 | 
|---|
| 145 |             if (filter) {
 | 
|---|
| 146 |                 FILTER_INFO info;
 | 
|---|
| 147 |                 filter->QueryFilterInfo(&info);
 | 
|---|
| 148 |                 if (info.pGraph != m_graph) {
 | 
|---|
| 149 |                     if (info.pGraph) {
 | 
|---|
| 150 |                         m_mediaObject->catchComError(info.pGraph->RemoveFilter(filter));
 | 
|---|
| 151 |                     }
 | 
|---|
| 152 |                     m_mediaObject->catchComError(m_graph->AddFilter(filter, 0));
 | 
|---|
| 153 |                 }
 | 
|---|
| 154 |                 if (info.pGraph) {
 | 
|---|
| 155 |                     info.pGraph->Release();
 | 
|---|
| 156 |                 }
 | 
|---|
| 157 |             }
 | 
|---|
| 158 |         }
 | 
|---|
| 159 | 
 | 
|---|
| 160 |         void MediaGraph::switchFilters(Filter oldFilter, Filter newFilter)
 | 
|---|
| 161 |         {
 | 
|---|
| 162 |             OAFilterState state = syncGetRealState();
 | 
|---|
| 163 |             if (state != State_Stopped) {
 | 
|---|
| 164 |                 ensureStopped(); //to do the transaction
 | 
|---|
| 165 |             }
 | 
|---|
| 166 | 
 | 
|---|
| 167 | 
 | 
|---|
| 168 |             OutputPin connected;
 | 
|---|
| 169 |             {
 | 
|---|
| 170 |                 InputPin pin = BackendNode::pins(oldFilter, PINDIR_INPUT).first();
 | 
|---|
| 171 |                 pin->ConnectedTo(connected.pparam());
 | 
|---|
| 172 |             }
 | 
|---|
| 173 | 
 | 
|---|
| 174 |             m_graph->RemoveFilter(oldFilter);
 | 
|---|
| 175 |             m_graph->AddFilter(newFilter, 0);
 | 
|---|
| 176 | 
 | 
|---|
| 177 |             if (connected) {
 | 
|---|
| 178 |                 InputPin pin = BackendNode::pins(newFilter, PINDIR_INPUT).first();
 | 
|---|
| 179 |                 //let's reestablish the connections
 | 
|---|
| 180 |                 m_graph->Connect(connected, pin);
 | 
|---|
| 181 |             }
 | 
|---|
| 182 | 
 | 
|---|
| 183 |             switch(state)
 | 
|---|
| 184 |             {
 | 
|---|
| 185 |             case State_Running:
 | 
|---|
| 186 |                 play();
 | 
|---|
| 187 |                 break;
 | 
|---|
| 188 |             case State_Paused:
 | 
|---|
| 189 |                 pause();
 | 
|---|
| 190 |                 break;
 | 
|---|
| 191 |             default:
 | 
|---|
| 192 |                 break;
 | 
|---|
| 193 |             }
 | 
|---|
| 194 | 
 | 
|---|
| 195 |         }
 | 
|---|
| 196 | 
 | 
|---|
| 197 |         OAFilterState MediaGraph::syncGetRealState() const
 | 
|---|
| 198 |         {
 | 
|---|
| 199 |             OAFilterState state;
 | 
|---|
| 200 |             m_mediaControl->GetState(INFINITE, &state);
 | 
|---|
| 201 |             return state;
 | 
|---|
| 202 |         }
 | 
|---|
| 203 | 
 | 
|---|
| 204 | 
 | 
|---|
| 205 | 
 | 
|---|
| 206 |         void MediaGraph::ensureSourceDisconnected()
 | 
|---|
| 207 |         {
 | 
|---|
| 208 |             for (int i = 0; i < m_sinkConnections.count(); ++i) {
 | 
|---|
| 209 |                 const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
 | 
|---|
| 210 |                 const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
 | 
|---|
| 211 |                 const QList<InputPin> outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
 | 
|---|
| 212 | 
 | 
|---|
| 213 |                 for (int i = 0; i < inputs.count(); ++i) {
 | 
|---|
| 214 |                     for (int o = 0; o < outputs.count(); o++) {
 | 
|---|
| 215 |                         tryDisconnect(outputs.at(o), inputs.at(i));
 | 
|---|
| 216 |                     }
 | 
|---|
| 217 | 
 | 
|---|
| 218 |                     for (int d = 0; d < m_decoderPins.count(); ++d) {
 | 
|---|
| 219 |                         tryDisconnect(m_decoderPins.at(d), inputs.at(i));
 | 
|---|
| 220 |                     }
 | 
|---|
| 221 |                 }
 | 
|---|
| 222 |             }
 | 
|---|
| 223 |         }
 | 
|---|
| 224 | 
 | 
|---|
| 225 |         void MediaGraph::ensureSourceConnectedTo(bool force)
 | 
|---|
| 226 |         {
 | 
|---|
| 227 |             if (m_connectionsDirty == false && force == false) {
 | 
|---|
| 228 |                 return;
 | 
|---|
| 229 |             }
 | 
|---|
| 230 | 
 | 
|---|
| 231 |             m_connectionsDirty = false;
 | 
|---|
| 232 |             ensureSourceDisconnected();
 | 
|---|
| 233 | 
 | 
|---|
| 234 |             //reconnect the pins
 | 
|---|
| 235 |             for (int i = 0; i < m_sinkConnections.count(); ++i) {
 | 
|---|
| 236 |                 const Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
 | 
|---|
| 237 |                 const QList<InputPin> inputs = BackendNode::pins(currentFilter, PINDIR_INPUT);
 | 
|---|
| 238 |                 for(int i = 0; i < inputs.count(); ++i) {
 | 
|---|
| 239 |                     //we ensure the filter belongs to the graph
 | 
|---|
| 240 |                     grabFilter(currentFilter);
 | 
|---|
| 241 | 
 | 
|---|
| 242 |                     for (int d = 0; d < m_decoderPins.count(); ++d) {
 | 
|---|
| 243 |                         //a decoder has only one output
 | 
|---|
| 244 |                         if (tryConnect(m_decoderPins.at(d), inputs.at(i))) {
 | 
|---|
| 245 |                             break;
 | 
|---|
| 246 |                         }
 | 
|---|
| 247 |                     }
 | 
|---|
| 248 |                 }
 | 
|---|
| 249 |             }
 | 
|---|
| 250 |         }
 | 
|---|
| 251 | 
 | 
|---|
| 252 |         QList<Filter> MediaGraph::getAllFilters(Graph graph)
 | 
|---|
| 253 |         {
 | 
|---|
| 254 |             QList<Filter> ret;
 | 
|---|
| 255 |             ComPointer<IEnumFilters> enumFilters;
 | 
|---|
| 256 |             graph->EnumFilters(enumFilters.pparam());
 | 
|---|
| 257 |             Filter current;
 | 
|---|
| 258 |             while( enumFilters && enumFilters->Next(1, current.pparam(), 0) == S_OK) {
 | 
|---|
| 259 |                 ret += current;
 | 
|---|
| 260 |             }
 | 
|---|
| 261 |             return ret;
 | 
|---|
| 262 |         }
 | 
|---|
| 263 | 
 | 
|---|
| 264 |         QList<Filter> MediaGraph::getAllFilters() const
 | 
|---|
| 265 |         {
 | 
|---|
| 266 |             return getAllFilters(m_graph);
 | 
|---|
| 267 |         }
 | 
|---|
| 268 | 
 | 
|---|
| 269 | 
 | 
|---|
| 270 |         bool MediaGraph::isSeekable() const
 | 
|---|
| 271 |         {
 | 
|---|
| 272 |             return m_isSeekable;
 | 
|---|
| 273 |         }
 | 
|---|
| 274 | 
 | 
|---|
| 275 |         qint64 MediaGraph::absoluteTotalTime() const
 | 
|---|
| 276 |         {
 | 
|---|
| 277 |             if (m_seekId) {
 | 
|---|
| 278 |                 return m_totalTime;
 | 
|---|
| 279 |             } else {
 | 
|---|
| 280 |                 qint64 ret = 0;
 | 
|---|
| 281 |                 if (m_mediaSeeking) {
 | 
|---|
| 282 |                     m_mediaSeeking->GetDuration(&ret);
 | 
|---|
| 283 |                     ret /= 10000; //convert to milliseconds
 | 
|---|
| 284 |                 }
 | 
|---|
| 285 |                 return ret;
 | 
|---|
| 286 |             }
 | 
|---|
| 287 |         }
 | 
|---|
| 288 | 
 | 
|---|
| 289 |         qint64 MediaGraph::absoluteCurrentTime() const
 | 
|---|
| 290 |         {
 | 
|---|
| 291 |             if (m_seekId) {
 | 
|---|
| 292 |                 return m_currentTime;
 | 
|---|
| 293 |             } else {
 | 
|---|
| 294 |                 qint64 ret = -1;
 | 
|---|
| 295 |                 if (m_mediaSeeking) {
 | 
|---|
| 296 |                     HRESULT hr = m_mediaSeeking->GetCurrentPosition(&ret);
 | 
|---|
| 297 |                     if (FAILED(hr)) {
 | 
|---|
| 298 |                         return ret;
 | 
|---|
| 299 |                     }
 | 
|---|
| 300 |                     ret /= 10000; //convert to milliseconds
 | 
|---|
| 301 |                 }
 | 
|---|
| 302 |                 return ret;
 | 
|---|
| 303 |             }
 | 
|---|
| 304 |         }
 | 
|---|
| 305 | 
 | 
|---|
| 306 |         Phonon::MediaSource MediaGraph::mediaSource() const
 | 
|---|
| 307 |         {
 | 
|---|
| 308 |             return m_mediaSource;
 | 
|---|
| 309 |         }
 | 
|---|
| 310 | 
 | 
|---|
| 311 |         void MediaGraph::play()
 | 
|---|
| 312 |         {
 | 
|---|
| 313 |             ensureSourceConnectedTo();
 | 
|---|
| 314 |             m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Running, m_decoders);
 | 
|---|
| 315 |         }
 | 
|---|
| 316 | 
 | 
|---|
| 317 |         void MediaGraph::pause()
 | 
|---|
| 318 |         {
 | 
|---|
| 319 |             ensureSourceConnectedTo();
 | 
|---|
| 320 |             m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Paused, m_decoders);
 | 
|---|
| 321 |         }
 | 
|---|
| 322 | 
 | 
|---|
| 323 |         HRESULT MediaGraph::renderResult() const
 | 
|---|
| 324 |         {
 | 
|---|
| 325 |             return m_result;
 | 
|---|
| 326 |         }
 | 
|---|
| 327 | 
 | 
|---|
| 328 |         bool MediaGraph::isStopping() const
 | 
|---|
| 329 |         {
 | 
|---|
| 330 |             return m_isStopping;
 | 
|---|
| 331 |         }
 | 
|---|
| 332 | 
 | 
|---|
| 333 |         Graph MediaGraph::graph() const
 | 
|---|
| 334 |         {
 | 
|---|
| 335 |             return m_graph;
 | 
|---|
| 336 |         }
 | 
|---|
| 337 | 
 | 
|---|
| 338 |         void MediaGraph::stop()
 | 
|---|
| 339 |         {
 | 
|---|
| 340 |             if (!isLoading()) {
 | 
|---|
| 341 |                 ensureStopped();
 | 
|---|
| 342 |                 absoluteSeek(0); //resets the clock
 | 
|---|
| 343 |                         } else {
 | 
|---|
| 344 |                                 m_mediaObject->workerThread()->abortCurrentRender(m_renderId);
 | 
|---|
| 345 |                         m_renderId = 0; //cancels current loading
 | 
|---|
| 346 |                         }
 | 
|---|
| 347 |             m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Stopped);
 | 
|---|
| 348 |         }
 | 
|---|
| 349 | 
 | 
|---|
| 350 |         void MediaGraph::ensureStopped()
 | 
|---|
| 351 |         {
 | 
|---|
| 352 |             m_isStopping = true;
 | 
|---|
| 353 |             //special case here because we want stopped to be synchronous
 | 
|---|
| 354 |             m_graph->Abort();
 | 
|---|
| 355 |             m_mediaControl->Stop(); 
 | 
|---|
| 356 |             OAFilterState dummy;
 | 
|---|
| 357 |             //this will wait until the change is effective
 | 
|---|
| 358 |             m_mediaControl->GetState(INFINITE, &dummy);
 | 
|---|
| 359 |             m_isStopping = false;
 | 
|---|
| 360 |         }
 | 
|---|
| 361 | 
 | 
|---|
| 362 |         bool MediaGraph::isLoading() const
 | 
|---|
| 363 |         {
 | 
|---|
| 364 |             return m_renderId != 0;
 | 
|---|
| 365 |         }
 | 
|---|
| 366 | 
 | 
|---|
| 367 |         void MediaGraph::absoluteSeek(qint64 time)
 | 
|---|
| 368 |         {
 | 
|---|
| 369 |             //this just sends a request
 | 
|---|
| 370 |             if (m_seekId == 0) {
 | 
|---|
| 371 |                 m_currentTime = absoluteCurrentTime();
 | 
|---|
| 372 |                 m_totalTime = absoluteTotalTime();
 | 
|---|
| 373 |             }
 | 
|---|
| 374 |             m_seekId = m_mediaObject->workerThread()->addSeekRequest(m_graph, time);
 | 
|---|
| 375 |         }
 | 
|---|
| 376 | 
 | 
|---|
| 377 |         HRESULT MediaGraph::removeFilter(const Filter& filter)
 | 
|---|
| 378 |         {
 | 
|---|
| 379 |             FILTER_INFO info;
 | 
|---|
| 380 |             filter->QueryFilterInfo(&info);
 | 
|---|
| 381 | #ifdef GRAPH_DEBUG
 | 
|---|
| 382 |             qDebug() << "removeFilter" << QString((const QChar *)info.achName);
 | 
|---|
| 383 | #endif
 | 
|---|
| 384 |             if (info.pGraph) {
 | 
|---|
| 385 |                 info.pGraph->Release();
 | 
|---|
| 386 |                 if (info.pGraph == m_graph)
 | 
|---|
| 387 |                     return m_graph->RemoveFilter(filter);
 | 
|---|
| 388 |             }
 | 
|---|
| 389 | 
 | 
|---|
| 390 |             //already removed
 | 
|---|
| 391 |             return S_OK;
 | 
|---|
| 392 |         }
 | 
|---|
| 393 | 
 | 
|---|
| 394 |         HRESULT MediaGraph::cleanup()
 | 
|---|
| 395 |         {
 | 
|---|
| 396 |             stop();
 | 
|---|
| 397 | 
 | 
|---|
| 398 |             ensureSourceDisconnected();
 | 
|---|
| 399 | 
 | 
|---|
| 400 |             QList<Filter> list = m_decoders;
 | 
|---|
| 401 |             if (m_demux) {
 | 
|---|
| 402 |                 list << m_demux;
 | 
|---|
| 403 |             }
 | 
|---|
| 404 |             if (m_realSource) {
 | 
|---|
| 405 |                 list << m_realSource;
 | 
|---|
| 406 |             }
 | 
|---|
| 407 |             list << m_decoders;
 | 
|---|
| 408 | 
 | 
|---|
| 409 |             for (int i = 0; i < m_decoders.count(); ++i) {
 | 
|---|
| 410 |                 list += getFilterChain(m_demux, m_decoders.at(i));
 | 
|---|
| 411 |             }
 | 
|---|
| 412 | 
 | 
|---|
| 413 |             for (int i = 0; i < list.count(); ++i) {
 | 
|---|
| 414 |                 removeFilter(list.at(i));
 | 
|---|
| 415 |             }
 | 
|---|
| 416 | 
 | 
|---|
| 417 |             //Let's reinitialize the internal lists
 | 
|---|
| 418 |             m_decoderPins.clear();
 | 
|---|
| 419 |             m_decoders.clear();
 | 
|---|
| 420 |             m_demux = Filter();
 | 
|---|
| 421 |             m_realSource = Filter();
 | 
|---|
| 422 |             m_mediaSource = Phonon::MediaSource();
 | 
|---|
| 423 | 
 | 
|---|
| 424 |             absoluteSeek(0); //resets the clock
 | 
|---|
| 425 | 
 | 
|---|
| 426 |             return S_OK;
 | 
|---|
| 427 |         }
 | 
|---|
| 428 | 
 | 
|---|
| 429 | 
 | 
|---|
| 430 |         bool MediaGraph::disconnectNodes(BackendNode *source, BackendNode *sink)
 | 
|---|
| 431 |         {
 | 
|---|
| 432 |             const Filter sinkFilter = sink->filter(m_index);
 | 
|---|
| 433 |             const QList<InputPin> inputs = BackendNode::pins(sinkFilter, PINDIR_INPUT);
 | 
|---|
| 434 | 
 | 
|---|
| 435 |             QList<OutputPin> outputs;
 | 
|---|
| 436 |             if (source == m_mediaObject) {
 | 
|---|
| 437 |                 outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT);
 | 
|---|
| 438 |                 outputs += m_decoderPins;
 | 
|---|
| 439 |             } else {
 | 
|---|
| 440 |                 outputs = BackendNode::pins(source->filter(m_index), PINDIR_OUTPUT);
 | 
|---|
| 441 |             }
 | 
|---|
| 442 | 
 | 
|---|
| 443 | 
 | 
|---|
| 444 |             for (int i = 0; i < inputs.count(); ++i) {
 | 
|---|
| 445 |                 for (int o = 0; o < outputs.count(); ++o) {
 | 
|---|
| 446 |                     tryDisconnect(outputs.at(o), inputs.at(i));
 | 
|---|
| 447 |                 }
 | 
|---|
| 448 |             }
 | 
|---|
| 449 | 
 | 
|---|
| 450 |             if (m_sinkConnections.removeOne(sink)) {
 | 
|---|
| 451 |                 m_connectionsDirty = true;
 | 
|---|
| 452 |             }
 | 
|---|
| 453 |             return true;
 | 
|---|
| 454 |         }
 | 
|---|
| 455 | 
 | 
|---|
| 456 |         bool MediaGraph::tryDisconnect(const OutputPin &out, const InputPin &in)
 | 
|---|
| 457 |         {
 | 
|---|
| 458 |             bool ret = false;
 | 
|---|
| 459 | 
 | 
|---|
| 460 |             OutputPin output;
 | 
|---|
| 461 |             if (SUCCEEDED(in->ConnectedTo(output.pparam()))) {
 | 
|---|
| 462 | 
 | 
|---|
| 463 |                 if (output == out) {
 | 
|---|
| 464 |                     //we need a simple disconnection
 | 
|---|
| 465 |                     ret = SUCCEEDED(out->Disconnect()) && SUCCEEDED(in->Disconnect());
 | 
|---|
| 466 |                 } else {
 | 
|---|
| 467 |                     InputPin in2;
 | 
|---|
| 468 |                     if (SUCCEEDED(out->ConnectedTo(in2.pparam()))) {
 | 
|---|
| 469 |                         PIN_INFO info;
 | 
|---|
| 470 |                         in2->QueryPinInfo(&info);
 | 
|---|
| 471 |                         Filter tee(info.pFilter);
 | 
|---|
| 472 |                         CLSID clsid;
 | 
|---|
| 473 |                         tee->GetClassID(&clsid);
 | 
|---|
| 474 |                         if (clsid == CLSID_InfTee) {
 | 
|---|
| 475 |                             //we have to remove all intermediate filters between the tee and the sink
 | 
|---|
| 476 |                             PIN_INFO info;
 | 
|---|
| 477 |                             in->QueryPinInfo(&info);
 | 
|---|
| 478 |                             Filter sink(info.pFilter);
 | 
|---|
| 479 |                             QList<Filter> list = getFilterChain(tee, sink);
 | 
|---|
| 480 |                             out->QueryPinInfo(&info);
 | 
|---|
| 481 |                             Filter source(info.pFilter);
 | 
|---|
| 482 | 
 | 
|---|
| 483 |                             if (list.isEmpty()) {
 | 
|---|
| 484 |                                 output->QueryPinInfo(&info);
 | 
|---|
| 485 |                                 if (Filter(info.pFilter) == tee) {
 | 
|---|
| 486 |                                     ret = SUCCEEDED(output->Disconnect()) && SUCCEEDED(in->Disconnect());
 | 
|---|
| 487 |                                 }
 | 
|---|
| 488 |                             } else {
 | 
|---|
| 489 |                                 ret = true;
 | 
|---|
| 490 |                                 for (int i = 0; i < list.count(); ++i) {
 | 
|---|
| 491 |                                     ret = ret && SUCCEEDED(removeFilter(list.at(i)));
 | 
|---|
| 492 |                                 }
 | 
|---|
| 493 |                             }
 | 
|---|
| 494 | 
 | 
|---|
| 495 |                             //Let's try to see if the Tee filter is still useful
 | 
|---|
| 496 |                             if (ret) {
 | 
|---|
| 497 |                                 int connections = 0;
 | 
|---|
| 498 |                                 const QList<OutputPin> outputs = BackendNode::pins(tee, PINDIR_OUTPUT);
 | 
|---|
| 499 |                                 for(int i = 0; i < outputs.count(); ++i) {
 | 
|---|
| 500 |                                     InputPin p;
 | 
|---|
| 501 |                                     if ( SUCCEEDED(outputs.at(i)->ConnectedTo(p.pparam()))) {
 | 
|---|
| 502 |                                         connections++;
 | 
|---|
| 503 |                                     }
 | 
|---|
| 504 |                                 }
 | 
|---|
| 505 |                                 if (connections == 0) {
 | 
|---|
| 506 |                                     //this avoids a crash if the filter is destroyed
 | 
|---|
| 507 |                                     //by the subsequent call to removeFilter
 | 
|---|
| 508 |                                     output = OutputPin();
 | 
|---|
| 509 |                                     removeFilter(tee); //there is no more output for the tee, we remove it
 | 
|---|
| 510 |                                 }
 | 
|---|
| 511 |                             }
 | 
|---|
| 512 |                         }
 | 
|---|
| 513 |                     }
 | 
|---|
| 514 |                 }
 | 
|---|
| 515 |             }
 | 
|---|
| 516 |             return ret;
 | 
|---|
| 517 |         }
 | 
|---|
| 518 | 
 | 
|---|
| 519 |         bool MediaGraph::tryConnect(const OutputPin &out, const InputPin &newIn)
 | 
|---|
| 520 |         {
 | 
|---|
| 521 | 
 | 
|---|
| 522 | 
 | 
|---|
| 523 |             ///The management of the creation of the Tees is done here (this is the only place where we call IPin::Connect
 | 
|---|
| 524 |             InputPin inPin;
 | 
|---|
| 525 |             if (SUCCEEDED(out->ConnectedTo(inPin.pparam()))) {
 | 
|---|
| 526 | 
 | 
|---|
| 527 |                 //the fake source has another mechanism for the connection
 | 
|---|
| 528 |                 if (BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).contains(out)) {
 | 
|---|
| 529 |                     return false;
 | 
|---|
| 530 |                 }
 | 
|---|
| 531 | 
 | 
|---|
| 532 |                 //the output pin is already connected
 | 
|---|
| 533 |                 PIN_INFO info;
 | 
|---|
| 534 |                 inPin->QueryPinInfo(&info);
 | 
|---|
| 535 |                 Filter filter(info.pFilter); //this will ensure the interface is "Release"d
 | 
|---|
| 536 |                 CLSID clsid;
 | 
|---|
| 537 |                 filter->GetClassID(&clsid);
 | 
|---|
| 538 |                 if (clsid == CLSID_InfTee) {
 | 
|---|
| 539 |                     //there is already a Tee (namely 'filter') in use
 | 
|---|
| 540 |                     const QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
 | 
|---|
| 541 |                     for(int i = 0; i < outputs.count(); ++i) {
 | 
|---|
| 542 |                         const OutputPin &pin = outputs.at(i);
 | 
|---|
| 543 |                         if (HRESULT(VFW_E_NOT_CONNECTED) == pin->ConnectedTo(inPin.pparam())) {
 | 
|---|
| 544 |                             return SUCCEEDED(pin->Connect(newIn, 0));
 | 
|---|
| 545 |                         }
 | 
|---|
| 546 |                     }
 | 
|---|
| 547 |                     //we shoud never go here
 | 
|---|
| 548 |                     return false;
 | 
|---|
| 549 |                 } else {
 | 
|---|
| 550 |                     QAMMediaType type;
 | 
|---|
| 551 |                     out->ConnectionMediaType(&type);
 | 
|---|
| 552 | 
 | 
|---|
| 553 |                     //first we disconnect the current connection (and we save the current media type)
 | 
|---|
| 554 |                     if (!tryDisconnect(out, inPin)) {
 | 
|---|
| 555 |                         return false;
 | 
|---|
| 556 |                     }
 | 
|---|
| 557 | 
 | 
|---|
| 558 |                     //..then we try to connect the new node
 | 
|---|
| 559 |                     if (SUCCEEDED(out->Connect(newIn, 0))) {
 | 
|---|
| 560 | 
 | 
|---|
| 561 |                         //we have to insert the Tee
 | 
|---|
| 562 |                         if (!tryDisconnect(out, newIn)) {
 | 
|---|
| 563 |                             return false;
 | 
|---|
| 564 |                         }
 | 
|---|
| 565 | 
 | 
|---|
| 566 |                         Filter filter(CLSID_InfTee, IID_IBaseFilter);
 | 
|---|
| 567 |                         if (!filter) {
 | 
|---|
| 568 |                             //rollback
 | 
|---|
| 569 |                             m_graph->Connect(out, inPin);
 | 
|---|
| 570 |                             return false;
 | 
|---|
| 571 |                         }
 | 
|---|
| 572 | 
 | 
|---|
| 573 |                         if (FAILED(m_graph->AddFilter(filter, 0))) {
 | 
|---|
| 574 |                             return false;
 | 
|---|
| 575 |                         }
 | 
|---|
| 576 | 
 | 
|---|
| 577 | 
 | 
|---|
| 578 |                         InputPin teeIn = BackendNode::pins(filter, PINDIR_INPUT).first(); //a Tee has always one input
 | 
|---|
| 579 |                         HRESULT hr = out->Connect(teeIn, &type);
 | 
|---|
| 580 |                         if (FAILED(hr)) {
 | 
|---|
| 581 |                             hr = m_graph->Connect(out, teeIn);
 | 
|---|
| 582 |                         }
 | 
|---|
| 583 |                         if (FAILED(hr)) {
 | 
|---|
| 584 |                             m_graph->Connect(out, inPin);
 | 
|---|
| 585 |                             return false;
 | 
|---|
| 586 |                         }
 | 
|---|
| 587 | 
 | 
|---|
| 588 |                         OutputPin teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
 | 
|---|
| 589 | 
 | 
|---|
| 590 |                         //we simply reconnect the pins as they
 | 
|---|
| 591 |                         hr = m_graph->Connect(teeOut, inPin);
 | 
|---|
| 592 |                         if (FAILED(hr)) {
 | 
|---|
| 593 |                             m_graph->Connect(out, inPin);
 | 
|---|
| 594 |                             return false;
 | 
|---|
| 595 |                         }
 | 
|---|
| 596 | 
 | 
|---|
| 597 |                         teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last(); //the last is always the one that's not connected
 | 
|---|
| 598 |                         if (FAILED(m_graph->Connect(teeOut, newIn))) {
 | 
|---|
| 599 |                             m_graph->Connect(out, inPin);
 | 
|---|
| 600 |                             return false;
 | 
|---|
| 601 |                         }
 | 
|---|
| 602 | 
 | 
|---|
| 603 |                         return true;
 | 
|---|
| 604 |                     } else {
 | 
|---|
| 605 |                         //we simply reconnect the pins as they
 | 
|---|
| 606 |                         m_graph->Connect(out, inPin);
 | 
|---|
| 607 |                         return false;
 | 
|---|
| 608 |                     }
 | 
|---|
| 609 |                 }
 | 
|---|
| 610 | 
 | 
|---|
| 611 |             } else {
 | 
|---|
| 612 |                 return SUCCEEDED(m_graph->Connect(out, newIn));
 | 
|---|
| 613 |             }
 | 
|---|
| 614 |         }
 | 
|---|
| 615 | 
 | 
|---|
| 616 |         bool MediaGraph::connectNodes(BackendNode *source, BackendNode *sink)
 | 
|---|
| 617 |         {
 | 
|---|
| 618 |             bool ret = false;
 | 
|---|
| 619 |             const QList<InputPin> inputs = BackendNode::pins(sink->filter(m_index), PINDIR_INPUT);
 | 
|---|
| 620 |             QList<OutputPin> outputs = BackendNode::pins(source == m_mediaObject ? m_fakeSource : source->filter(m_index), PINDIR_OUTPUT);
 | 
|---|
| 621 |             
 | 
|---|
| 622 |             if (source == m_mediaObject) {
 | 
|---|
| 623 |                 grabFilter(m_fakeSource);
 | 
|---|
| 624 |             }
 | 
|---|
| 625 | 
 | 
|---|
| 626 | #ifdef GRAPH_DEBUG
 | 
|---|
| 627 |             qDebug() << Q_FUNC_INFO << source << sink << this;
 | 
|---|
| 628 | #endif
 | 
|---|
| 629 | 
 | 
|---|
| 630 |             for (int o = 0; o < outputs.count(); o++) {
 | 
|---|
| 631 |                 InputPin p;
 | 
|---|
| 632 |                 for (int i = 0; i < inputs.count(); i++) {
 | 
|---|
| 633 |                     const InputPin &inPin = inputs.at(i);
 | 
|---|
| 634 |                     if (tryConnect(outputs.at(o), inPin)) {
 | 
|---|
| 635 |                         //tell the sink node that it just got a new input
 | 
|---|
| 636 |                         sink->connected(source, inPin);
 | 
|---|
| 637 |                         ret = true;
 | 
|---|
| 638 |                         if (source == m_mediaObject) {
 | 
|---|
| 639 |                             m_connectionsDirty = true;
 | 
|---|
| 640 |                             m_sinkConnections += sink;
 | 
|---|
| 641 | #ifdef GRAPH_DEBUG
 | 
|---|
| 642 |                             qDebug() << "found a sink connection" << sink << m_sinkConnections.count();
 | 
|---|
| 643 | #endif
 | 
|---|
| 644 |                         }
 | 
|---|
| 645 |                         break;
 | 
|---|
| 646 |                     }
 | 
|---|
| 647 |                 }
 | 
|---|
| 648 |             }
 | 
|---|
| 649 | 
 | 
|---|
| 650 |             return ret;
 | 
|---|
| 651 |         }
 | 
|---|
| 652 | 
 | 
|---|
| 653 | 
 | 
|---|
| 654 |         HRESULT MediaGraph::loadSource(const Phonon::MediaSource &source)
 | 
|---|
| 655 |         {
 | 
|---|
| 656 |             m_hasVideo = false;
 | 
|---|
| 657 |             m_hasAudio = false;
 | 
|---|
| 658 |             m_isSeekable = false;
 | 
|---|
| 659 | 
 | 
|---|
| 660 | 
 | 
|---|
| 661 |             //cleanup of the previous filters
 | 
|---|
| 662 |             m_result = cleanup();
 | 
|---|
| 663 |             if (FAILED(m_result)) {
 | 
|---|
| 664 |                 return m_result;
 | 
|---|
| 665 |             }
 | 
|---|
| 666 | 
 | 
|---|
| 667 |             m_mediaSource = source;
 | 
|---|
| 668 | 
 | 
|---|
| 669 |             switch (source.type())
 | 
|---|
| 670 |             {
 | 
|---|
| 671 |             case Phonon::MediaSource::Disc:
 | 
|---|
| 672 |                 if (source.discType() == Phonon::Dvd) {
 | 
|---|
| 673 |                     m_result = E_NOTIMPL;
 | 
|---|
| 674 |                     /*m_realSource = Filter(CLSID_DVDNavigator, IID_IBaseFilter);
 | 
|---|
| 675 |                     if (m_realSource) {
 | 
|---|
| 676 |                         return REGDB_E_CLASSNOTREG;
 | 
|---|
| 677 |                     }
 | 
|---|
| 678 | 
 | 
|---|
| 679 |                     m_result = m_graph->AddFilter(m_realSource, L"DVD Navigator");*/
 | 
|---|
| 680 | 
 | 
|---|
| 681 | 
 | 
|---|
| 682 |  #ifndef QT_NO_PHONON_MEDIACONTROLLER
 | 
|---|
| 683 |                } else if (source.discType() == Phonon::Cd) {
 | 
|---|
| 684 |                     m_realSource = Filter(new QAudioCDPlayer);
 | 
|---|
| 685 | 
 | 
|---|
| 686 | #endif //QT_NO_PHONON_MEDIACONTROLLER
 | 
|---|
| 687 |                 } else {
 | 
|---|
| 688 |                     m_result = E_NOTIMPL;
 | 
|---|
| 689 |                 }
 | 
|---|
| 690 |                 if (FAILED(m_result)) {
 | 
|---|
| 691 |                     return m_result;
 | 
|---|
| 692 |                 }
 | 
|---|
| 693 |                 m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
 | 
|---|
| 694 |                 return m_result;
 | 
|---|
| 695 |             case Phonon::MediaSource::Invalid:
 | 
|---|
| 696 |                 return m_result;
 | 
|---|
| 697 |             case Phonon::MediaSource::Url:
 | 
|---|
| 698 |             case Phonon::MediaSource::LocalFile:
 | 
|---|
| 699 |                 {
 | 
|---|
| 700 |                     QString url;
 | 
|---|
| 701 |                     if (source.type() == Phonon::MediaSource::LocalFile) {
 | 
|---|
| 702 |                         url = source.fileName();
 | 
|---|
| 703 |                     } else {
 | 
|---|
| 704 |                         url = source.url().toString();
 | 
|---|
| 705 |                     }
 | 
|---|
| 706 |                     m_renderId = m_mediaObject->workerThread()->addUrlToRender(url);
 | 
|---|
| 707 |                 }
 | 
|---|
| 708 |                 break;
 | 
|---|
| 709 | #ifndef QT_NO_PHONON_ABSTRACTMEDIASTREAM
 | 
|---|
| 710 |             case Phonon::MediaSource::Stream:
 | 
|---|
| 711 |                 {
 | 
|---|
| 712 |                     m_realSource = Filter(new IODeviceReader(source, this));
 | 
|---|
| 713 |                     m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
 | 
|---|
| 714 |                 }
 | 
|---|
| 715 |                 break;
 | 
|---|
| 716 | #endif //QT_NO_PHONON_ABSTRACTMEDIASTREAM
 | 
|---|
| 717 |             default:
 | 
|---|
| 718 |                 m_result = E_FAIL;
 | 
|---|
| 719 |             }
 | 
|---|
| 720 | 
 | 
|---|
| 721 |             return m_result;
 | 
|---|
| 722 |         }
 | 
|---|
| 723 | 
 | 
|---|
| 724 |         void MediaGraph::finishSeeking(quint16 workId, qint64 time)
 | 
|---|
| 725 |         {
 | 
|---|
| 726 |             if (m_seekId == workId) {
 | 
|---|
| 727 |                 m_currentTime = time;
 | 
|---|
| 728 |                 m_mediaObject->seekingFinished(this);
 | 
|---|
| 729 |                 m_seekId = 0;
 | 
|---|
| 730 |             } else {
 | 
|---|
| 731 |                 //it's a queue seek command
 | 
|---|
| 732 |                 //we're still seeking
 | 
|---|
| 733 |             }
 | 
|---|
| 734 |         }
 | 
|---|
| 735 | 
 | 
|---|
| 736 |         void MediaGraph::finishLoading(quint16 workId, HRESULT hr, Graph graph)
 | 
|---|
| 737 |         {
 | 
|---|
| 738 |             if (m_renderId == workId) {
 | 
|---|
| 739 |                 m_renderId = 0;
 | 
|---|
| 740 | 
 | 
|---|
| 741 |                                 //let's determine if the graph is seekable
 | 
|---|
| 742 |                                 {
 | 
|---|
| 743 |                                         ComPointer<IMediaSeeking> mediaSeeking(graph, IID_IMediaSeeking);
 | 
|---|
| 744 |                                         DWORD caps = AM_SEEKING_CanSeekAbsolute;
 | 
|---|
| 745 |                     m_isSeekable = mediaSeeking && SUCCEEDED(mediaSeeking->CheckCapabilities(&caps));
 | 
|---|
| 746 |                                 }
 | 
|---|
| 747 | 
 | 
|---|
| 748 |                 m_result = reallyFinishLoading(hr, graph);
 | 
|---|
| 749 |                 m_mediaObject->loadingFinished(this);
 | 
|---|
| 750 |             }
 | 
|---|
| 751 |         }
 | 
|---|
| 752 | 
 | 
|---|
| 753 | 
 | 
|---|
| 754 |         HRESULT MediaGraph::reallyFinishLoading(HRESULT hr, const Graph &graph)
 | 
|---|
| 755 |         {
 | 
|---|
| 756 |             if (FAILED(hr)) {
 | 
|---|
| 757 |                 return hr;
 | 
|---|
| 758 |             }
 | 
|---|
| 759 | 
 | 
|---|
| 760 |             const Graph oldGraph = m_graph;
 | 
|---|
| 761 |             m_graph = graph;
 | 
|---|
| 762 | 
 | 
|---|
| 763 |             //we keep the source and all the way down to the decoders
 | 
|---|
| 764 |             QList<Filter> removedFilters;
 | 
|---|
| 765 | 
 | 
|---|
| 766 |                         const QList<Filter> allFilters = getAllFilters(graph);
 | 
|---|
| 767 |             for (int i = 0; i < allFilters.count(); ++i) {
 | 
|---|
| 768 |                 const Filter &filter = allFilters.at(i);
 | 
|---|
| 769 |                 if (isSourceFilter(filter)) {
 | 
|---|
| 770 |                     m_realSource = filter; //save the source filter
 | 
|---|
| 771 |                     if (!m_demux ) {
 | 
|---|
| 772 |                         m_demux = filter; //in the WMV case, the demuxer is the source filter itself
 | 
|---|
| 773 |                     }
 | 
|---|
| 774 |                 } else if (isDemuxerFilter(filter)) {
 | 
|---|
| 775 |                     m_demux = filter;
 | 
|---|
| 776 |                 } else if (isDecoderFilter(filter)) {
 | 
|---|
| 777 |                     m_decoders += filter;
 | 
|---|
| 778 |                     m_decoderPins += BackendNode::pins(filter, PINDIR_OUTPUT).first();
 | 
|---|
| 779 |                 }  else {
 | 
|---|
| 780 |                     removedFilters += filter;
 | 
|---|
| 781 |                 }
 | 
|---|
| 782 |             }
 | 
|---|
| 783 | 
 | 
|---|
| 784 |             for (int i = 0; i < m_decoders.count(); ++i) {
 | 
|---|
| 785 |                 QList<Filter> chain = getFilterChain(m_demux, m_decoders.at(i));
 | 
|---|
| 786 |                 for (int i = 0; i < chain.count(); ++i) {
 | 
|---|
| 787 |                     //we keep those filters
 | 
|---|
| 788 |                     removedFilters.removeOne(chain.at(i));
 | 
|---|
| 789 |                 }
 | 
|---|
| 790 |             }
 | 
|---|
| 791 | 
 | 
|---|
| 792 |             for (int i = 0; i < removedFilters.count(); ++i) {
 | 
|---|
| 793 |                 graph->RemoveFilter(removedFilters.at(i));
 | 
|---|
| 794 |             }
 | 
|---|
| 795 | 
 | 
|---|
| 796 |             m_mediaObject->workerThread()->replaceGraphForEventManagement(graph, oldGraph);
 | 
|---|
| 797 | 
 | 
|---|
| 798 |             //let's transfer the nodes from the current graph to the new one
 | 
|---|
| 799 |             QList<GraphConnection> connections; //we store the connections that need to be restored
 | 
|---|
| 800 | 
 | 
|---|
| 801 |             // First get all the sink nodes (nodes with no input connected)
 | 
|---|
| 802 |             for (int i = 0; i < m_sinkConnections.count(); ++i) {
 | 
|---|
| 803 |                 Filter currentFilter = m_sinkConnections.at(i)->filter(m_index);
 | 
|---|
| 804 |                 connections += getConnections(currentFilter);
 | 
|---|
| 805 |                 grabFilter(currentFilter);
 | 
|---|
| 806 |             }
 | 
|---|
| 807 | 
 | 
|---|
| 808 |             //we need to do something smart to detect if the streams are unencoded
 | 
|---|
| 809 |             if (m_demux) {
 | 
|---|
| 810 |                 const QList<OutputPin> outputs = BackendNode::pins(m_demux, PINDIR_OUTPUT);
 | 
|---|
| 811 |                 for (int i = 0; i < outputs.count(); ++i) {
 | 
|---|
| 812 |                     const OutputPin &out = outputs.at(i);
 | 
|---|
| 813 |                     InputPin pin;
 | 
|---|
| 814 |                     if (out->ConnectedTo(pin.pparam()) == HRESULT(VFW_E_NOT_CONNECTED)) {
 | 
|---|
| 815 |                         m_decoderPins += out; //unconnected outputs can be decoded outputs
 | 
|---|
| 816 |                     }
 | 
|---|
| 817 |                 }
 | 
|---|
| 818 |             }
 | 
|---|
| 819 | 
 | 
|---|
| 820 |             ensureSourceConnectedTo(true);
 | 
|---|
| 821 | 
 | 
|---|
| 822 |             //let's reestablish the connections
 | 
|---|
| 823 |             for (int i = 0; i < connections.count(); ++i) {
 | 
|---|
| 824 |                 const GraphConnection &connection = connections.at(i);
 | 
|---|
| 825 |                 //check if we shoud transfer the sink node
 | 
|---|
| 826 | 
 | 
|---|
| 827 |                 grabFilter(connection.input);
 | 
|---|
| 828 |                 grabFilter(connection.output);
 | 
|---|
| 829 | 
 | 
|---|
| 830 |                 const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset);
 | 
|---|
| 831 |                 const InputPin input   = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset);
 | 
|---|
| 832 |                 HRESULT hr = output->Connect(input, 0);
 | 
|---|
| 833 |                 Q_UNUSED(hr);
 | 
|---|
| 834 |                 Q_ASSERT( SUCCEEDED(hr));
 | 
|---|
| 835 |             }
 | 
|---|
| 836 | 
 | 
|---|
| 837 |             //Finally, let's update the interfaces
 | 
|---|
| 838 |             m_mediaControl = ComPointer<IMediaControl>(graph, IID_IMediaControl);
 | 
|---|
| 839 |             m_mediaSeeking = ComPointer<IMediaSeeking>(graph, IID_IMediaSeeking);
 | 
|---|
| 840 |             return hr;
 | 
|---|
| 841 |         }
 | 
|---|
| 842 | 
 | 
|---|
| 843 |         //utility functions
 | 
|---|
| 844 |         //retrieves the filters between source and sink
 | 
|---|
| 845 |         QList<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink)
 | 
|---|
| 846 |         {
 | 
|---|
| 847 |             QList<Filter> ret;
 | 
|---|
| 848 |             Filter current = sink;
 | 
|---|
| 849 |             while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) {
 | 
|---|
| 850 |                 if (current != source)
 | 
|---|
| 851 |                     ret += current;
 | 
|---|
| 852 |                 InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first();
 | 
|---|
| 853 |                 current = Filter();
 | 
|---|
| 854 |                 OutputPin output;
 | 
|---|
| 855 |                 if (pin->ConnectedTo(output.pparam()) == S_OK) {
 | 
|---|
| 856 |                     PIN_INFO info;
 | 
|---|
| 857 |                     if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) {
 | 
|---|
| 858 |                         current = Filter(info.pFilter); //this will take care of releasing the interface pFilter
 | 
|---|
| 859 |                     }
 | 
|---|
| 860 |                 }
 | 
|---|
| 861 |             }
 | 
|---|
| 862 |             if (current != source) {
 | 
|---|
| 863 |                 //the soruce and sink don't seem to be connected
 | 
|---|
| 864 |                 ret.clear();
 | 
|---|
| 865 |             }
 | 
|---|
| 866 |             return ret;
 | 
|---|
| 867 |         }
 | 
|---|
| 868 | 
 | 
|---|
| 869 |         bool MediaGraph::isDecoderFilter(const Filter &filter)
 | 
|---|
| 870 |         {
 | 
|---|
| 871 |             if (filter == 0) {
 | 
|---|
| 872 |                 return false;
 | 
|---|
| 873 |             }
 | 
|---|
| 874 | #ifdef GRAPH_DEBUG
 | 
|---|
| 875 |             {
 | 
|---|
| 876 |                 FILTER_INFO info;
 | 
|---|
| 877 |                 filter->QueryFilterInfo(&info);
 | 
|---|
| 878 |                 qDebug() << Q_FUNC_INFO << QString((const QChar *)info.achName);
 | 
|---|
| 879 |                 if (info.pGraph) {
 | 
|---|
| 880 |                     info.pGraph->Release();
 | 
|---|
| 881 |                 }
 | 
|---|
| 882 |             }
 | 
|---|
| 883 | #endif
 | 
|---|
| 884 | 
 | 
|---|
| 885 | 
 | 
|---|
| 886 |             QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
 | 
|---|
| 887 |             QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
 | 
|---|
| 888 | 
 | 
|---|
| 889 |             //TODO: find a better way to detect if a node is a decoder
 | 
|---|
| 890 |             if (inputs.count() == 0 || outputs.count() ==0) {
 | 
|---|
| 891 |                 return false;
 | 
|---|
| 892 |             }
 | 
|---|
| 893 | 
 | 
|---|
| 894 |             //the input pin must be encoded data
 | 
|---|
| 895 |             QAMMediaType type;
 | 
|---|
| 896 |             HRESULT hr = inputs.first()->ConnectionMediaType(&type);
 | 
|---|
| 897 |             if (FAILED(hr)) {
 | 
|---|
| 898 |                 return false;
 | 
|---|
| 899 |             }
 | 
|---|
| 900 | 
 | 
|---|
| 901 | 
 | 
|---|
| 902 |             //...and the output must be decoded
 | 
|---|
| 903 |             QAMMediaType type2;
 | 
|---|
| 904 |             hr = outputs.first()->ConnectionMediaType(&type2);
 | 
|---|
| 905 |             if (FAILED(hr)) {
 | 
|---|
| 906 |                 return false;
 | 
|---|
| 907 |             }
 | 
|---|
| 908 | 
 | 
|---|
| 909 |             if (type2.majortype != MEDIATYPE_Video &&
 | 
|---|
| 910 |                 type2.majortype != MEDIATYPE_Audio) {
 | 
|---|
| 911 |                     return false;
 | 
|---|
| 912 |             }
 | 
|---|
| 913 | 
 | 
|---|
| 914 |             if (type2.majortype == MEDIATYPE_Video) {
 | 
|---|
| 915 |                 m_hasVideo = true;
 | 
|---|
| 916 |             } else {
 | 
|---|
| 917 |                 m_hasAudio = true;
 | 
|---|
| 918 |             }
 | 
|---|
| 919 | 
 | 
|---|
| 920 | #ifdef GRAPH_DEBUG
 | 
|---|
| 921 |             {
 | 
|---|
| 922 |                 FILTER_INFO info;
 | 
|---|
| 923 |                 filter->QueryFilterInfo(&info);
 | 
|---|
| 924 |                 qDebug() << "found a decoder filter" << QString((const QChar *)info.achName);
 | 
|---|
| 925 |                 if (info.pGraph) {
 | 
|---|
| 926 |                     info.pGraph->Release();
 | 
|---|
| 927 |                 }
 | 
|---|
| 928 |             }
 | 
|---|
| 929 | #endif
 | 
|---|
| 930 | 
 | 
|---|
| 931 |             return true;
 | 
|---|
| 932 |         }
 | 
|---|
| 933 | 
 | 
|---|
| 934 |         bool MediaGraph::isSourceFilter(const Filter &filter) const
 | 
|---|
| 935 |         {
 | 
|---|
| 936 | #ifdef GRAPH_DEBUG
 | 
|---|
| 937 |             {
 | 
|---|
| 938 |                 FILTER_INFO info;
 | 
|---|
| 939 |                 filter->QueryFilterInfo(&info);
 | 
|---|
| 940 |                 qDebug() << Q_FUNC_INFO << QString((const QChar *)info.achName);
 | 
|---|
| 941 |                 if (info.pGraph) {
 | 
|---|
| 942 |                     info.pGraph->Release();
 | 
|---|
| 943 |                 }
 | 
|---|
| 944 |             }
 | 
|---|
| 945 | #endif
 | 
|---|
| 946 |             //a source filter is one that has no input
 | 
|---|
| 947 |             return BackendNode::pins(filter, PINDIR_INPUT).isEmpty();
 | 
|---|
| 948 |         }
 | 
|---|
| 949 | 
 | 
|---|
| 950 |         bool MediaGraph::isDemuxerFilter(const Filter &filter) const
 | 
|---|
| 951 |         {
 | 
|---|
| 952 |             QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
 | 
|---|
| 953 |             QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
 | 
|---|
| 954 | 
 | 
|---|
| 955 | #ifdef GRAPH_DEBUG
 | 
|---|
| 956 |             {
 | 
|---|
| 957 |                 FILTER_INFO info;
 | 
|---|
| 958 |                 filter->QueryFilterInfo(&info);
 | 
|---|
| 959 |                 qDebug() << Q_FUNC_INFO << QString((const QChar *)info.achName);
 | 
|---|
| 960 |                 if (info.pGraph) {
 | 
|---|
| 961 |                     info.pGraph->Release();
 | 
|---|
| 962 |                 }
 | 
|---|
| 963 |             }
 | 
|---|
| 964 | #endif
 | 
|---|
| 965 | 
 | 
|---|
| 966 |             if (inputs.count() != 1 || outputs.count() == 0) {
 | 
|---|
| 967 |                 return false; //a demuxer has only one input
 | 
|---|
| 968 |             }
 | 
|---|
| 969 | 
 | 
|---|
| 970 |             QAMMediaType type;
 | 
|---|
| 971 |             HRESULT hr = inputs.first()->ConnectionMediaType(&type);
 | 
|---|
| 972 |             if (FAILED(hr)) {
 | 
|---|
| 973 |                 return false;
 | 
|---|
| 974 |             }
 | 
|---|
| 975 | 
 | 
|---|
| 976 |             if (type.majortype != MEDIATYPE_Stream) {
 | 
|---|
| 977 |                 return false;
 | 
|---|
| 978 |             }
 | 
|---|
| 979 | 
 | 
|---|
| 980 |             for (int i = 0; i < outputs.count(); ++i) {
 | 
|---|
| 981 |                 QAMMediaType type;
 | 
|---|
| 982 |                 //for now we support only video and audio
 | 
|---|
| 983 |                 hr = outputs.at(i)->ConnectionMediaType(&type);
 | 
|---|
| 984 |                 if (SUCCEEDED(hr) && 
 | 
|---|
| 985 |                     type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) {
 | 
|---|
| 986 |                         return false;
 | 
|---|
| 987 |                 }
 | 
|---|
| 988 |             }
 | 
|---|
| 989 | #ifdef GRAPH_DEBUG
 | 
|---|
| 990 |             {
 | 
|---|
| 991 |                 FILTER_INFO info;
 | 
|---|
| 992 |                 filter->QueryFilterInfo(&info);
 | 
|---|
| 993 |                 qDebug() << "found a demuxer filter" << QString((const QChar *)info.achName);
 | 
|---|
| 994 |                 if (info.pGraph) {
 | 
|---|
| 995 |                     info.pGraph->Release();
 | 
|---|
| 996 |                 }
 | 
|---|
| 997 |             }
 | 
|---|
| 998 | #endif
 | 
|---|
| 999 |             return true;
 | 
|---|
| 1000 |         }
 | 
|---|
| 1001 | 
 | 
|---|
| 1002 |         QMultiMap<QString, QString> MediaGraph::metadata() const
 | 
|---|
| 1003 |         {
 | 
|---|
| 1004 |             QMultiMap<QString, QString> ret;
 | 
|---|
| 1005 |             ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent);
 | 
|---|
| 1006 |             if (mediaContent) {
 | 
|---|
| 1007 |                 //let's get the meta data
 | 
|---|
| 1008 |                 BSTR str;
 | 
|---|
| 1009 |                 HRESULT hr = mediaContent->get_AuthorName(&str);
 | 
|---|
| 1010 |                 if (SUCCEEDED(hr)) {
 | 
|---|
| 1011 |                     ret.insert(QLatin1String("ARTIST"), QString::fromWCharArray(str));
 | 
|---|
| 1012 |                     SysFreeString(str);
 | 
|---|
| 1013 |                 }
 | 
|---|
| 1014 |                 hr = mediaContent->get_Title(&str);
 | 
|---|
| 1015 |                 if (SUCCEEDED(hr)) {
 | 
|---|
| 1016 |                     ret.insert(QLatin1String("TITLE"), QString::fromWCharArray(str));
 | 
|---|
| 1017 |                     SysFreeString(str);
 | 
|---|
| 1018 |                 }
 | 
|---|
| 1019 |                 hr = mediaContent->get_Description(&str);
 | 
|---|
| 1020 |                 if (SUCCEEDED(hr)) {
 | 
|---|
| 1021 |                     ret.insert(QLatin1String("DESCRIPTION"), QString::fromWCharArray(str));
 | 
|---|
| 1022 |                     SysFreeString(str);
 | 
|---|
| 1023 |                 }
 | 
|---|
| 1024 |                 hr = mediaContent->get_Copyright(&str);
 | 
|---|
| 1025 |                 if (SUCCEEDED(hr)) {
 | 
|---|
| 1026 |                     ret.insert(QLatin1String("COPYRIGHT"), QString::fromWCharArray(str));
 | 
|---|
| 1027 |                     SysFreeString(str);
 | 
|---|
| 1028 |                 }
 | 
|---|
| 1029 |                 hr = mediaContent->get_MoreInfoText(&str);
 | 
|---|
| 1030 |                 if (SUCCEEDED(hr)) {
 | 
|---|
| 1031 |                     ret.insert(QLatin1String("MOREINFO"), QString::fromWCharArray(str));
 | 
|---|
| 1032 |                     SysFreeString(str);
 | 
|---|
| 1033 |                 }
 | 
|---|
| 1034 |             }
 | 
|---|
| 1035 |             return ret;
 | 
|---|
| 1036 |         }
 | 
|---|
| 1037 | 
 | 
|---|
| 1038 |         Filter MediaGraph::realSource() const
 | 
|---|
| 1039 |         {
 | 
|---|
| 1040 |             return m_realSource;
 | 
|---|
| 1041 |         }
 | 
|---|
| 1042 | 
 | 
|---|
| 1043 | #ifndef QT_NO_PHONON_MEDIACONTROLLER
 | 
|---|
| 1044 |         void MediaGraph::setStopPosition(qint64 time)
 | 
|---|
| 1045 |         {
 | 
|---|
| 1046 |             qint64 current = 0,
 | 
|---|
| 1047 |                 stop = 0;
 | 
|---|
| 1048 |             m_mediaSeeking->GetPositions(¤t, &stop);
 | 
|---|
| 1049 | 
 | 
|---|
| 1050 |             const bool shouldSeek = current == stop;
 | 
|---|
| 1051 | 
 | 
|---|
| 1052 |             if (time == -1) {
 | 
|---|
| 1053 |                 HRESULT hr = m_mediaSeeking->GetDuration(&time);
 | 
|---|
| 1054 |                 if (FAILED(hr)) {
 | 
|---|
| 1055 |                     return;
 | 
|---|
| 1056 |                 }
 | 
|---|
| 1057 |             } else {
 | 
|---|
| 1058 |                 time *= 10000;
 | 
|---|
| 1059 |             }
 | 
|---|
| 1060 | 
 | 
|---|
| 1061 |             if (time == stop) {
 | 
|---|
| 1062 |                 //the stop position is already at the right place
 | 
|---|
| 1063 |                 return;
 | 
|---|
| 1064 |             }
 | 
|---|
| 1065 | 
 | 
|---|
| 1066 |             if (shouldSeek) {
 | 
|---|
| 1067 |                 m_mediaSeeking->SetPositions(¤t, AM_SEEKING_AbsolutePositioning, 
 | 
|---|
| 1068 |                     &time, AM_SEEKING_AbsolutePositioning);
 | 
|---|
| 1069 |             } else {
 | 
|---|
| 1070 |                 m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning, 
 | 
|---|
| 1071 |                     &time, AM_SEEKING_AbsolutePositioning);
 | 
|---|
| 1072 |             }
 | 
|---|
| 1073 |         }
 | 
|---|
| 1074 | 
 | 
|---|
| 1075 |         qint64 MediaGraph::stopPosition() const
 | 
|---|
| 1076 |         {
 | 
|---|
| 1077 |             qint64 ret;
 | 
|---|
| 1078 |             m_mediaSeeking->GetStopPosition(&ret);
 | 
|---|
| 1079 |             return ret / 10000;
 | 
|---|
| 1080 | 
 | 
|---|
| 1081 |         }
 | 
|---|
| 1082 | 
 | 
|---|
| 1083 |         QList<qint64> MediaGraph::titles() const
 | 
|---|
| 1084 |         {
 | 
|---|
| 1085 |             //for now we only manage that for the audio cd
 | 
|---|
| 1086 |             ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
 | 
|---|
| 1087 |             if (titleIFace) {
 | 
|---|
| 1088 |                 return titleIFace->titles();
 | 
|---|
| 1089 |             } else {
 | 
|---|
| 1090 |                 // the default value: only one title that starts at position 0
 | 
|---|
| 1091 |                 return QList<qint64>() << 0;
 | 
|---|
| 1092 |             }
 | 
|---|
| 1093 |         }
 | 
|---|
| 1094 | #endif //QT_NO_PHONON_MEDIACONTROLLER
 | 
|---|
| 1095 | 
 | 
|---|
| 1096 | 
 | 
|---|
| 1097 | 
 | 
|---|
| 1098 |     }
 | 
|---|
| 1099 | }
 | 
|---|
| 1100 | 
 | 
|---|
| 1101 | QT_END_NAMESPACE
 | 
|---|