PDA

View Full Version : QNetworkAccessManager and phonon - can it be done?



serenti
12th June 2009, 19:18
I have a problem with phonon: I need to stream audio from a password protected site, and there doesn't seem to be any way to set authentication information in Phonon::MediaSource, so it displays the default Windows authentication dialog for every file I play. I figured that maybe I could use QNetworkAccessManager to stream and phonon to play, which would solve the authentication problem, but so far I wasn't able to get the two to work together.

I tried a lot of different variations, all with the basic idea that QNetworkReply inherits from QIODevice, so I should be able to set it as setCurrentSource of MediaObject. But it just refuses to play while streaming. It does work if I call play() after the entire download finishes. But it won't play while it downloads. Here's a simplified example to give you the idea:


QNetworkRequest request;
request.setUrl(QUrl("http://localhost/mm132.mp3"));
networkReply = networkAccessManager.get(request);

mediaObject = Phonon::createPlayer(Phonon::MusicCategory);
mediaObject->setCurrentSource(networkReply);
mediaObject->play()

I also tried creating a separate QByteArray and write to it in readReady() and then use it with QBuffer in read-only mode, assigning it to MediaSource, but it won't work either. Maybe my thinking is too simplistic and it needs some more complex buffering system...

Anyone has any suggestions on how this can be done? (Can it be done?) Or is there a way to overwrite that default authentication dialog in phonon and supply the user name and password some other way?

I would appreciate any ideas or snippets of code you might have. Thank you.

wysota
15th June 2009, 19:34
How did you try to set the auth data on the request? Did you use the appropriate auth mechanism?

serenti
15th June 2009, 20:00
Thank you for answering. As far as the proper authentication mechanism on QNetworkAccessManager request, then yes, I did. I get the data downloaded properly, it's the playing of that data with phonon that doesn't work.

As far as Phonon::MediaSource, there doesn't seem to be a mechanism for setting username and password. That's the problem.

wysota
15th June 2009, 20:35
Have you ensured there is enough data downloaded for the media to be recognized by the player? You can start playing immediately after sending the request, remember there is no buffering involved.

serenti
15th June 2009, 20:43
I thought about this, so I tried playing only after the buffer is a certain size, but it didn't work either. I'm not sure how much data is enough... But tell me, is this approach even doable (simply)? Because if you can see that this won't work properly (in a user-friendly fashion) without writing some complex buffering system, than I won't be spending time on it. I can use a flash player in a QWebView to accomplish pretty much the same thing, and that will work for sure.

wysota
15th June 2009, 21:18
I thought about this, so I tried playing only after the buffer is a certain size, but it didn't work either. I'm not sure how much data is enough... But tell me, is this approach even doable (simply)?
If it works when the download is complete when reading from the network reply then it should also work during download. It's hard for me to say how much of the data should be available, it probably depends on the codec used. I would allow at least a megabyte to be downloaded before I started playing... Also make sure the network reply object is open in a proper mode.

serenti
15th June 2009, 22:07
Thank you for your comments. I think I'll try using a Flash player in a QWebView instead. It seems simpler and works out of the box. I'll just need to check if flash objects work on all three platforms.

Pozdrawiam.

wysota
15th June 2009, 22:21
I just made a small example application and it works fine for me. I can download a file using QNetworkAccessManager and set the stream being downloaded as a phonon media source. The setting routine looks like so:

void downloadProgress(qint64 cur,qint64 tot) {
if(!loaded && cur>1024){
qDebug() << "Loading...";
pl->load(Phonon::MediaSource((QIODevice*)sender()));
connect(pl->mediaObject(), SIGNAL(stateChanged(Phonon::State,Phonon::State)),
this, SLOT(onStateChanged(Phonon::State,Phonon::State))) ;
loaded = true;
}
}

where "pl" is a Phonon::VideoPlayer and "loaded" is a boolean where I check if I have already set the source on the player.

serenti
16th June 2009, 01:22
There must be something wrong on my end, because I can't make it play with your sample code either. Could you take a look? The file does download, the video player does work when used directly with the url, all the slots are connected and execute... Any ideas?


test::test(QWidget *parent, Qt::WFlags flags)
: QDialog(parent, flags)
{
ui.setupUi(this);

pl = new Phonon::VideoPlayer(Phonon::MusicCategory, this);

// this plays fine, meaning the video player does work:
// pl->load(Phonon::MediaSource(QUrl("http://localhost/hello.mp3")));
// pl->play();
// return;

loaded = false;
networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(networkManagerFinished(QNetworkReply*)));
connect(networkManager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
this, SLOT(authenticationRequired(QNetworkReply*, QAuthenticator*)));

networkReply = networkManager->get(QNetworkRequest(QUrl("http://localhost/hello.mp3")));
connect(networkReply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(downloadProgress(qint64, qint64)));
}

test::~test()
{
}

void test::downloadProgress(qint64 cur,qint64 tot)
{
if(!loaded && cur > 1024)
{
qDebug() << "Loading...";
pl->load(Phonon::MediaSource((QIODevice*)sender()));
connect(pl->mediaObject(), SIGNAL(stateChanged(Phonon::State,Phonon::State)),
this, SLOT(onStateChanged(Phonon::State, Phonon::State)));
loaded = true;
}
}

void test::onStateChanged(Phonon::State newState,Phonon::State oldState)
{
// here we always get Phonon::ErrorState
qDebug() << "New state: " << newState;
}

void test::networkManagerFinished(QNetworkReply *reply)
{
// this works, it saves the file in full, and I can play it
QByteArray byteArray = reply->readAll();
byteArrayToFile(&byteArray, "d:/hello.mp3");
}

void test::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
{
// this is not used on localhost, but it does authenticate properly when using with the site on line
qDebug() << "Authenticating";
authenticator->setUser("username");
authenticator->setPassword("password");
}

wysota
16th June 2009, 09:46
What platform and phonon backend do you use? It seems you're trying this on Windows, I've been using the xine backend on Linux, maybe they work differently.

Comparing my code to yours - try increasing the read buffer of the network reply (set it to 0). Also try dumping Phonon::MediaObject::errorString() to debug and see what the error is.

serenti
16th June 2009, 17:37
The error I'm getting from MediaObject::errorString() is:

Error: "No combination of filters could be found to render the stream. (0x80040218)"

I increased the read buffer of the network reply to 0, but it didn't help.

As far as the backend, I only have DirectSound available. I tried all 4 possible output devices that exist on my computer (Windows XP), none of them work:

Output device: Default DirectSound Device
Output device: DirectSound: Realtek HD Audio output
Output device: Realtek HD Audio output
Output device: Default WaveOut Device

I set them properly and then made sure they are used this way:


void test::onStateChanged(Phonon::State newState,Phonon::State oldState)
{
// here we always get Phonon::ErrorState
qDebug() << "New state: " << newState;
qDebug() << "Error: " << pl->mediaObject()->errorString();
qDebug() << "Output device: " << pl->audioOutput()->outputDevice().name();
}

So I guess the unfortunate conclusion of our tests is that this particular functionality depends on the backend used. Which, in turn, means I can't use phonon for what I need. I'd be happy to use the default streaming capabilities of MediaObject if only there was a way to set the user name and password, so I don't get the default login dialog every time it connects. Well, maybe in the future versions they'll make this possible.

Thank you for all your help. It's great to know you are there to help whenever we need it. I guess I'll just stick to using a Flash Player for now. I'd prefer using phonon, since it's more "native", but using Flash is not a bad solution, and it works well. (At least on Windows :). I still need to check it on other platforms.). Thanks again.

wysota
16th June 2009, 21:45
Maybe a better thing would be to use some mpg library directly? If it's just about playing mp3 files then probably you could just use libmad functionality. Having a web browser for playing audio files seems strange.

http://www.underbit.com/products/mad/

serenti
17th June 2009, 00:10
Thank you for the suggestion. I'll look into it.

KBHomes
5th April 2010, 05:50
I know that this a particularly old thread, but it did not seem to have a definitive solution, and having spent a while looking around for one, I figured that others could benefit from my solution. Actually, now that I think about it, the poster and I probably have different problems; I didn't get any filter errors, but I couldn't get it to play either.

My solution was basically create the QByteArray that backed my QBuffer to the actual length of the MP3 and fill it with '\0' (QByteArray *ba = new QByteArray(length, '\0');). I did this by retrieving the ContentLength header from the QNetworkReply on the very first readRead() signal (by way of using a firstRead boolean, or, in my case, checking the buffer and byte array pointers). Then you can send the QBuffer to the MediaObject and it works like a charm. Whenever you need to write to the byte array, simply use ba->replace(lastPosition, readData->size(), readData);, and then increase lastPosition by the readData->size(), where lastPosition is simply a variable to hold the next place to write in the byte array, and readData is the data read from the QNetworkReply.

Hopefully this would have worked, although chances are there are other causes that lead to the same symptoms.

mep
17th July 2011, 22:39
Hey KBHomes,

I tried your suggestion and Phonon begins to play. But it immediately stops. I am waiting till the Reply loaded 20000 bytes and I start playing then. But I think after these 20000 bytes Phonon stops playing, and I really do not know why.

Could you show me your complete solution?

Thanks in advance!

PS: I know your post was a year ago, but I hope nevertheless receiving an answer

derektmm
1st December 2011, 19:43
Hi Mep,
Just wondering if you solved this problem, as I have the same problem now. If I buffer q LOT of data (say 90 or 100Kb) then it will play continuously. If not, it will play the buffered data and then stop forever.

Did you find a way to tell Phonon to keep reading?

Thanks,
Derek

mep
2nd December 2011, 19:32
Hey,

I noticed that it just did not work on my Mac, and I havent found a solution for it. On my Windows and Linux machine this approach works for me, but I think it is not a very nice solution.

I found this: https://bugreports.qt.nokia.com/browse/QTBUG-17581

In the comments from Kevin Barreau, there is a patch for Phonon. I did not try it, because I do wanted it to work on a vanilla Phonon Version. I thought that maybe the patch would be imported to the official branch, but I think Phonon - Qt development has stopped so far.

I started now trying to wrap my own QIODevice over the NetworkReply, and in the atEnd() method I return reply.atEnd() && reply.isFinished(). That should do the thing also I thought, but I could not get this work either.

I will inform you if I have some updates and maybe you could do that too :D

So I am sorry, but I could not get this work in a way I liked ;)