#include "fakesource.h"
#include "iodevicereader.h"
#include "qaudiocdreader.h"
#include "mediagraph.h"
#include "mediaobject.h"
#include <QtCore/QUrl>
#include <qnetwork.h>
QT_BEGIN_NAMESPACE
uint qHash (const Phonon::DS9::Filter &f)
{
return uint(static_cast<IBaseFilter*>(f));
}
uint qHash (const Phonon::DS9::OutputPin &p)
{
return uint(static_cast<IPin*>(p));
}
namespace Phonon
{
namespace DS9
{
struct GraphConnection
{
Filter output;
int outputOffset;
Filter input;
int inputOffset;
};
QList<GraphConnection> getOrderedConnections(Filter source, const QSet<Filter> &validFilters)
{
QList<GraphConnection> ret;
int outOffset = 0;
foreach(OutputPin output, BackendNode::pins(source, PINDIR_OUTPUT)) {
InputPin input;
if (output->ConnectedTo(input.pparam()) == S_OK) {
PIN_INFO info;
input->QueryPinInfo(&info);
Filter current(info.pFilter);
if (current && validFilters.contains(current)) {
const int inOffset = BackendNode::pins(current, PINDIR_INPUT).indexOf(input);
const GraphConnection connection = {source, outOffset, current, inOffset};
ret += connection;
ret += getOrderedConnections(current, validFilters);
}
}
outOffset++;
}
return ret;
}
MediaGraph::MediaGraph(MediaObject *mo, short index) : m_fakeSource(new FakeSource()),
m_hasVideo(false), m_hasAudio(false), m_connectionsDirty(false),
m_isStopping(false), m_isSeekable(false), m_result(S_OK),
m_index(index), m_mediaObject(mo),
m_graph(CLSID_FilterGraph, IID_IGraphBuilder),
m_renderId(0), m_seekId(0), m_currentTime(0)
{
m_mediaControl = ComPointer<IMediaControl>(m_graph, IID_IMediaControl);
Q_ASSERT(m_mediaControl);
m_mediaSeeking = ComPointer<IMediaSeeking>(m_graph, IID_IMediaSeeking);
Q_ASSERT(m_mediaSeeking);
m_mediaObject->workerThread()->addGraphForEventManagement(m_graph);
HRESULT hr = m_graph->AddFilter(m_fakeSource, L"Fake Source");
if (m_mediaObject->catchComError(hr)) {
return;
}
connect(mo->workerThread(), SIGNAL(asyncRenderFinished(quint16, HRESULT, Graph)),
SLOT(finishLoading(quint16, HRESULT, Graph)));
connect(mo->workerThread(), SIGNAL(asyncSeekingFinished(quint16, qint64)),
SLOT(finishSeeking(quint16, qint64)));
}
MediaGraph::~MediaGraph()
{
}
short MediaGraph::index() const
{
return m_index;
}
void MediaGraph::grabNode(BackendNode *node)
{
FILTER_INFO info;
const Filter filter = node->filter(m_index);
if (filter) {
filter->QueryFilterInfo(&info);
if (info.pGraph == 0) {
HRESULT hr = m_graph->AddFilter(filter, 0);
m_mediaObject->catchComError(hr);
} else {
info.pGraph->Release();
}
}
}
void MediaGraph::switchFilters(Filter oldFilter, Filter newFilter)
{
OAFilterState state = syncGetRealState();
if (state != State_Stopped) {
ensureStopped();
}
OutputPin connected;
{
InputPin pin = BackendNode::pins(oldFilter, PINDIR_INPUT).first();
pin->ConnectedTo(connected.pparam());
}
m_graph->RemoveFilter(oldFilter);
m_graph->AddFilter(newFilter, 0);
if (connected) {
InputPin pin = BackendNode::pins(newFilter, PINDIR_INPUT).first();
m_graph->Connect(connected, pin);
}
switch(state)
{
case State_Running:
play();
break;
case State_Paused:
pause();
break;
default:
break;
}
}
OAFilterState MediaGraph::syncGetRealState() const
{
OAFilterState state;
m_mediaControl->GetState(INFINITE, &state);
return state;
}
void MediaGraph::ensureSourceDisconnected()
{
foreach(BackendNode *node, m_sinkConnections) {
const Filter currentFilter = node->filter(m_index);
foreach(const InputPin inpin, BackendNode::pins(currentFilter, PINDIR_INPUT)) {
foreach(const OutputPin outpin, BackendNode::pins(m_fakeSource, PINDIR_OUTPUT)) {
tryDisconnect(outpin, inpin);
}
foreach(const OutputPin outpin, m_decoderPins) {
tryDisconnect(outpin, inpin);
}
}
}
}
void MediaGraph::ensureSourceConnectedTo(bool force)
{
if (m_connectionsDirty == false && force == false) {
return;
}
m_connectionsDirty = false;
ensureSourceDisconnected();
foreach(BackendNode *node, m_sinkConnections) {
const Filter currentFilter = node->filter(m_index);
foreach(const InputPin &inpin, BackendNode::pins(currentFilter, PINDIR_INPUT)) {
FILTER_INFO info;
currentFilter->QueryFilterInfo(&info);
if (info.pGraph == 0) {
m_graph->AddFilter(currentFilter, 0);
} else {
info.pGraph->Release();
}
foreach(const OutputPin outpin, m_decoderPins) {
if (tryConnect(outpin, inpin)) {
break;
}
}
}
}
}
QSet<Filter> MediaGraph::getAllFilters(Graph graph)
{
QSet<Filter> ret;
ComPointer<IEnumFilters> enumFilters;
graph->EnumFilters(enumFilters.pparam());
Filter current;
while( enumFilters && enumFilters->Next(1, current.pparam(), 0) == S_OK) {
ret += current;
}
return ret;
}
QSet<Filter> MediaGraph::getAllFilters() const
{
return getAllFilters(m_graph);
}
bool MediaGraph::isSeekable() const
{
return m_isSeekable;
}
qint64 MediaGraph::absoluteTotalTime() const
{
if (m_seekId) {
return m_totalTime;
} else {
qint64 ret = 0;
if (m_mediaSeeking) {
m_mediaSeeking->GetDuration(&ret);
ret /= 10000;
}
return ret;
}
}
qint64 MediaGraph::absoluteCurrentTime() const
{
if (m_seekId) {
return m_currentTime;
} else {
qint64 ret = -1;
if (m_mediaSeeking) {
HRESULT hr = m_mediaSeeking->GetCurrentPosition(&ret);
if (FAILED(hr)) {
return ret;
}
ret /= 10000;
}
return ret;
}
}
Phonon::MediaSource MediaGraph::mediaSource() const
{
return m_mediaSource;
}
void MediaGraph::play()
{
ensureSourceConnectedTo();
m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Running, m_decoders);
}
void MediaGraph::pause()
{
ensureSourceConnectedTo();
m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Paused, m_decoders);
}
HRESULT MediaGraph::renderResult() const
{
return m_result;
}
bool MediaGraph::isStopping() const
{
return m_isStopping;
}
Graph MediaGraph::graph() const
{
return m_graph;
}
void MediaGraph::stop()
{
if (!isLoading()) {
ensureStopped();
absoluteSeek(0);
} else {
m_mediaObject->workerThread()->abortCurrentRender(m_renderId);
m_renderId = 0;
}
m_mediaObject->workerThread()->addStateChangeRequest(m_graph, State_Stopped);
}
void MediaGraph::ensureStopped()
{
m_isStopping = true;
m_graph->Abort();
m_mediaControl->Stop();
m_isStopping = false;
}
bool MediaGraph::isLoading() const
{
return m_renderId != 0;
}
void MediaGraph::absoluteSeek(qint64 time)
{
if (m_seekId == 0) {
m_currentTime = absoluteCurrentTime();
m_totalTime = absoluteTotalTime();
}
m_seekId = m_mediaObject->workerThread()->addSeekRequest(m_graph, time);
}
HRESULT MediaGraph::removeFilter(const Filter& filter)
{
FILTER_INFO info;
filter->QueryFilterInfo(&info);
#ifdef GRAPH_DEBUG
qDebug() << "removeFilter" << QString::fromWCharArray(info.achName);
#endif
if (info.pGraph) {
info.pGraph->Release();
return m_graph->RemoveFilter(filter);
}
return S_OK;
}
HRESULT MediaGraph::cleanup()
{
stop();
ensureSourceDisconnected();
QSet<Filter> list = m_decoders;
if (m_demux) {
list << m_demux;
}
if (m_realSource) {
list << m_realSource;
}
foreach(const Filter &decoder, m_decoders) {
list += getFilterChain(m_demux, decoder);
}
foreach(const Filter &filter, list) {
HRESULT hr = removeFilter(filter);
if(FAILED(hr)) {
return hr;
}
}
m_decoderPins.clear();
m_decoders.clear();
m_demux = Filter();
m_realSource = Filter();
m_mediaSource = Phonon::MediaSource();
absoluteSeek(0);
return S_OK;
}
bool MediaGraph::disconnectNodes(BackendNode *source, BackendNode *sink)
{
const Filter sinkFilter = sink->filter(m_index);
const QList<InputPin> inputs = BackendNode::pins(sinkFilter, PINDIR_INPUT);
QSet<OutputPin> outputs;
if (source == m_mediaObject) {
outputs = BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).toSet();
outputs += m_decoderPins;
} else {
outputs = BackendNode::pins(source->filter(m_index), PINDIR_OUTPUT).toSet();
}
foreach(InputPin inPin, inputs) {
foreach(OutputPin outPin, outputs) {
tryDisconnect(outPin, inPin);
}
}
if (m_sinkConnections.remove(sink)) {
m_connectionsDirty = true;
}
return true;
}
bool MediaGraph::tryDisconnect(const OutputPin &out, const InputPin &in)
{
bool ret = false;
OutputPin output;
if (SUCCEEDED(in->ConnectedTo(output.pparam()))) {
if (output == out) {
ret = SUCCEEDED(out->Disconnect()) && SUCCEEDED(in->Disconnect());
} else {
InputPin in2;
if (SUCCEEDED(out->ConnectedTo(in2.pparam()))) {
PIN_INFO info;
in2->QueryPinInfo(&info);
Filter tee(info.pFilter);
CLSID clsid;
tee->GetClassID(&clsid);
if (clsid == CLSID_InfTee) {
PIN_INFO info;
in->QueryPinInfo(&info);
Filter sink(info.pFilter);
QSet<Filter> list = getFilterChain(tee, sink);
list -= sink;
list -= tee;
out->QueryPinInfo(&info);
Filter source(info.pFilter);
if (list.isEmpty()) {
output->QueryPinInfo(&info);
if (Filter(info.pFilter) == tee) {
ret = SUCCEEDED(output->Disconnect()) && SUCCEEDED(in->Disconnect());
}
} else {
ret = true;
foreach(Filter f, list) {
ret = ret && SUCCEEDED(removeFilter(f));
}
}
if (ret) {
int connections = 0;
foreach (OutputPin out, BackendNode::pins(tee, PINDIR_OUTPUT)) {
InputPin p;
if ( SUCCEEDED(out->ConnectedTo(p.pparam()))) {
connections++;
}
}
if (connections == 0) {
output = OutputPin();
removeFilter(tee);
}
}
}
}
}
}
return ret;
}
bool MediaGraph::tryConnect(const OutputPin &out, const InputPin &newIn)
{
InputPin inPin;
if (SUCCEEDED(out->ConnectedTo(inPin.pparam()))) {
if (BackendNode::pins(m_fakeSource, PINDIR_OUTPUT).contains(out)) {
return false;
}
PIN_INFO info;
inPin->QueryPinInfo(&info);
Filter filter(info.pFilter);
CLSID clsid;
filter->GetClassID(&clsid);
if (clsid == CLSID_InfTee) {
foreach(OutputPin pin, BackendNode::pins(filter, PINDIR_OUTPUT)) {
if (VFW_E_NOT_CONNECTED == pin->ConnectedTo(inPin.pparam())) {
return SUCCEEDED(pin->Connect(newIn, 0));
}
}
return false;
} else {
QAMMediaType type;
out->ConnectionMediaType(&type);
if (!tryDisconnect(out, inPin)) {
return false;
}
if (SUCCEEDED(out->Connect(newIn, 0))) {
if (!tryDisconnect(out, newIn)) {
return false;
}
Filter filter(CLSID_InfTee, IID_IBaseFilter);
if (!filter) {
m_graph->Connect(out, inPin);
return false;
}
if (FAILED(m_graph->AddFilter(filter, 0))) {
return false;
}
InputPin teeIn = BackendNode::pins(filter, PINDIR_INPUT).first();
HRESULT hr = out->Connect(teeIn, &type);
if (FAILED(hr)) {
hr = m_graph->Connect(out, teeIn);
}
if (FAILED(hr)) {
m_graph->Connect(out, inPin);
return false;
}
OutputPin teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last();
hr = m_graph->Connect(teeOut, inPin);
if (FAILED(hr)) {
m_graph->Connect(out, inPin);
return false;
}
teeOut = BackendNode::pins(filter, PINDIR_OUTPUT).last();
if (FAILED(m_graph->Connect(teeOut, newIn))) {
m_graph->Connect(out, inPin);
return false;
}
return true;
} else {
m_graph->Connect(out, inPin);
return false;
}
}
} else {
return SUCCEEDED(m_graph->Connect(out, newIn));
}
}
bool MediaGraph::connectNodes(BackendNode *source, BackendNode *sink)
{
bool ret = false;
const QList<InputPin> inputs = BackendNode::pins(sink->filter(m_index), PINDIR_INPUT);
const QList<OutputPin> outputs = BackendNode::pins(source == m_mediaObject ? m_fakeSource : source->filter(m_index), PINDIR_OUTPUT);
#ifdef GRAPH_DEBUG
qDebug() << Q_FUNC_INFO << source << sink << this;
#endif
foreach(OutputPin outPin, outputs) {
InputPin p;
foreach(InputPin inPin, inputs) {
if (tryConnect(outPin, inPin)) {
sink->connected(source, inPin);
ret = true;
if (source == m_mediaObject) {
m_connectionsDirty = true;
m_sinkConnections += sink;
#ifdef GRAPH_DEBUG
qDebug() << "found a sink connection" << sink << m_sinkConnections.count();
#endif
}
break;
}
}
}
return ret;
}
HRESULT MediaGraph::loadSource(const Phonon::MediaSource &source)
{
m_hasVideo = false;
m_hasAudio = false;
m_isSeekable = false;
m_result = cleanup();
if (FAILED(m_result)) {
return m_result;
}
m_mediaSource = source;
switch (source.type())
{
case Phonon::MediaSource::Disc:
if (source.discType() == Phonon::Dvd) {
m_result = E_NOTIMPL;
} else if (source.discType() == Phonon::Cd) {
m_realSource = Filter(new QAudioCDPlayer);
m_result = m_graph->AddFilter(m_realSource, L"Audio CD Reader");
} else {
m_result = E_NOTIMPL;
}
if (FAILED(m_result)) {
return m_result;
}
m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
return m_result;
case Phonon::MediaSource::Invalid:
return m_result;
case Phonon::MediaSource::Url:
case Phonon::MediaSource::LocalFile:
{
QString url;
if (source.type() == Phonon::MediaSource::LocalFile) {
url = source.fileName();
} else {
url = source.url().toString();
}
m_renderId = m_mediaObject->workerThread()->addUrlToRender(url);
}
break;
case Phonon::MediaSource::Stream:
{
m_realSource = Filter(new IODeviceReader(source, this));
m_result = m_graph->AddFilter(m_realSource, L"Phonon Stream Reader");
if (FAILED(m_result)) {
return m_result;
}
m_renderId = m_mediaObject->workerThread()->addFilterToRender(m_realSource);
}
break;
}
return m_result;
}
void MediaGraph::finishSeeking(quint16 workId, qint64 time)
{
if (m_seekId == workId) {
m_currentTime = time;
emit seekingFinished(this);
m_seekId = 0;
} else {
}
}
void MediaGraph::finishLoading(quint16 workId, HRESULT hr, Graph graph)
{
if (m_renderId == workId) {
m_renderId = 0;
{
ComPointer<IMediaSeeking> mediaSeeking(graph, IID_IMediaSeeking);
DWORD caps = AM_SEEKING_CanSeekAbsolute;
m_isSeekable = mediaSeeking && SUCCEEDED(mediaSeeking->CheckCapabilities(&caps));
}
m_result = reallyFinishLoading(hr, graph);
emit loadingFinished(this);
}
}
HRESULT MediaGraph::reallyFinishLoading(HRESULT hr, const Graph &graph)
{
if (FAILED(hr)) {
return hr;
}
QSet<Filter> keptFilters;
const QSet<Filter> allFilters = getAllFilters(graph);
foreach(const Filter &filter, allFilters) {
if (isSourceFilter(filter)) {
m_realSource = filter;
if (!m_demux ) {
m_demux = filter;
}
keptFilters << filter;
} else if (isDemuxerFilter(filter)) {
m_demux = filter;
keptFilters << filter;
} else if (isDecoderFilter(filter)) {
m_decoders += filter;
m_decoderPins += BackendNode::pins(filter, PINDIR_OUTPUT).first();
keptFilters << filter;
}
}
foreach(const Filter &decoder, m_decoders) {
keptFilters += getFilterChain(m_demux, decoder);
}
const QList<GraphConnection> connections = getOrderedConnections(m_realSource, keptFilters);
foreach(const Filter filter, keptFilters) {
graph->RemoveFilter(filter);
hr = m_graph->AddFilter(filter, 0);
if (FAILED(hr)) {
return hr;
}
}
foreach(GraphConnection connection, connections) {
const OutputPin output = BackendNode::pins(connection.output, PINDIR_OUTPUT).at(connection.outputOffset);
const InputPin input = BackendNode::pins(connection.input, PINDIR_INPUT).at(connection.inputOffset);
m_graph->Connect( output, input);
}
if (m_demux) {
foreach(const OutputPin out, BackendNode::pins(m_demux, PINDIR_OUTPUT)) {
InputPin pin;
if (out->ConnectedTo(pin.pparam()) == VFW_E_NOT_CONNECTED) {
m_decoderPins += out;
}
}
}
ensureSourceConnectedTo(true);
return hr;
}
QSet<Filter> MediaGraph::getFilterChain(const Filter &source, const Filter &sink)
{
QSet<Filter> ret;
Filter current = sink;
while (current && BackendNode::pins(current, PINDIR_INPUT).count() == 1 && current != source) {
ret += current;
InputPin pin = BackendNode::pins(current, PINDIR_INPUT).first();
current = Filter();
OutputPin output;
if (pin->ConnectedTo(output.pparam()) == S_OK) {
PIN_INFO info;
if (SUCCEEDED(output->QueryPinInfo(&info)) && info.pFilter) {
current = Filter(info.pFilter);
}
}
}
if (current != source) {
ret.clear();
}
return ret;
}
bool MediaGraph::isDecoderFilter(const Filter &filter)
{
if (filter == 0) {
return false;
}
#ifdef GRAPH_DEBUG
{
FILTER_INFO info;
filter->QueryFilterInfo(&info);
qDebug() << Q_FUNC_INFO << QString::fromWCharArray(info.achName);
if (info.pGraph) {
info.pGraph->Release();
}
}
#endif
QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
if (inputs.count() == 0 || outputs.count() ==0) {
return false;
}
QAMMediaType type;
HRESULT hr = inputs.first()->ConnectionMediaType(&type);
if (FAILED(hr)) {
return false;
}
QAMMediaType type2;
hr = outputs.first()->ConnectionMediaType(&type2);
if (FAILED(hr)) {
return false;
}
if (type2.majortype != MEDIATYPE_Video &&
type2.majortype != MEDIATYPE_Audio) {
return false;
}
if (type2.majortype == MEDIATYPE_Video) {
m_hasVideo = true;
} else {
m_hasAudio = true;
}
#ifdef GRAPH_DEBUG
{
FILTER_INFO info;
filter->QueryFilterInfo(&info);
qDebug() << "found a decoder filter" << QString::fromWCharArray(info.achName);
if (info.pGraph) {
info.pGraph->Release();
}
}
#endif
return true;
}
bool MediaGraph::isSourceFilter(const Filter &filter) const
{
return BackendNode::pins(filter, PINDIR_INPUT).isEmpty();
}
bool MediaGraph::isDemuxerFilter(const Filter &filter) const
{
QList<InputPin> inputs = BackendNode::pins(filter, PINDIR_INPUT);
QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT);
#ifdef GRAPH_DEBUG
{
FILTER_INFO info;
filter->QueryFilterInfo(&info);
qDebug() << Q_FUNC_INFO << QString::fromWCharArray(info.achName);
if (info.pGraph) {
info.pGraph->Release();
}
}
#endif
if (inputs.count() != 1 || outputs.count() == 0) {
return false;
}
QAMMediaType type;
HRESULT hr = inputs.first()->ConnectionMediaType(&type);
if (FAILED(hr)) {
return false;
}
if (type.majortype != MEDIATYPE_Stream) {
return false;
}
foreach(const OutputPin &outpin, outputs) {
QAMMediaType type;
hr = outpin->ConnectionMediaType(&type);
if (SUCCEEDED(hr) &&
type.majortype != MEDIATYPE_Video && type.majortype != MEDIATYPE_Audio) {
return false;
}
}
#ifdef GRAPH_DEBUG
{
FILTER_INFO info;
filter->QueryFilterInfo(&info);
qDebug() << "found a demuxer filter" << QString::fromWCharArray(info.achName);
if (info.pGraph) {
info.pGraph->Release();
}
}
#endif
return true;
}
QMultiMap<QString, QString> MediaGraph::metadata() const
{
QMultiMap<QString, QString> ret;
ComPointer<IAMMediaContent> mediaContent(m_demux, IID_IAMMediaContent);
if (mediaContent) {
BSTR str;
HRESULT hr = mediaContent->get_AuthorName(&str);
if (SUCCEEDED(hr)) {
ret.insert(QLatin1String("ARTIST"), QString::fromWCharArray(str));
SysFreeString(str);
}
hr = mediaContent->get_Title(&str);
if (SUCCEEDED(hr)) {
ret.insert(QLatin1String("TITLE"), QString::fromWCharArray(str));
SysFreeString(str);
}
hr = mediaContent->get_Description(&str);
if (SUCCEEDED(hr)) {
ret.insert(QLatin1String("DESCRIPTION"), QString::fromWCharArray(str));
SysFreeString(str);
}
hr = mediaContent->get_Copyright(&str);
if (SUCCEEDED(hr)) {
ret.insert(QLatin1String("COPYRIGHT"), QString::fromWCharArray(str));
SysFreeString(str);
}
hr = mediaContent->get_MoreInfoText(&str);
if (SUCCEEDED(hr)) {
ret.insert(QLatin1String("MOREINFO"), QString::fromWCharArray(str));
SysFreeString(str);
}
}
return ret;
}
Filter MediaGraph::realSource() const
{
return m_realSource;
}
void MediaGraph::setStopPosition(qint64 time)
{
qint64 current = 0,
stop = 0;
m_mediaSeeking->GetPositions(¤t, &stop);
const bool shouldSeek = current == stop;
if (time == -1) {
HRESULT hr = m_mediaSeeking->GetDuration(&time);
if (FAILED(hr)) {
return;
}
} else {
time *= 10000;
}
if (time == stop) {
return;
}
if (shouldSeek) {
m_mediaSeeking->SetPositions(¤t, AM_SEEKING_AbsolutePositioning,
&time, AM_SEEKING_AbsolutePositioning);
} else {
m_mediaSeeking->SetPositions(0, AM_SEEKING_NoPositioning,
&time, AM_SEEKING_AbsolutePositioning);
}
}
qint64 MediaGraph::stopPosition() const
{
qint64 ret;
m_mediaSeeking->GetStopPosition(&ret);
return ret / 10000;
}
QList<qint64> MediaGraph::titles() const
{
ComPointer<ITitleInterface> titleIFace(m_realSource, IID_ITitleInterface);
if (titleIFace) {
return titleIFace->titles();
} else {
return QList<qint64>() << 0;
}
}
HRESULT MediaGraph::saveToFile(const QString &filepath) const
{
const WCHAR wszStreamName[] = L"ActiveMovieGraph";
HRESULT hr;
ComPointer<IStorage> storage;
hr = StgCreateDocfile(reinterpret_cast<const wchar_t *>(filepath.utf16()),
STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE |
STGM_SHARE_EXCLUSIVE,
0, storage.pparam());
if (FAILED(hr)) {
return hr;
}
ComPointer<IStream> stream;
hr = storage->CreateStream(wszStreamName,
STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
0, 0, stream.pparam());
if (FAILED(hr)) {
return hr;
}
ComPointer<IPersistStream> persist(m_graph, IID_IPersistStream);
hr = persist->Save(stream, TRUE);
if (SUCCEEDED(hr)) {
hr = storage->Commit(STGC_DEFAULT);
}
return hr;
}
}
}
QT_END_NAMESPACE
#include "moc_mediagraph.cpp"