1、DynamicRTSPServer.cpp
- ServerMediaSession* DynamicRTSPServer
- ::lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession) {
- // First, check whether the specified "streamName" exists as a local file:
- // Next, check whether we already have a "ServerMediaSession" for this file:
- ServerMediaSession* sms = RTSPServer::lookupServerMediaSession(streamName);
- Boolean smsExists = sms != NULL;
- FILE* fid = fopen(streamName, "rb");
- Boolean fileExists = fid != NULL;
- if(strcmp(streamName,"live") == 0)
- {
- if (smsExists) {
- // "sms" was created for a file that no longer exists. Remove it:
- removeServerMediaSession(sms);
- sms = NULL;
- }
- if (sms == NULL) {
- sms = createNewSMS(envir(), streamName, fid);
- addServerMediaSession(sms);
- }
- return sms;
- }
- // Handle the four possibilities for "fileExists" and "smsExists":
- if (!fileExists) {
- if (smsExists) {
- // "sms" was created for a file that no longer exists. Remove it:
- removeServerMediaSession(sms);
- sms = NULL;
- }
- return NULL;
- } else {
- if (smsExists && isFirstLookupInSession) {
- // Remove the existing "ServerMediaSession" and create a new one, in case the underlying
- // file has changed in some way:
- removeServerMediaSession(sms);
- sms = NULL;
- }
- if (sms == NULL) {
- sms = createNewSMS(envir(), streamName, fid);
- addServerMediaSession(sms);
- }
- fclose(fid);
- return sms;
- }
- }
- static ServerMediaSession* createNewSMS(UsageEnvironment& env,
- char const* fileName, FILE* /*fid*/) {
- ServerMediaSession* sms = NULL;
- Boolean const reuseSource = False;
- if (strcmp(fileName, "live") == 0) {
- NEW_SMS("live");
- //env << "live detected. addsubsession:\n";
- //sms->addSubsession(LiveADTSAudioServerMediaSubsession::createNew(env, fileName, reuseSource));
- OutPacketBuffer::maxSize = 300000;
- sms->addSubsession(LiveVideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- return sms;
- }
- // Use the file name extension to determine the type of "ServerMediaSession":
- char const* extension = strrchr(fileName, '.');
- if (extension == NULL) return NULL;
- if (strcmp(extension, ".aac") == 0) {
- // Assumed to be an AAC Audio (ADTS format) file:
- NEW_SMS("AAC Audio");
- sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".amr") == 0) {
- // Assumed to be an AMR Audio file:
- NEW_SMS("AMR Audio");
- sms->addSubsession(AMRAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".ac3") == 0) {
- // Assumed to be an AC-3 Audio file:
- NEW_SMS("AC-3 Audio");
- sms->addSubsession(AC3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".m4e") == 0) {
- // Assumed to be a MPEG-4 Video Elementary Stream file:
- NEW_SMS("MPEG-4 Video");
- sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".264") == 0) {
- // Assumed to be a H.264 Video Elementary Stream file:
- NEW_SMS("H.264 Video");
- OutPacketBuffer::maxSize = 300000; // allow for some possibly large H.264 frames
- sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".265") == 0) {
- // Assumed to be a H.265 Video Elementary Stream file:
- NEW_SMS("H.265 Video");
- OutPacketBuffer::maxSize = 300000; // allow for some possibly large H.265 frames
- sms->addSubsession(H265VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".mp3") == 0) {
- // Assumed to be a MPEG-1 or 2 Audio file:
- NEW_SMS("MPEG-1 or 2 Audio");
- // To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
- //#define STREAM_USING_ADUS 1
- // To also reorder ADUs before streaming, uncomment the following:
- //#define INTERLEAVE_ADUS 1
- // (For more information about ADUs and interleaving,
- // see <http://www.live555.com/rtp-mp3/>)
- Boolean useADUs = False;
- Interleaving* interleaving = NULL;
- #ifdef STREAM_USING_ADUS
- useADUs = True;
- #ifdef INTERLEAVE_ADUS
- unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
- unsigned const interleaveCycleSize
- = (sizeof interleaveCycle)/(sizeof (unsigned char));
- interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);
- #endif
- #endif
- sms->addSubsession(MP3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, useADUs, interleaving));
- } else if (strcmp(extension, ".mpg") == 0) {
- // Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:
- NEW_SMS("MPEG-1 or 2 Program Stream");
- MPEG1or2FileServerDemux* demux
- = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);
- sms->addSubsession(demux->newVideoServerMediaSubsession());
- sms->addSubsession(demux->newAudioServerMediaSubsession());
- } else if (strcmp(extension, ".vob") == 0) {
- // Assumed to be a VOB (MPEG-2 Program Stream, with AC-3 audio) file:
- NEW_SMS("VOB (MPEG-2 video with AC-3 audio)");
- MPEG1or2FileServerDemux* demux
- = MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);
- sms->addSubsession(demux->newVideoServerMediaSubsession());
- sms->addSubsession(demux->newAC3AudioServerMediaSubsession());
- } else if (strcmp(extension, ".ts") == 0) {
- // Assumed to be a MPEG Transport Stream file:
- // Use an index file name that's the same as the TS file name, except with ".tsx":
- unsigned indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"
- char* indexFileName = new char[indexFileNameLen];
- sprintf(indexFileName, "%sx", fileName);
- NEW_SMS("MPEG Transport Stream");
- sms->addSubsession(MPEG2TransportFileServerMediaSubsession::createNew(env, fileName, indexFileName, reuseSource));
- delete[] indexFileName;
- } else if (strcmp(extension, ".wav") == 0) {
- // Assumed to be a WAV Audio file:
- NEW_SMS("WAV Audio Stream");
- // To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
- // change the following to True:
- Boolean convertToULaw = False;
- sms->addSubsession(WAVAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, convertToULaw));
- } else if (strcmp(extension, ".dv") == 0) {
- // Assumed to be a DV Video file
- // First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
- OutPacketBuffer::maxSize = 300000;
- NEW_SMS("DV Video");
- sms->addSubsession(DVVideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
- } else if (strcmp(extension, ".mkv") == 0 || strcmp(extension, ".webm") == 0) {
- // Assumed to be a Matroska file (note that WebM ('.webm') files are also Matroska files)
- OutPacketBuffer::maxSize = 100000; // allow for some possibly large VP8 or VP9 frames
- NEW_SMS("Matroska video+audio+(optional)subtitles");
- // Create a Matroska file server demultiplexor for the specified file.
- // (We enter the event loop to wait for this to complete.)
- MatroskaDemuxCreationState creationState;
- creationState.watchVariable = 0;
- MatroskaFileServerDemux::createNew(env, fileName, onMatroskaDemuxCreation, &creationState);
- env.taskScheduler().doEventLoop(&creationState.watchVariable);
- ServerMediaSubsession* smss;
- while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
- sms->addSubsession(smss);
- }
- } else if (strcmp(extension, ".ogg") == 0 || strcmp(extension, ".ogv") == 0 || strcmp(extension, ".opus") == 0) {
- // Assumed to be an Ogg file
- NEW_SMS("Ogg video and/or audio");
- // Create a Ogg file server demultiplexor for the specified file.
- // (We enter the event loop to wait for this to complete.)
- OggDemuxCreationState creationState;
- creationState.watchVariable = 0;
- OggFileServerDemux::createNew(env, fileName, onOggDemuxCreation, &creationState);
- env.taskScheduler().doEventLoop(&creationState.watchVariable);
- ServerMediaSubsession* smss;
- while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
- sms->addSubsession(smss);
- }
- }
- return sms;
- }
2、添加LiveADTSAudioServerMediaSubsession
- #ifndef _H264_STREAM_FILE_SOURCE_HH
- #define _H264_STREAM_FILE_SOURCE_HH
- #ifndef _FRAMED_FILE_SOURCE_HH
- #include "FramedFileSource.hh"
- #endif
- #include<pthread.h>
- #include "semaphore.h"
- #define H264_BUF_SIZE 150000
- #define H264_BUF_COUNT 10
- typedef void (*CB_FUN)(void);
- extern void h264_buf_init();
- extern void h264_buf_destroy();
- extern Boolean h264_buf_full();
- extern Boolean h264_buf_empty();
- extern int h264_buf_put(unsigned char* buf,int len);
- extern unsigned char* h264_buf_get(int* len);
- extern sem_t h264_f;
- extern sem_t h264_e;
- extern int b264IsInit;
- extern CB_FUN startfun;
- extern CB_FUN endfun;
- class LiveH264StreamSource: public FramedSource {
- public:
- static LiveH264StreamSource* createNew(UsageEnvironment& env,
- char const* fileName,
- unsigned preferredFrameSize = 0,
- unsigned playTimePerFrame = 0);
- // "preferredFrameSize" == 0 means 'no preference'
- // "playTimePerFrame" is in microseconds
- /*
- static LiveH264StreamSource* createNew(UsageEnvironment& env,
- unsigned preferredFrameSize = 0,
- unsigned playTimePerFrame = 0);
- // an alternative version of "createNew()" that's used if you already have
- // an open file.
- */
- u_int64_t fileSize() const { return fFileSize; }
- // 0 means zero-length, unbounded, or unknown
- void seekToByteAbsolute(u_int64_t byteNumber, u_int64_t numBytesToStream = 0);
- // if "numBytesToStream" is >0, then we limit the stream to that number of bytes, before treating it as EOF
- void seekToByteRelative(int64_t offset, u_int64_t numBytesToStream = 0);
- void seekToEnd(); // to force EOF handling on the next read
- protected:
- LiveH264StreamSource(UsageEnvironment& env,
- unsigned preferredFrameSize,
- unsigned playTimePerFrame);
- // called only by createNew()
- virtual ~LiveH264StreamSource();
- static void fileReadableHandler(LiveH264StreamSource* source, int mask);
- void doReadFromFile();
- private:
- // redefined virtual functions:
- virtual void doGetNextFrame();
- virtual void doStopGettingFrames();
- protected:
- u_int64_t fFileSize;
- private:
- unsigned fPreferredFrameSize;
- unsigned fPlayTimePerFrame;
- Boolean fFidIsSeekable;
- unsigned fLastPlayTime;
- Boolean fHaveStartedReading;
- Boolean fLimitNumBytesToStream;
- u_int64_t fNumBytesToStream; // used iff "fLimitNumBytesToStream" is True
- };
- #endif
- #include "LiveADTSAudioServerMediaSubsession.hh"
- #include "LiveADTSAudioSource.hh"
- #include "MPEG4GenericRTPSink.hh"
- LiveADTSAudioServerMediaSubsession*
- LiveADTSAudioServerMediaSubsession::createNew(UsageEnvironment& env,
- char const* fileName,
- Boolean reuseFirstSource) {
- return new LiveADTSAudioServerMediaSubsession(env, fileName, reuseFirstSource);
- }
- LiveADTSAudioServerMediaSubsession
- ::LiveADTSAudioServerMediaSubsession(UsageEnvironment& env,
- char const* fileName, Boolean reuseFirstSource)
- : FileServerMediaSubsession(env, fileName, reuseFirstSource) {
- }
- LiveADTSAudioServerMediaSubsession
- ::~LiveADTSAudioServerMediaSubsession() {
- }
- FramedSource* LiveADTSAudioServerMediaSubsession
- ::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {
- estBitrate = 96; // kbps, estimate
- return LiveADTSAudioSource::createNew(envir());
- }
- RTPSink* LiveADTSAudioServerMediaSubsession
- ::createNewRTPSink(Groupsock* rtpGroupsock,
- unsigned char rtpPayloadTypeIfDynamic,
- FramedSource* inputSource) {
- LiveADTSAudioSource* adtsSource = (LiveADTSAudioSource*)inputSource;
- return MPEG4GenericRTPSink::createNew(envir(), rtpGroupsock,
- rtpPayloadTypeIfDynamic,
- adtsSource->samplingFrequency(),
- "audio", "AAC-hbr", adtsSource->configStr(),
- adtsSource->numChannels());
- }
3、添加LiveADTSAudioSource类
- #ifndef _LiveADTSAudioSource_HH
- #define _LiveADTSAudioSource_HH
- #ifndef _FRAMED_FILE_SOURCE_HH
- #include "FramedFileSource.hh"
- #endif
- #include<pthread.h>
- #include "semaphore.h"
- #define AAC_BUF_SIZE 10000
- #define AAC_BUF_COUNT 20
- extern void aac_buf_init();
- extern Boolean aac_buf_full();
- extern Boolean aac_buf_empty();
- extern int aac_buf_put(unsigned char* buf,int len);
- extern unsigned char* aac_buf_get();
- extern void aac_buf_destroy();
- extern sem_t aac_f;
- extern sem_t aac_e;
- extern int bIsInit;
- class LiveADTSAudioSource: public FramedSource {
- public:
- static LiveADTSAudioSource* createNew(UsageEnvironment& env);
- unsigned samplingFrequency() const { return fSamplingFrequency; }
- unsigned numChannels() const { return fNumChannels; }
- char const* configStr() const { return fConfigStr; }
- // returns the 'AudioSpecificConfig' for this stream (in ASCII form)
- private:
- LiveADTSAudioSource(UsageEnvironment& env, u_int8_t profile,
- u_int8_t samplingFrequencyIndex, u_int8_t channelConfiguration);
- // called only by createNew()
- virtual ~LiveADTSAudioSource();
- private:
- // redefined virtual functions:
- virtual void doGetNextFrame();
- private:
- unsigned fSamplingFrequency;
- unsigned fNumChannels;
- unsigned fuSecsPerFrame;
- char fConfigStr[5];
- };
- #endif
- #include "LiveADTSAudioSource.hh"
- #include "InputFile.hh"
- #include <GroupsockHelper.hh>
- // ADTSAudioFileSource //
- static unsigned const samplingFrequencyTable[16] = {
- 96000, 88200, 64000, 48000,
- 44100, 32000, 24000, 22050,
- 16000, 12000, 11025, 8000,
- 7350, 0, 0, 0
- };
- unsigned char aac_framebuf[AAC_BUF_COUNT][AAC_BUF_SIZE];
- int aac_frame_len[AAC_BUF_COUNT];
- int aac_buf_head;
- int aac_buf_tail;
- int aac_buf_size;
- void aac_buf_init();
- void aac_buf_destroy();
- Boolean aac_buf_full();
- Boolean aac_buf_empty();
- int aac_buf_put(unsigned char* buf,int len);
- unsigned char* aac_buf_get();
- sem_t aac_f;
- sem_t aac_e;
- sem_t aac_m;
- int bIsInit = 0;
- void aac_buf_init()
- {
- if(bIsInit == 0)
- {
- sem_init(&aac_f,0,0);
- sem_init(&aac_e,0,AAC_BUF_COUNT);
- sem_init(&aac_m,0,1);
- aac_buf_head = 0;
- aac_buf_tail = 0;
- aac_buf_size = 0;
- }
- }
- Boolean aac_buf_full()
- {
- if(aac_buf_size == AAC_BUF_COUNT)
- return True;
- return False;
- }
- Boolean aac_buf_empty()
- {
- if(aac_buf_size == 0)
- return True;
- return False;
- }
- int aac_buf_put(unsigned char* buf,int len)
- {
- sem_wait(&aac_e);
- sem_wait(&aac_m);
- bzero(aac_framebuf[aac_buf_tail],AAC_BUF_SIZE);
- memcpy(aac_framebuf[aac_buf_tail],buf,len);
- aac_frame_len[aac_buf_tail] = len;
- aac_buf_tail = (aac_buf_tail + 1)%AAC_BUF_COUNT;
- aac_buf_size++;
- sem_post(&aac_m);
- sem_post(&aac_f);
- }
- unsigned char* aac_buf_get()
- {
- sem_wait(&aac_m);
- unsigned char* rt = aac_framebuf[aac_buf_head];
- aac_buf_head = (aac_buf_head+1)%AAC_BUF_COUNT;
- aac_buf_size--;
- sem_post(&aac_m);
- return rt;
- }
- void aac_buf_destroy()
- {
- sem_destroy(&aac_f);
- sem_destroy(&aac_e);
- sem_destroy(&aac_m);
- }
- LiveADTSAudioSource*
- LiveADTSAudioSource::createNew(UsageEnvironment& env) {
- aac_buf_init();
- bIsInit = 1;
- return new LiveADTSAudioSource(env, 1, 4, 2);
- }
- LiveADTSAudioSource
- ::LiveADTSAudioSource(UsageEnvironment& env, u_int8_t profile,
- u_int8_t samplingFrequencyIndex, u_int8_t channelConfiguration)
- : FramedSource(env) {
- fSamplingFrequency = samplingFrequencyTable[samplingFrequencyIndex];
- fNumChannels = channelConfiguration == 0 ? 2 : channelConfiguration;
- fuSecsPerFrame
- = (1024/*samples-per-frame*/*1000000) / fSamplingFrequency/*samples-per-second*/;
- // Construct the 'AudioSpecificConfig', and from it, the corresponding ASCII string:
- unsigned char audioSpecificConfig[2];
- u_int8_t const audioObjectType = profile + 1;
- audioSpecificConfig[0] = (audioObjectType<<3) | (samplingFrequencyIndex>>1);
- audioSpecificConfig[1] = (samplingFrequencyIndex<<7) | (channelConfiguration<<3);
- sprintf(fConfigStr, "%02X%02x", audioSpecificConfig[0], audioSpecificConfig[1]);
- //env << "liveADTSAudioSource : construct\n";
- }
- LiveADTSAudioSource::~LiveADTSAudioSource() {
- }
- // Note: We should change the following to use asynchronous file reading, #####
- // as we now do with ByteStreamFileSource. #####
- void LiveADTSAudioSource::doGetNextFrame() {
- // Begin by reading the 7-byte fixed_variable headers:
- sem_wait(&aac_f);
- unsigned char* cur = aac_buf_get();
- int pos = 0;
- unsigned char* headers;
- headers = cur;
- // Extract important fields from the headers:
- Boolean protection_absent = headers[1]&0x01;
- u_int16_t frame_length
- = ((headers[3]&0x03)<<11) | (headers[4]<<3) | ((headers[5]&0xE0)>>5);
- if(0)
- {
- u_int16_t syncword = (headers[0]<<4) | (headers[1]>>4);
- fprintf(stderr, "Read frame: syncword 0x%x, protection_absent %d, frame_length %d\n", syncword, protection_absent, frame_length);
- if (syncword != 0xFFF) fprintf(stderr, "WARNING: Bad syncword!\n");
- }
- unsigned numBytesToRead
- = frame_length > 7 ? frame_length - 7 : 0;
- pos = 7;
- // If there's a 'crc_check' field, skip it:
- if (!protection_absent) {
- pos += 2;
- numBytesToRead = numBytesToRead > 2 ? numBytesToRead - 2 : 0;
- }
- // Next, read the raw frame data into the buffer provided:
- if (numBytesToRead > fMaxSize) {
- fNumTruncatedBytes = numBytesToRead - fMaxSize;
- numBytesToRead = fMaxSize;
- }
- memcpy(fTo,cur + pos,numBytesToRead);
- sem_post(&aac_e);
- int numBytesRead = numBytesToRead;
- fFrameSize = numBytesRead;
- fNumTruncatedBytes += numBytesToRead - numBytesRead;
- // Set the 'presentation time':
- if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
- // This is the first frame, so use the current time:
- gettimeofday(&fPresentationTime, NULL);
- } else {
- // Increment by the play time of the previous frame:
- unsigned uSeconds = fPresentationTime.tv_usec + fuSecsPerFrame;
- fPresentationTime.tv_sec += uSeconds/1000000;
- fPresentationTime.tv_usec = uSeconds%1000000;
- }
- fDurationInMicroseconds = fuSecsPerFrame;
- // Switch to another task, and inform the reader that he has data:
- nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
- (TaskFunc*)FramedSource::afterGetting, this);
- }
4、添加LiveVideoFileServerMediaSubsession类
- #ifndef _LIVE_VIDEO_FILE_SERVER_MEDIA_SUBSESSION_HH
- #define _LIVE_VIDEO_FILE_SERVER_MEDIA_SUBSESSION_HH
- #ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
- #include "FileServerMediaSubsession.hh"
- #endif
- class LiveVideoFileServerMediaSubsession: public FileServerMediaSubsession {
- public:
- static LiveVideoFileServerMediaSubsession*
- createNew(UsageEnvironment& env, char const* fileName, Boolean reuseFirstSource);
- // Used to implement "getAuxSDPLine()":
- void checkForAuxSDPLine1();
- void afterPlayingDummy1();
- protected:
- LiveVideoFileServerMediaSubsession(UsageEnvironment& env,
- char const* fileName, Boolean reuseFirstSource);
- // called only by createNew();
- virtual ~LiveVideoFileServerMediaSubsession();
- void setDoneFlag() { fDoneFlag = ~0; }
- protected: // redefined virtual functions
- virtual char const* getAuxSDPLine(RTPSink* rtpSink,
- FramedSource* inputSource);
- virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
- unsigned& estBitrate);
- virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
- unsigned char rtpPayloadTypeIfDynamic,
- FramedSource* inputSource);
- private:
- char* fAuxSDPLine;
- char fDoneFlag; // used when setting up "fAuxSDPLine"
- RTPSink* fDummyRTPSink; // ditto
- };
- #endif
- #include "LiveVideoFileServerMediaSubsession.hh"
- #include "H264VideoRTPSink.hh"
- #include "LiveH264StreamSource.hh"
- #include "H264VideoStreamFramer.hh"
- LiveVideoFileServerMediaSubsession*
- LiveVideoFileServerMediaSubsession::createNew(UsageEnvironment& env,
- char const* fileName,
- Boolean reuseFirstSource) {
- return new LiveVideoFileServerMediaSubsession(env, fileName, reuseFirstSource);
- }
- LiveVideoFileServerMediaSubsession::LiveVideoFileServerMediaSubsession(UsageEnvironment& env,
- char const* fileName, Boolean reuseFirstSource)
- : FileServerMediaSubsession(env, fileName, reuseFirstSource),
- fAuxSDPLine(NULL), fDoneFlag(0), fDummyRTPSink(NULL) {
- }
- LiveVideoFileServerMediaSubsession::~LiveVideoFileServerMediaSubsession() {
- delete[] fAuxSDPLine;
- }
- static void afterPlayingDummy(void* clientData) {
- LiveVideoFileServerMediaSubsession* subsess = (LiveVideoFileServerMediaSubsession*)clientData;
- subsess->afterPlayingDummy1();
- }
- void LiveVideoFileServerMediaSubsession::afterPlayingDummy1() {
- // Unschedule any pending 'checking' task:
- envir().taskScheduler().unscheduleDelayedTask(nextTask());
- // Signal the event loop that we're done:
- setDoneFlag();
- }
- static void checkForAuxSDPLine(void* clientData) {
- LiveVideoFileServerMediaSubsession* subsess = (LiveVideoFileServerMediaSubsession*)clientData;
- subsess->checkForAuxSDPLine1();
- }
- void LiveVideoFileServerMediaSubsession::checkForAuxSDPLine1() {
- char const* dasl;
- if (fAuxSDPLine != NULL) {
- // Signal the event loop that we're done:
- setDoneFlag();
- } else if (fDummyRTPSink != NULL && (dasl = fDummyRTPSink->auxSDPLine()) != NULL) {
- fAuxSDPLine = strDup(dasl);
- fDummyRTPSink = NULL;
- // Signal the event loop that we're done:
- setDoneFlag();
- } else if (!fDoneFlag) {
- // try again after a brief delay:
- int uSecsToDelay = 100000; // 100 ms
- nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,
- (TaskFunc*)checkForAuxSDPLine, this);
- }
- }
- char const* LiveVideoFileServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource) {
- if (fAuxSDPLine != NULL) return fAuxSDPLine; // it's already been set up (for a previous client)
- if (fDummyRTPSink == NULL) { // we're not already setting it up for another, concurrent stream
- // Note: For H264 video files, the 'config' information ("profile-level-id" and "sprop-parameter-sets") isn't known
- // until we start reading the file. This means that "rtpSink"s "auxSDPLine()" will be NULL initially,
- // and we need to start reading data from our file until this changes.
- fDummyRTPSink = rtpSink;
- // Start reading the file:
- fDummyRTPSink->startPlaying(*inputSource, afterPlayingDummy, this);
- // Check whether the sink's 'auxSDPLine()' is ready:
- checkForAuxSDPLine(this);
- }
- envir().taskScheduler().doEventLoop(&fDoneFlag);
- return fAuxSDPLine;
- }
- FramedSource* LiveVideoFileServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {
- estBitrate = 500; // kbps, estimate
- // Create the video source:
- LiveH264StreamSource* fileSource = LiveH264StreamSource::createNew(envir(), fFileName);
- if (fileSource == NULL) return NULL;
- fFileSize = fileSource->fileSize();
- // Create a framer for the Video Elementary Stream:
- return H264VideoStreamFramer::createNew(envir(), fileSource);
- }
- RTPSink* LiveVideoFileServerMediaSubsession
- ::createNewRTPSink(Groupsock* rtpGroupsock,
- unsigned char rtpPayloadTypeIfDynamic,
- FramedSource* /*inputSource*/) {
- return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
- }
5、添加LiveH264StreamSource类
- #ifndef _H264_STREAM_FILE_SOURCE_HH
- #define _H264_STREAM_FILE_SOURCE_HH
- #ifndef _FRAMED_FILE_SOURCE_HH
- #include "FramedFileSource.hh"
- #endif
- #include<pthread.h>
- #include "semaphore.h"
- #define H264_BUF_SIZE 150000
- #define H264_BUF_COUNT 10
- typedef void (*CB_FUN)(void);
- extern void h264_buf_init();
- extern void h264_buf_destroy();
- extern Boolean h264_buf_full();
- extern Boolean h264_buf_empty();
- extern int h264_buf_put(unsigned char* buf,int len);
- extern unsigned char* h264_buf_get(int* len);
- extern sem_t h264_f;
- extern sem_t h264_e;
- extern int b264IsInit;
- extern CB_FUN startfun;
- extern CB_FUN endfun;
- class LiveH264StreamSource: public FramedSource {
- public:
- static LiveH264StreamSource* createNew(UsageEnvironment& env,
- char const* fileName,
- unsigned preferredFrameSize = 0,
- unsigned playTimePerFrame = 0);
- // "preferredFrameSize" == 0 means 'no preference'
- // "playTimePerFrame" is in microseconds
- /*
- static LiveH264StreamSource* createNew(UsageEnvironment& env,
- unsigned preferredFrameSize = 0,
- unsigned playTimePerFrame = 0);
- // an alternative version of "createNew()" that's used if you already have
- // an open file.
- */
- u_int64_t fileSize() const { return fFileSize; }
- // 0 means zero-length, unbounded, or unknown
- void seekToByteAbsolute(u_int64_t byteNumber, u_int64_t numBytesToStream = 0);
- // if "numBytesToStream" is >0, then we limit the stream to that number of bytes, before treating it as EOF
- void seekToByteRelative(int64_t offset, u_int64_t numBytesToStream = 0);
- void seekToEnd(); // to force EOF handling on the next read
- protected:
- LiveH264StreamSource(UsageEnvironment& env,
- unsigned preferredFrameSize,
- unsigned playTimePerFrame);
- // called only by createNew()
- virtual ~LiveH264StreamSource();
- static void fileReadableHandler(LiveH264StreamSource* source, int mask);
- void doReadFromFile();
- private:
- // redefined virtual functions:
- virtual void doGetNextFrame();
- virtual void doStopGettingFrames();
- protected:
- u_int64_t fFileSize;
- private:
- unsigned fPreferredFrameSize;
- unsigned fPlayTimePerFrame;
- Boolean fFidIsSeekable;
- unsigned fLastPlayTime;
- Boolean fHaveStartedReading;
- Boolean fLimitNumBytesToStream;
- u_int64_t fNumBytesToStream; // used iff "fLimitNumBytesToStream" is True
- };
- #endif
- #include "LiveH264StreamSource.hh"
- #include "InputFile.hh"
- #include "GroupsockHelper.hh"
- // LiveH264StreamSource //
- unsigned char h264_framebuf[H264_BUF_COUNT][H264_BUF_SIZE];
- int h264_frame_len[H264_BUF_COUNT];
- int h264_buf_head;
- int h264_buf_tail;
- int h264_buf_size;
- void h264_buf_init();
- void h264_buf_destroy();
- Boolean h264_buf_full();
- Boolean h264_buf_empty();
- int h264_buf_put(unsigned char* buf,int len);
- unsigned char* h264_buf_get();
- sem_t h264_f;
- sem_t h264_e;
- sem_t h264_m;
- int b264IsInit = 0;
- CB_FUN startfun = NULL;
- CB_FUN endfun = NULL;
- unsigned char obuf[H264_BUF_SIZE];
- unsigned olen = 0;
- void h264_buf_init()
- {
- if(b264IsInit == 0)
- {
- sem_init(&h264_f,1,0);
- sem_init(&h264_e,1,H264_BUF_COUNT);
- sem_init(&h264_m,1,1);
- h264_buf_head = 0;
- h264_buf_tail = 0;
- h264_buf_size = 0;
- //printf("must 1\n");
- }
- }
- Boolean h264_buf_full()
- {
- if(h264_buf_size == H264_BUF_COUNT)
- return True;
- return False;
- }
- Boolean h264_buf_empty()
- {
- if(h264_buf_size == 0)
- return True;
- return False;
- }
- int h264_buf_put(unsigned char* buf,int len)
- {
- sem_wait(&h264_e);
- sem_wait(&h264_m);
- bzero(h264_framebuf[h264_buf_tail],H264_BUF_SIZE);
- memcpy(h264_framebuf[h264_buf_tail],buf,len);
- h264_frame_len[h264_buf_tail] = len;
- h264_buf_tail = (h264_buf_tail + 1)%H264_BUF_COUNT;
- h264_buf_size++;
- sem_post(&h264_f);
- sem_post(&h264_m);
- }
- unsigned char* h264_buf_get(int* len)
- {
- sem_wait(&h264_m);
- unsigned char* rt = h264_framebuf[h264_buf_head];
- *len = h264_frame_len[h264_buf_head];
- h264_buf_head = (h264_buf_head+1)%H264_BUF_COUNT;
- h264_buf_size--;
- sem_post(&h264_m);
- return rt;
- }
- void h264_buf_destroy()
- {
- sem_destroy(&h264_f);
- sem_destroy(&h264_e);
- sem_destroy(&h264_m);
- }
- LiveH264StreamSource*
- LiveH264StreamSource::createNew(UsageEnvironment& env, char const* fileName,
- unsigned preferredFrameSize,
- unsigned playTimePerFrame) {
- h264_buf_init();
- b264IsInit = 1;
- LiveH264StreamSource* newSource
- = new LiveH264StreamSource(env, preferredFrameSize, playTimePerFrame);
- if(startfun != NULL)
- {
- startfun();
- }
- return newSource;
- }
- /*
- LiveH264StreamSource*
- LiveH264StreamSource::createNew(UsageEnvironment& env,
- unsigned preferredFrameSize,
- unsigned playTimePerFrame) {
- LiveH264StreamSource* newSource = new LiveH264StreamSource(env, preferredFrameSize, playTimePerFrame);
- return newSource;
- }
- */
- void LiveH264StreamSource::seekToByteAbsolute(u_int64_t byteNumber, u_int64_t numBytesToStream) {
- fNumBytesToStream = numBytesToStream;
- fLimitNumBytesToStream = fNumBytesToStream > 0;
- }
- void LiveH264StreamSource::seekToByteRelative(int64_t offset, u_int64_t numBytesToStream) {
- fNumBytesToStream = numBytesToStream;
- fLimitNumBytesToStream = fNumBytesToStream > 0;
- }
- void LiveH264StreamSource::seekToEnd() {
- }
- LiveH264StreamSource::LiveH264StreamSource(UsageEnvironment& env,
- unsigned preferredFrameSize,
- unsigned playTimePerFrame)
- : FramedSource(env), fFileSize(0), fPreferredFrameSize(preferredFrameSize),
- fPlayTimePerFrame(playTimePerFrame), fLastPlayTime(0),
- fHaveStartedReading(False), fLimitNumBytesToStream(False), fNumBytesToStream(0) {
- // Test whether the file is seekable
- fFidIsSeekable = False;
- }
- LiveH264StreamSource::~LiveH264StreamSource() {
- if(endfun != NULL)
- {
- endfun();
- }
- }
- void LiveH264StreamSource::doGetNextFrame() {
- doReadFromFile();
- }
- void LiveH264StreamSource::doStopGettingFrames() {
- //envir().taskScheduler().unscheduleDelayedTask(nextTask());
- }
- void LiveH264StreamSource::fileReadableHandler(LiveH264StreamSource* source, int /*mask*/) {
- if (!source->isCurrentlyAwaitingData()) {
- source->doStopGettingFrames(); // we're not ready for the data yet
- return;
- }
- source->doReadFromFile();
- }
- void LiveH264StreamSource::doReadFromFile() {
- // Try to read as many bytes as will fit in the buffer provided (or "fPreferredFrameSize" if less)
- if (fLimitNumBytesToStream && fNumBytesToStream < (u_int64_t)fMaxSize) {
- fMaxSize = (unsigned)fNumBytesToStream;
- }
- if (fPreferredFrameSize > 0 && fPreferredFrameSize < fMaxSize) {
- fMaxSize = fPreferredFrameSize;
- }
- sem_wait(&h264_f);
- int len = 0;
- unsigned char* frame = h264_buf_get(&len);
- if(olen > 0)
- {
- memcpy(fTo,obuf,olen);
- }
- if(len + olen>= fMaxSize)
- {
- unsigned need = fMaxSize-olen;
- memcpy(&fTo[olen],frame,need);
- fFrameSize = fMaxSize;
- olen = len - need;
- memcpy(obuf,&frame[need],olen);
- }else{
- memcpy(&fTo[olen],frame,len+ olen);
- fFrameSize = olen + len;
- olen = 0;
- }
- sem_post(&h264_e);
- //fFrameSize = fread(fTo, 1, fMaxSize, fFid);
- //fwrite(fTo,fFrameSize,1,file);
- if (fFrameSize == 0) {
- //handleClosure();
- //return;
- }
- fNumBytesToStream -= fFrameSize;
- // Set the 'presentation time':
- if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {
- if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {
- // This is the first frame, so use the current time:
- gettimeofday(&fPresentationTime, NULL);
- } else {
- // Increment by the play time of the previous data:
- unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;
- fPresentationTime.tv_sec += uSeconds/1000000;
- fPresentationTime.tv_usec = uSeconds%1000000;
- }
- // Remember the play time of this data:
- fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;
- fDurationInMicroseconds = fLastPlayTime;
- } else {
- // We don't know a specific play time duration for this data,
- // so just record the current time as being the 'presentation time':
- gettimeofday(&fPresentationTime, NULL);
- }
- // To avoid possible infinite recursion, we need to return to the event loop to do this:
- nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
- (TaskFunc*)FramedSource::afterGetting, this);
- }
6、修改livemedia.hh,添加
- <span style="color:#666666;">#include "LiveADTSAudioSource.hh"
- #include "LiveADTSAudioServerMediaSubsession.hh"
- #include "LiveVideoFileServerMediaSubsession.hh"
- #include "LiveH264StreamSource.hh"</span>
7、添加接口代码
- #include <BasicUsageEnvironment.hh>
- #include "DynamicRTSPServer.hh"
- #include <liveMedia.hh>
- #include "version.hh"
- typedef void (*CB_FUN)(void);
- int Rtsp_Server_Start();
- int Rtsp_Server_Stop();
- int Rtsp_AAC_Frame(unsigned char* frame,int framelen);
- int Rtsp_H264_Frame(unsigned char* frame,int framelen);
- int Rtsp_Regist_Start_Routine(CB_FUN fun);
- int Rtsp_Regist_Stop_Routine(CB_FUN fun);
- int Rtsp_Port();
- #include "librtspserv.hh"
- #include <pthread.h>
- pthread_t id;
- void* server_start(void* arg) {
- // Begin by setting up our usage environment:
- TaskScheduler* scheduler = BasicTaskScheduler::createNew();
- UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
- UserAuthenticationDatabase* authDB = NULL;
- #ifdef ACCESS_CONTROL
- // To implement client access control to the RTSP server, do the following:
- authDB = new UserAuthenticationDatabase;
- authDB->addUserRecord("username1", "password1"); // replace these with real strings
- // Repeat the above with each <username>, <password> that you wish to allow
- // access to the server.
- #endif
- // Create the RTSP server. Try first with the default port number (554),
- // and then with the alternative port number (8554):
- RTSPServer* rtspServer;
- portNumBits rtspServerPortNum = 554;
- rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
- if (rtspServer == NULL) {
- rtspServerPortNum = 8554;
- rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
- }
- if (rtspServer == NULL) {
- *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
- exit(1);
- }
- *env << "LIVE555 Media Server\n";
- *env << "\tversion " << MEDIA_SERVER_VERSION_STRING
- << " (LIVE555 Streaming Media library version "
- << LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n";
- char* urlPrefix = rtspServer->rtspURLPrefix();
- *env << "Play streams from this server using the URL\n\t"
- << urlPrefix << "<filename>\nwhere <filename> is a file present in the current directory.\n";
- *env << "Each file's type is inferred from its name suffix:\n";
- *env << "\t\".264\" => a H.264 Video Elementary Stream file\n";
- *env << "\t\".265\" => a H.265 Video Elementary Stream file\n";
- *env << "\t\".aac\" => an AAC Audio (ADTS format) file\n";
- *env << "\t\".ac3\" => an AC-3 Audio file\n";
- *env << "\t\".amr\" => an AMR Audio file\n";
- *env << "\t\".dv\" => a DV Video file\n";
- *env << "\t\".m4e\" => a MPEG-4 Video Elementary Stream file\n";
- *env << "\t\".mkv\" => a Matroska audio+video+(optional)subtitles file\n";
- *env << "\t\".mp3\" => a MPEG-1 or 2 Audio file\n";
- *env << "\t\".mpg\" => a MPEG-1 or 2 Program Stream (audio+video) file\n";
- *env << "\t\".ogg\" or \".ogv\" or \".opus\" => an Ogg audio and/or video file\n";
- *env << "\t\".ts\" => a MPEG Transport Stream file\n";
- *env << "\t\t(a \".tsx\" index file - if present - provides server 'trick play' support)\n";
- *env << "\t\".vob\" => a VOB (MPEG-2 video with AC-3 audio) file\n";
- *env << "\t\".wav\" => a WAV Audio file\n";
- *env << "\t\".webm\" => a WebM audio(Vorbis)+video(VP8) file\n";
- *env << "See http://www.live555.com/mediaServer/ for additional documentation.\n";
- // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
- // Try first with the default HTTP port (80), and then with the alternative HTTP
- // port numbers (8000 and 8080).
- if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
- *env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";
- } else {
- *env << "(RTSP-over-HTTP tunneling is not available.)\n";
- }
- env->taskScheduler().doEventLoop(); // does not return
- return 0; // only to prevent compiler warning
- }
- int Rtsp_Server_Start()
- {
- pthread_create(&id,NULL,server_start,NULL);
- }
- int Rtsp_AAC_Frame(unsigned char* frame,int framelen)
- {
- while(bIsInit == 0)
- {
- usleep(1000*100);
- }
- aac_buf_put(frame,framelen);
- usleep(0);
- return 0;
- }
- int Rtsp_Server_Stop()
- {
- //printf("destory.....\n");
- h264_buf_destroy();
- aac_buf_destroy();
- }
- int Rtsp_H264_Frame(unsigned char* frame,int framelen)
- {
- while(b264IsInit == 0)
- {
- usleep(1000*100);
- //printf("usleep(1000*80);\n");
- }
- int i = 0;
- //printf("framelen = %d,count = %d,mod = %d\n",framelen,framelen/H264_BUF_SIZE,framelen%H264_BUF_SIZE);
- for(i = 0; i < framelen/H264_BUF_SIZE;i++)
- {
- h264_buf_put(&frame[i*H264_BUF_SIZE],H264_BUF_SIZE);
- }
- int lastsize = framelen % H264_BUF_SIZE;
- if(lastsize > 0)
- {
- h264_buf_put(&frame[i*H264_BUF_SIZE],lastsize);
- }
- usleep(0);
- return 0;
- }
- int Rtsp_Regist_Start_Routine(CB_FUN fun)
- {
- startfun = fun;
- return 0;
- }
- int Rtsp_Regist_Stop_Routine(CB_FUN fun)
- {
- endfun = fun;
- return 0;
- }
- int Rtsp_Port()
- {
- }