1

我在 ubuntu 12.04 和 live555 的最后一个版本上使用 QT5.02。

这是我的类(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://admin@192.168.1.99: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 的源存档中。我做错了什么?有没有使用 live555 的 QT 应用程序示例?

4

1 回答 1

0

您需要将回调放在 RTSPClient 类中或更改 sendSetupCommand 和 sendPlayCommand(可能还有其他)的函数签名以获取 CameraStream 类中的函数指针。

于 2013-07-12T13:02:35.690 回答