Я использую QT5.02 на Ubuntu 12.04 и последний выпуск Live555.
Это мой класс (CameraStream.cpp), который будет читать поток rtsp:
#include "CameraStream.h"#include <DummySink.h>
#include <ourRTSPClient.h>
#include "liveMedia.hh"#include "BasicUsageEnvironment.hh"
CameraStream::CameraStream(){
eventLoopWatchVariable = 0;
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
openURL(*env, "argv[0]", "rtsp://[email protected]:1234/file.sdp");
// All subsequent activity takes place within the event loop:
env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
}
#define RTSP_CLIENT_VERBOSITY_LEVEL 1
static unsigned rtspClientCount = 0;
void CameraStream::openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) {
RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
if (rtspClient == NULL) {
env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n";
return;
}
++rtspClientCount;
rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}// Implementation of the RTSP 'response handlers':
void CameraStream::continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n";
delete[] resultString;
break;
}
char* const sdpDescription = resultString;
env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n";
// Create a media session object from this SDP description:
scs.session = MediaSession::createNew(env, sdpDescription);
delete[] sdpDescription; // because we don't need it anymore
if (scs.session == NULL) {
env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
break;
} else if (!scs.session->hasSubsessions()) {
env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
break;
}
scs.iter = new MediaSubsessionIterator(*scs.session);
setupNextSubsession(rtspClient);
return;
} while (0);
// An unrecoverable error occurred with this stream.
shutdownStream(rtspClient);
}
// By default, we request that the server stream its data using RTP/UDP.
// If, instead, you want to request that the server stream via RTP-over-TCP, change the following to True:
#define REQUEST_STREAMING_OVER_TCP False
void CameraStream::setupNextSubsession(RTSPClient* rtspClient) {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
scs.subsession = scs.iter->next();
if (scs.subsession != NULL) {
if (!scs.subsession->initiate()) {
env << *rtspClient << "Failed to initiate the \"" << *scs.subsession << "\" subsession: " << env.getResultMsg() << "\n";
setupNextSubsession(rtspClient); // give up on this subsession; go to the next one
} else {
env << *rtspClient << "Initiated the \"" << *scs.subsession
<< "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
// Continue setting up this subsession, by sending a RTSP "SETUP" command:
rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
}
return;
}
// We've finished setting up all of the subsessions. Now, send a RTSP "PLAY" command to start the streaming:
if (scs.session->absStartTime() != NULL) {
// Special case: The stream is indexed by 'absolute' time, so send an appropriate "PLAY" command:
rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY, scs.session->absStartTime(), scs.session->absEndTime());
} else {
scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
}
}
void CameraStream::continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to set up the \"" << *scs.subsession << "\" subsession: " << resultString << "\n";
break;
}
env << *rtspClient << "Set up the \"" << *scs.subsession
<< "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
// Having successfully setup the subsession, create a data sink for it, and call "startPlaying()" on it.
// (This will prepare the data sink to receive data; the actual flow of data from the client won't start happening until later,
// after we've sent a RTSP "PLAY" command.)
scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());
// perhaps use your own custom "MediaSink" subclass instead
if (scs.subsession->sink == NULL) {
env << *rtspClient << "Failed to create a data sink for the \"" << *scs.subsession
<< "\" subsession: " << env.getResultMsg() << "\n";
break;
}
env << *rtspClient << "Created a data sink for the \"" << *scs.subsession << "\" subsession\n";
scs.subsession->miscPtr = rtspClient; // a hack to let subsession handle functions get the "RTSPClient" from the subsession
scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
subsessionAfterPlaying, scs.subsession);
// Also set a handler to be called if a RTCP "BYE" arrives for this subsession:
if (scs.subsession->rtcpInstance() != NULL) {
scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession);
}
} while (0);
delete[] resultString;
// Set up the next subsession, if any:
setupNextSubsession(rtspClient);
}
void CameraStream::continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) {
Boolean success = False;
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to start playing session: " << resultString << "\n";
break;
}
// Set a timer to be handled at the end of the stream's expected duration (if the stream does not already signal its end
// using a RTCP "BYE"). This is optional. If, instead, you want to keep the stream active - e.g., so you can later
// 'seek' back within it and do another RTSP "PLAY" - then you can omit this code.
// (Alternatively, if you don't want to receive the entire stream, you could set this timer for some shorter value.)
if (scs.duration > 0) {
unsigned const delaySlop = 2; // number of seconds extra to delay, after the stream's expected duration. (This is optional.)
scs.duration += delaySlop;
unsigned uSecsToDelay = (unsigned)(scs.duration*1000000);
scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient);
}
env << *rtspClient << "Started playing session";
if (scs.duration > 0) {
env << " (for up to " << scs.duration << " seconds)";
}
env << "...\n";
success = True;
} while (0);
delete[] resultString;
if (!success) {
// An unrecoverable error occurred with this stream.
shutdownStream(rtspClient);
}
}// Implementation of the other event handlers:
void CameraStream::subsessionAfterPlaying(void* clientData) {
MediaSubsession* subsession = (MediaSubsession*)clientData;
RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
// Begin by closing this subsession's stream:
Medium::close(subsession->sink);
subsession->sink = NULL;
// Next, check whether *all* subsessions' streams have now been closed:
MediaSession& session = subsession->parentSession();
MediaSubsessionIterator iter(session);
while ((subsession = iter.next()) != NULL) {
if (subsession->sink != NULL) return; // this subsession is still active
}
// All subsessions' streams have now been closed, so shutdown the client:
shutdownStream(rtspClient);
}
void CameraStream::subsessionByeHandler(void* clientData) {
MediaSubsession* subsession = (MediaSubsession*)clientData;
RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
UsageEnvironment& env = rtspClient->envir(); // alias
env << *rtspClient << "Received RTCP \"BYE\" on \"" << *subsession << "\" subsession\n";
// Now act as if the subsession had closed:
subsessionAfterPlaying(subsession);
}
void CameraStream::streamTimerHandler(void* clientData) {
ourRTSPClient* rtspClient = (ourRTSPClient*)clientData;
StreamClientState& scs = rtspClient->scs; // alias
scs.streamTimerTask = NULL;
// Shut down the stream:
shutdownStream(rtspClient);
}
void CameraStream::shutdownStream(RTSPClient* rtspClient, int exitCode) {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
// First, check whether any subsessions have still to be closed:
if (scs.session != NULL) {
Boolean someSubsessionsWereActive = False;
MediaSubsessionIterator iter(*scs.session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
if (subsession->sink != NULL) {
Medium::close(subsession->sink);
subsession->sink = NULL;
if (subsession->rtcpInstance() != NULL) {
subsession->rtcpInstance()->setByeHandler(NULL, NULL); // in case the server sends a RTCP "BYE" while handling "TEARDOWN"}
someSubsessionsWereActive = True;
}
}
if (someSubsessionsWereActive) {
// Send a RTSP "TEARDOWN" command, to tell the server to shutdown the stream.
// Don't bother handling the response to the "TEARDOWN".
rtspClient->sendTeardownCommand(*scs.session, NULL);
}
}
env << *rtspClient << "Closing the stream.\n";
Medium::close(rtspClient);
// Note that this will also cause this stream's "StreamClientState" structure to get reclaimed.
if (--rtspClientCount == 0) {
// The final stream has ended, so exit the application now.
// (Of course, if you're embedding this code into your own application, you might want to comment this out,
// and replace it with "eventLoopWatchVariable = 1;", so that we leave the LIVE555 event loop, and continue running "main()".)
exit(exitCode);
}
}
UsageEnvironment& operator<<(UsageEnvironment& env, const RTSPClient& rtspClient) {
return env << "[URL:\"" << rtspClient.url() << "\"]: ";
}
UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession) {
return env << subsession.mediumName() << "/" << subsession.codecName();
}
И это заголовок файла (CameraStream.h):
#ifndef CAMERASTREAM_H
#define CAMERASTREAM_H
#include <liveMedia.hh>class CameraStream
{
public:
CameraStream();
private:
// Forward function definitions:
// RTSP 'response handlers':
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString);
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString);
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString);
// Other event handler functions:
void subsessionAfterPlaying(void* clientData); // called when a stream's subsession (e.g., audio or video substream) ends
void subsessionByeHandler(void* clientData); // called when a RTCP "BYE" is received for a subsession
void streamTimerHandler(void* clientData);
// called at the end of a stream's expected duration (if the stream has not already signaled its end using a RTCP "BYE")
// The main streaming routine (for each "rtsp://" URL):
void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL);
// Used to iterate through each stream's 'subsessions', setting up each one:
void setupNextSubsession(RTSPClient* rtspClient);
// Used to shut down and close a stream (including its "RTSPClient" object):
void shutdownStream(RTSPClient* rtspClient, int exitCode = 1);
char eventLoopWatchVariable;
};
// A function that outputs a string that identifies each stream (for debugging output). Modify this if you wish:
UsageEnvironment& operator<<(UsageEnvironment& env, const RTSPClient& rtspClient) ;
// A function that outputs a string that identifies each subsession (for debugging output). Modify this if you wish:
UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession);#endif // CAMERASTREAM_H
когда я пытаюсь скомпилировать, я получаю следующую ошибку:
../CameraProva/CameraStream.cpp: In member function 'void CameraStream::setupNextSubsession(RTSPClient*)':
../CameraProva/CameraStream.cpp:110:112: error: no matching function for call to 'RTSPClient::sendSetupCommand(MediaSubsession&, <unresolved overloaded function type>, const Boolean&, const Boolean&)'
../CameraProva/CameraStream.cpp:110:112: note: candidate is:
../include/RTSPClient.hh:85:12: note: unsigned int RTSPClient::sendSetupCommand(MediaSubsession&, void (*)(RTSPClient*, int, char*), Boolean, Boolean, Boolean, Authenticator*)
../include/RTSPClient.hh:85:12: note: no known conversion for argument 2 from '<unresolved overloaded function type>' to 'void (*)(RTSPClient*, int, char*)'
../CameraProva/CameraStream.cpp:118:124: error: no matching function for call to 'RTSPClient::sendPlayCommand(MediaSession&, <unresolved overloaded function type>, char*, char*)'
../CameraProva/CameraStream.cpp:118:124: note: candidates are:
../include/RTSPClient.hh:93:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSession&, void (*)(RTSPClient*, int, char*), double, double, float, Authenticator*)
../include/RTSPClient.hh:93:12: note: no known conversion for argument 2 from '<unresolved overloaded function type>' to 'void (*)(RTSPClient*, int, char*)'
../include/RTSPClient.hh:99:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSubsession&, void (*)(RTSPClient*, int, char*), double, double, float, Authenticator*)
../include/RTSPClient.hh:99:12: note: no known conversion for argument 1 from 'MediaSession' to 'MediaSubsession&'
../include/RTSPClient.hh:109:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSession&, void (*)(RTSPClient*, int, char*), const char*, const char*, float, Authenticator*)
../include/RTSPClient.hh:109:12: note: no known conversion for argument 2 from '<unresolved overloaded function type>' to 'void (*)(RTSPClient*, int, char*)'
../include/RTSPClient.hh:112:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSubsession&, void (*)(RTSPClient*, int, char*), const char*, const char*, float, Authenticator*)
../include/RTSPClient.hh:112:12: note: no known conversion for argument 1 from 'MediaSession' to 'MediaSubsession&'
../CameraProva/CameraStream.cpp:121:68: error: no matching function for call to 'RTSPClient::sendPlayCommand(MediaSession&, <unresolved overloaded function type>)'
../CameraProva/CameraStream.cpp:121:68: note: candidates are:
../include/RTSPClient.hh:93:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSession&, void (*)(RTSPClient*, int, char*), double, double, float, Authenticator*)
../include/RTSPClient.hh:93:12: note: no known conversion for argument 2 from '<unresolved overloaded function type>' to 'void (*)(RTSPClient*, int, char*)'
../include/RTSPClient.hh:99:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSubsession&, void (*)(RTSPClient*, int, char*), double, double, float, Authenticator*)
../include/RTSPClient.hh:99:12: note: no known conversion for argument 1 from 'MediaSession' to 'MediaSubsession&'
../include/RTSPClient.hh:109:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSession&, void (*)(RTSPClient*, int, char*), const char*, const char*, float, Authenticator*)
../include/RTSPClient.hh:109:12: note: candidate expects 6 arguments, 2 provided
../include/RTSPClient.hh:112:12: note: unsigned int RTSPClient::sendPlayCommand(MediaSubsession&, void (*)(RTSPClient*, int, char*), const char*, const char*, float, Authenticator*)
../include/RTSPClient.hh:112:12: note: candidate expects 6 arguments, 2 provided
../CameraProva/CameraStream.cpp: In member function 'void CameraStream::continueAfterSETUP(RTSPClient*, int, char*)':
../CameraProva/CameraStream.cpp:153:82: error: no matching function for call to 'MediaSink::startPlaying(FramedSource&, <unresolved overloaded function type>, MediaSubsession*&)'
../CameraProva/CameraStream.cpp:153:82: note: candidate is:
../include/MediaSink.hh:34:11: note: Boolean MediaSink::startPlaying(MediaSource&, void (*)(void*), void*)
../include/MediaSink.hh:34:11: note: no known conversion for argument 2 from '<unresolved overloaded function type>' to 'void (*)(void*)'
../CameraProva/CameraStream.cpp:156:95: error: no matching function for call to 'RTCPInstance::setByeHandler(<unresolved overloaded function type>, MediaSubsession*&)'
../CameraProva/CameraStream.cpp:156:95: note: candidate is:
../include/RTCP.hh:59:8: note: void RTCPInstance::setByeHandler(void (*)(void*), void*, Boolean)
../include/RTCP.hh:59:8: note: no known conversion for argument 1 from '<unresolved overloaded function type>' to 'void (*)(void*)'
../CameraProva/CameraStream.cpp: In member function 'void CameraStream::continueAfterPLAY(RTSPClient*, int, char*)':
../CameraProva/CameraStream.cpp:185:100: error: invalid use of member (did you forget the '&' ?)
make: *** [CameraStream.o] Error 1
Я копирую код из примера testRTSPClient в исходный архив live555. что я делаю не так? Есть какие-нибудь примеры приложений QT, использующих live555?
Вам нужно поместить обратные вызовы в класс RTSPClient или изменить сигнатуру функции sendSetupCommand и sendPlayCommand (и, возможно, других), чтобы получить указатель на функцию, находящуюся в классе CameraStream.
Других решений пока нет …