[SCM] liblivemedia/upstream: Imported Upstream version 2015.12.22
sramacher at users.alioth.debian.org
sramacher at users.alioth.debian.org
Tue Dec 22 21:46:16 UTC 2015
The following commit has been merged in the upstream branch:
commit dfe4b3a3d98ea4b839664f311acf8f46a2c9fd22
Author: Sebastian Ramacher <sramacher at debian.org>
Date: Tue Dec 22 21:43:19 2015 +0100
Imported Upstream version 2015.12.22
diff --git a/BasicUsageEnvironment/BasicHashTable.cpp b/BasicUsageEnvironment/BasicHashTable.cpp
index 6a86612..a85f8b3 100644
--- a/BasicUsageEnvironment/BasicHashTable.cpp
+++ b/BasicUsageEnvironment/BasicHashTable.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Hash Table implementation
// Implementation
diff --git a/BasicUsageEnvironment/BasicTaskScheduler.cpp b/BasicUsageEnvironment/BasicTaskScheduler.cpp
index 15e0732..c0d87f5 100644
--- a/BasicUsageEnvironment/BasicTaskScheduler.cpp
+++ b/BasicUsageEnvironment/BasicTaskScheduler.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
@@ -33,7 +33,11 @@ BasicTaskScheduler* BasicTaskScheduler::createNew(unsigned maxSchedulerGranulari
}
BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity)
- : fMaxSchedulerGranularity(maxSchedulerGranularity), fMaxNumSockets(0) {
+ : fMaxSchedulerGranularity(maxSchedulerGranularity), fMaxNumSockets(0)
+#if defined(__WIN32__) || defined(_WIN32)
+ , fDummySocketNum(-1)
+#endif
+{
FD_ZERO(&fReadSet);
FD_ZERO(&fWriteSet);
FD_ZERO(&fExceptionSet);
@@ -42,6 +46,9 @@ BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity)
}
BasicTaskScheduler::~BasicTaskScheduler() {
+#if defined(__WIN32__) || defined(_WIN32)
+ if (fDummySocketNum >= 0) closeSocket(fDummySocketNum);
+#endif
}
void BasicTaskScheduler::schedulerTickTask(void* clientData) {
@@ -89,8 +96,9 @@ void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
if (err == WSAEINVAL && readSet.fd_count == 0) {
err = EINTR;
// To stop this from happening again, create a dummy socket:
- int dummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
- FD_SET((unsigned)dummySocketNum, &fReadSet);
+ if (fDummySocketNum >= 0) closeSocket(fDummySocketNum);
+ fDummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
+ FD_SET((unsigned)fDummySocketNum, &fReadSet);
}
if (err != EINTR) {
#else
@@ -172,7 +180,7 @@ void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
if (fTriggersAwaitingHandling != 0) {
if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
// Common-case optimization for a single event trigger:
- fTriggersAwaitingHandling = 0;
+ fTriggersAwaitingHandling &=~ fLastUsedTriggerMask;
if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
(*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
}
@@ -207,6 +215,9 @@ void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
void BasicTaskScheduler
::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
if (socketNum < 0) return;
+#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
+ if (socketNum >= (int)(FD_SETSIZE)) return;
+#endif
FD_CLR((unsigned)socketNum, &fReadSet);
FD_CLR((unsigned)socketNum, &fWriteSet);
FD_CLR((unsigned)socketNum, &fExceptionSet);
@@ -228,6 +239,9 @@ void BasicTaskScheduler
void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
+#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
+ if (oldSocketNum >= (int)(FD_SETSIZE) || newSocketNum >= (int)(FD_SETSIZE)) return; // sanity check
+#endif
if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
diff --git a/BasicUsageEnvironment/BasicTaskScheduler0.cpp b/BasicUsageEnvironment/BasicTaskScheduler0.cpp
index 3f6c401..ecf9339 100644
--- a/BasicUsageEnvironment/BasicTaskScheduler0.cpp
+++ b/BasicUsageEnvironment/BasicTaskScheduler0.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
@@ -73,7 +73,7 @@ void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
delete alarmHandler;
}
-void BasicTaskScheduler0::doEventLoop(char* watchVariable) {
+void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
// Repeatedly loop, handling readble sockets and timed events:
while (1) {
if (watchVariable != NULL && *watchVariable != 0) break;
diff --git a/BasicUsageEnvironment/BasicUsageEnvironment.cpp b/BasicUsageEnvironment/BasicUsageEnvironment.cpp
index 79dcd94..557d26f 100644
--- a/BasicUsageEnvironment/BasicUsageEnvironment.cpp
+++ b/BasicUsageEnvironment/BasicUsageEnvironment.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
diff --git a/BasicUsageEnvironment/BasicUsageEnvironment0.cpp b/BasicUsageEnvironment/BasicUsageEnvironment0.cpp
index 473b409..6474f57 100644
--- a/BasicUsageEnvironment/BasicUsageEnvironment0.cpp
+++ b/BasicUsageEnvironment/BasicUsageEnvironment0.cpp
@@ -13,12 +13,16 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
#include "BasicUsageEnvironment0.hh"
#include <stdio.h>
+#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
+#define snprintf _snprintf
+#endif
+
////////// BasicUsageEnvironment //////////
@@ -62,11 +66,25 @@ void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2,
void BasicUsageEnvironment0::setResultErrMsg(MsgString msg, int err) {
setResultMsg(msg);
-#ifndef _WIN32_WCE
- appendToResultMsg(strerror(err == 0 ? getErrno() : err));
+ if (err == 0) err = getErrno();
+#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
+ char errMsg[RESULT_MSG_BUFFER_MAX] = "\0";
+ if (0 != FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, errMsg, sizeof(errMsg)/sizeof(errMsg[0]), NULL)) {
+ // Remove all trailing '\r', '\n' and '.'
+ for (char* p = errMsg + strlen(errMsg); p != errMsg && (*p == '\r' || *p == '\n' || *p == '.' || *p == '\0'); --p) {
+ *p = '\0';
+ }
+ } else
+ snprintf(errMsg, sizeof(errMsg)/sizeof(errMsg[0]), "error %d", err);
+ appendToResultMsg(errMsg);
+#else
+ appendToResultMsg(strerror(err));
#endif
}
+
+
+
void BasicUsageEnvironment0::appendToResultMsg(MsgString msg) {
char* curPtr = &fResultMsgBuffer[fCurBufferSize];
unsigned spaceAvailable = fBufferMaxSize - fCurBufferSize;
diff --git a/BasicUsageEnvironment/DelayQueue.cpp b/BasicUsageEnvironment/DelayQueue.cpp
index 8e5c366..17ec6f9 100644
--- a/BasicUsageEnvironment/DelayQueue.cpp
+++ b/BasicUsageEnvironment/DelayQueue.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// Help by Carlo Bonamico to get working for Windows
// Delay queue
// Implementation
@@ -200,7 +200,7 @@ DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind) {
void DelayQueue::synchronize() {
// First, figure out how much time has elapsed since the last sync:
- EventTime timeNow = TimeNow();
+ _EventTime timeNow = TimeNow();
if (timeNow < fLastSyncTime) {
// The system clock has apparently gone back in time; reset our sync time and return:
fLastSyncTime = timeNow;
@@ -220,14 +220,14 @@ void DelayQueue::synchronize() {
}
-///// EventTime /////
+///// _EventTime /////
-EventTime TimeNow() {
+_EventTime TimeNow() {
struct timeval tvNow;
gettimeofday(&tvNow, NULL);
- return EventTime(tvNow.tv_sec, tvNow.tv_usec);
+ return _EventTime(tvNow.tv_sec, tvNow.tv_usec);
}
-const EventTime THE_END_OF_TIME(INT_MAX);
+const _EventTime THE_END_OF_TIME(INT_MAX);
diff --git a/BasicUsageEnvironment/Makefile.tail b/BasicUsageEnvironment/Makefile.tail
index 84de5df..9b2f6dc 100644
--- a/BasicUsageEnvironment/Makefile.tail
+++ b/BasicUsageEnvironment/Makefile.tail
@@ -37,7 +37,7 @@ install1: libBasicUsageEnvironment.$(LIB_SUFFIX)
install -m 644 include/*.hh $(DESTDIR)$(PREFIX)/include/BasicUsageEnvironment
install -m 644 libBasicUsageEnvironment.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)
install_shared_libraries: libBasicUsageEnvironment.$(LIB_SUFFIX)
- ln -s $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
- ln -s $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
+ ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
+ ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
##### Any additional, platform-specific rules come here:
diff --git a/BasicUsageEnvironment/include/BasicHashTable.hh b/BasicUsageEnvironment/include/BasicHashTable.hh
index 8e56a23..7b59984 100644
--- a/BasicUsageEnvironment/include/BasicHashTable.hh
+++ b/BasicUsageEnvironment/include/BasicHashTable.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Hash Table implementation
// C++ header
diff --git a/BasicUsageEnvironment/include/BasicUsageEnvironment.hh b/BasicUsageEnvironment/include/BasicUsageEnvironment.hh
index 1d68da3..da856f7 100644
--- a/BasicUsageEnvironment/include/BasicUsageEnvironment.hh
+++ b/BasicUsageEnvironment/include/BasicUsageEnvironment.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// C++ header
@@ -75,6 +75,12 @@ protected:
fd_set fReadSet;
fd_set fWriteSet;
fd_set fExceptionSet;
+
+private:
+#if defined(__WIN32__) || defined(_WIN32)
+ // Hack to work around a bug in Windows' "select()" implementation:
+ int fDummySocketNum;
+#endif
};
#endif
diff --git a/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh b/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh
index d64d8c8..c2a1ded 100644
--- a/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh
+++ b/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// C++ header
@@ -86,7 +86,7 @@ public:
void* clientData);
virtual void unscheduleDelayedTask(TaskToken& prevTask);
- virtual void doEventLoop(char* watchVariable);
+ virtual void doEventLoop(char volatile* watchVariable);
virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc);
virtual void deleteEventTrigger(EventTriggerId eventTriggerId);
@@ -104,7 +104,8 @@ protected:
int fLastHandledSocketNum;
// To implement event triggers:
- EventTriggerId fTriggersAwaitingHandling, fLastUsedTriggerMask; // implemented as 32-bit bitmaps
+ EventTriggerId volatile fTriggersAwaitingHandling; // implemented as a 32-bit bitmap
+ EventTriggerId fLastUsedTriggerMask; // implemented as a 32-bit bitmap
TaskFunc* fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS];
void* fTriggeredEventClientDatas[MAX_NUM_EVENT_TRIGGERS];
unsigned fLastUsedTriggerNum; // in the range [0,MAX_NUM_EVENT_TRIGGERS)
diff --git a/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh b/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh
index db74262..f06fee1 100644
--- a/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh
+++ b/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh
@@ -1,10 +1,10 @@
// Version information for the "BasicUsageEnvironment" library
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2015 Live Networks, Inc. All rights reserved.
#ifndef _BASICUSAGEENVIRONMENT_VERSION_HH
#define _BASICUSAGEENVIRONMENT_VERSION_HH
-#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_STRING "2014.01.13"
-#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT 1389571200
+#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_STRING "2015.12.22"
+#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT 1450742400
#endif
diff --git a/BasicUsageEnvironment/include/DelayQueue.hh b/BasicUsageEnvironment/include/DelayQueue.hh
index f6bb2ed..8ad1a3d 100644
--- a/BasicUsageEnvironment/include/DelayQueue.hh
+++ b/BasicUsageEnvironment/include/DelayQueue.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
- // Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+ // Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// Delay queue
// C++ header
@@ -115,19 +115,19 @@ extern DelayInterval const DELAY_MINUTE;
extern DelayInterval const DELAY_HOUR;
extern DelayInterval const DELAY_DAY;
-///// EventTime /////
+///// _EventTime /////
-class EventTime: public Timeval {
+class _EventTime: public Timeval {
public:
- EventTime(unsigned secondsSinceEpoch = 0,
+ _EventTime(unsigned secondsSinceEpoch = 0,
unsigned usecondsSinceEpoch = 0)
// We use the Unix standard epoch: January 1, 1970
: Timeval(secondsSinceEpoch, usecondsSinceEpoch) {}
};
-EventTime TimeNow();
+_EventTime TimeNow();
-extern EventTime const THE_END_OF_TIME;
+extern _EventTime const THE_END_OF_TIME;
///// DelayQueueEntry /////
@@ -176,7 +176,7 @@ private:
DelayQueueEntry* findEntryByToken(intptr_t token);
void synchronize(); // bring the 'time remaining' fields up-to-date
- EventTime fLastSyncTime;
+ _EventTime fLastSyncTime;
};
#endif
diff --git a/BasicUsageEnvironment/include/HandlerSet.hh b/BasicUsageEnvironment/include/HandlerSet.hh
index dbd00e3..63d5c7c 100644
--- a/BasicUsageEnvironment/include/HandlerSet.hh
+++ b/BasicUsageEnvironment/include/HandlerSet.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// C++ header
diff --git a/UsageEnvironment/HashTable.cpp b/UsageEnvironment/HashTable.cpp
index 63fc592..77302d1 100644
--- a/UsageEnvironment/HashTable.cpp
+++ b/UsageEnvironment/HashTable.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Generic Hash Table
// Implementation
diff --git a/UsageEnvironment/Makefile.tail b/UsageEnvironment/Makefile.tail
index 96c6227..a7c23df 100644
--- a/UsageEnvironment/Makefile.tail
+++ b/UsageEnvironment/Makefile.tail
@@ -31,7 +31,7 @@ install1: $(USAGE_ENVIRONMENT_LIB)
install -m 644 include/*.hh $(DESTDIR)$(PREFIX)/include/UsageEnvironment
install -m 644 $(USAGE_ENVIRONMENT_LIB) $(DESTDIR)$(LIBDIR)
install_shared_libraries: $(USAGE_ENVIRONMENT_LIB)
- ln -s $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
- ln -s $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
+ ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
+ ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
##### Any additional, platform-specific rules come here:
diff --git a/UsageEnvironment/UsageEnvironment.cpp b/UsageEnvironment/UsageEnvironment.cpp
index 2db4a68..1337718 100644
--- a/UsageEnvironment/UsageEnvironment.cpp
+++ b/UsageEnvironment/UsageEnvironment.cpp
@@ -13,15 +13,20 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Usage Environment
// Implementation
#include "UsageEnvironment.hh"
-void UsageEnvironment::reclaim() {
+Boolean UsageEnvironment::reclaim() {
// We delete ourselves only if we have no remainining state:
- if (liveMediaPriv == NULL && groupsockPriv == NULL) delete this;
+ if (liveMediaPriv == NULL && groupsockPriv == NULL) {
+ delete this;
+ return True;
+ }
+
+ return False;
}
UsageEnvironment::UsageEnvironment(TaskScheduler& scheduler)
diff --git a/UsageEnvironment/include/HashTable.hh b/UsageEnvironment/include/HashTable.hh
index 840628d..a0d59c2 100644
--- a/UsageEnvironment/include/HashTable.hh
+++ b/UsageEnvironment/include/HashTable.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Generic Hash Table
// C++ header
diff --git a/UsageEnvironment/include/UsageEnvironment.hh b/UsageEnvironment/include/UsageEnvironment.hh
index ad59669..2ff3469 100644
--- a/UsageEnvironment/include/UsageEnvironment.hh
+++ b/UsageEnvironment/include/UsageEnvironment.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Usage Environment
// C++ header
@@ -52,7 +52,8 @@ class TaskScheduler; // forward
class UsageEnvironment {
public:
- void reclaim();
+ Boolean reclaim();
+ // returns True iff we were actually able to delete our object
// task scheduler:
TaskScheduler& taskScheduler() const {return fScheduler;}
@@ -136,7 +137,7 @@ public:
virtual void moveSocketHandling(int oldSocketNum, int newSocketNum) = 0;
// Changes any socket handling for "oldSocketNum" so that occurs with "newSocketNum" instead.
- virtual void doEventLoop(char* watchVariable = NULL) = 0;
+ virtual void doEventLoop(char volatile* watchVariable = NULL) = 0;
// Causes further execution to take place within the event loop.
// Delayed tasks, background I/O handling, and other events are handled, sequentially (as a single thread of control).
// (If "watchVariable" is not NULL, then we return from this routine when *watchVariable != 0)
diff --git a/UsageEnvironment/include/UsageEnvironment_version.hh b/UsageEnvironment/include/UsageEnvironment_version.hh
index 40cfc5a..6179818 100644
--- a/UsageEnvironment/include/UsageEnvironment_version.hh
+++ b/UsageEnvironment/include/UsageEnvironment_version.hh
@@ -1,10 +1,10 @@
// Version information for the "UsageEnvironment" library
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2015 Live Networks, Inc. All rights reserved.
#ifndef _USAGEENVIRONMENT_VERSION_HH
#define _USAGEENVIRONMENT_VERSION_HH
-#define USAGEENVIRONMENT_LIBRARY_VERSION_STRING "2014.01.13"
-#define USAGEENVIRONMENT_LIBRARY_VERSION_INT 1389571200
+#define USAGEENVIRONMENT_LIBRARY_VERSION_STRING "2015.12.22"
+#define USAGEENVIRONMENT_LIBRARY_VERSION_INT 1450742400
#endif
diff --git a/UsageEnvironment/include/strDup.hh b/UsageEnvironment/include/strDup.hh
index d735a56..4ed8151 100644
--- a/UsageEnvironment/include/strDup.hh
+++ b/UsageEnvironment/include/strDup.hh
@@ -17,11 +17,13 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#ifndef _STRDUP_HH
#define _STRDUP_HH
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A C++ equivalent to the standard C routine "strdup()".
// This generates a char* that can be deleted using "delete[]"
// Header
+#include <string.h>
+
char* strDup(char const* str);
// Note: strDup(NULL) returns NULL
@@ -29,4 +31,7 @@ char* strDupSize(char const* str);
// Like "strDup()", except that it *doesn't* copy the original.
// (Instead, it just allocates a string of the same size as the original.)
+char* strDupSize(char const* str, size_t& resultBufSize);
+// An alternative form of "strDupSize()" that also returns the size of the allocated buffer.
+
#endif
diff --git a/UsageEnvironment/strDup.cpp b/UsageEnvironment/strDup.cpp
index b0d34a8..f2860d3 100644
--- a/UsageEnvironment/strDup.cpp
+++ b/UsageEnvironment/strDup.cpp
@@ -13,13 +13,12 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A C++ equivalent to the standard C routine "strdup()".
// This generates a char* that can be deleted using "delete[]"
// Implementation
#include "strDup.hh"
-#include "string.h"
char* strDup(char const* str) {
if (str == NULL) return NULL;
@@ -33,10 +32,19 @@ char* strDup(char const* str) {
}
char* strDupSize(char const* str) {
- if (str == NULL) return NULL;
- size_t len = strlen(str) + 1;
- char* copy = new char[len];
+ size_t dummy;
- return copy;
+ return strDupSize(str, dummy);
}
+char* strDupSize(char const* str, size_t& resultBufSize) {
+ if (str == NULL) {
+ resultBufSize = 0;
+ return NULL;
+ }
+
+ resultBufSize = strlen(str) + 1;
+ char* copy = new char[resultBufSize];
+
+ return copy;
+}
diff --git a/WindowsAudioInputDevice/WindowsAudioInputDevice_common.hh b/WindowsAudioInputDevice/WindowsAudioInputDevice_common.hh
index d3dc510..31d23f3 100644
--- a/WindowsAudioInputDevice/WindowsAudioInputDevice_common.hh
+++ b/WindowsAudioInputDevice/WindowsAudioInputDevice_common.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// Base class for both library versions:
// One that uses Windows' built-in software mixer; another that doesn't.
diff --git a/WindowsAudioInputDevice/WindowsAudioInputDevice_mixer.hh b/WindowsAudioInputDevice/WindowsAudioInputDevice_mixer.hh
index b009ef1..57ef0ac 100644
--- a/WindowsAudioInputDevice/WindowsAudioInputDevice_mixer.hh
+++ b/WindowsAudioInputDevice/WindowsAudioInputDevice_mixer.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// This version uses Windows' built-in software mixer.
// C++ header
diff --git a/WindowsAudioInputDevice/WindowsAudioInputDevice_noMixer.hh b/WindowsAudioInputDevice/WindowsAudioInputDevice_noMixer.hh
index 6b67856..784c9f1 100644
--- a/WindowsAudioInputDevice/WindowsAudioInputDevice_noMixer.hh
+++ b/WindowsAudioInputDevice/WindowsAudioInputDevice_noMixer.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// This version does not use Windows' built-in software mixer.
// C++ header
diff --git a/WindowsAudioInputDevice/showAudioInputPorts.cpp b/WindowsAudioInputDevice/showAudioInputPorts.cpp
index c879ffe..15a2d19 100644
--- a/WindowsAudioInputDevice/showAudioInputPorts.cpp
+++ b/WindowsAudioInputDevice/showAudioInputPorts.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that prints out this computer's audio input ports
#include "AudioInputDevice.hh"
diff --git a/config.iphone-simulator b/config.iphone-simulator
index 3401d59..6fb40fd 100644
--- a/config.iphone-simulator
+++ b/config.iphone-simulator
@@ -1,22 +1,26 @@
-# Change the following version number, if necessary, before running "genMakefiles iphoneos"
-IOS_VERSION = 6.1
+# **Note: You must install the relevant "Command line tools (OSX *.*) for Xcode - Xcode *.*"
+# for this configuration file to work.
-DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer
-TOOL_PATH = $(DEVELOPER_PATH)/usr/bin
-SDK_PATH = $(DEVELOPER_PATH)/SDKs
-SDK = $(SDK_PATH)/iPhoneSimulator$(IOS_VERSION).sdk
-COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O2 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC -arch i386 --sysroot=$(SDK)
+# Change the following version number, if necessary, before running "genMakefiles iphone-simulator"
+IOS_VERSION = 8.3
+MIN_IOS_VERSION = 7.0
+
+DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer
+TOOL_PATH = $(DEVELOPER_PATH)/usr/bin
+SDK_PATH = $(DEVELOPER_PATH)/SDKs
+SDK = $(SDK_PATH)/iPhoneSimulator$(IOS_VERSION).sdk
+COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O2 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -miphoneos-version-min=$(MIN_IOS_VERSION) -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC -arch i386 --sysroot=$(SDK) -isysroot $(SDK)
C = c
-C_COMPILER = $(TOOL_PATH)/gcc
+C_COMPILER = /usr/bin/xcrun clang
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
-CPLUSPLUS_COMPILER = $(TOOL_PATH)/g++
+CPLUSPLUS_COMPILER = /usr/bin/xcrun clang
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
-LINK = $(TOOL_PATH)/g++ -o
-LINK_OPTS = -L. -arch i386 --sysroot=$(SDK) -L$(SDK)/usr/lib/system
+LINK = /usr/bin/xcrun clang -o
+LINK_OPTS = -L. -arch i386 -miphoneos-version-min=$(MIN_IOS_VERSION) --sysroot=$(SDK) -isysroot -L$(SDK)/usr/lib/system -I$(SDK)/usr/lib /usr/lib/libc++.dylib
CONSOLE_LINK_OPTS = $(LINK_OPTS)
-LIBRARY_LINK = libtool -s -o
+LIBRARY_LINK = /usr/bin/xcrun libtool -static -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
diff --git a/config.iphoneos b/config.iphoneos
index 33d9af7..c534eed 100644
--- a/config.iphoneos
+++ b/config.iphoneos
@@ -1,24 +1,27 @@
+# **Note: You must install the relevant "Command line tools (OSX *.*) for Xcode - Xcode *.*"
+# for this configuration file to work.
+#
# Change the following version number, if necessary, before running "genMakefiles iphoneos"
-IOS_VERSION = 6.1
+IOS_VERSION = 8.3
-DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer
-TOOL_PATH = $(DEVELOPER_PATH)/usr/bin
-SDK_PATH = $(DEVELOPER_PATH)/SDKs
-SDK = $(SDK_PATH)/iPhoneOS$(IOS_VERSION).sdk
+DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer
+TOOL_PATH = $(DEVELOPER_PATH)/usr/bin
+SDK_PATH = $(DEVELOPER_PATH)/SDKs
+SDK = $(SDK_PATH)/iPhoneOS$(IOS_VERSION).sdk
COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O2 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC -arch armv7 --sysroot=$(SDK)
C = c
-C_COMPILER = $(TOOL_PATH)/gcc
+C_COMPILER = /usr/bin/xcrun clang
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
-CPLUSPLUS_COMPILER = $(TOOL_PATH)/g++
+CPLUSPLUS_COMPILER = /usr/bin/xcrun clang
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
-LINK = $(TOOL_PATH)/g++ -o
-LINK_OPTS = -L. -arch armv7 --sysroot=$(SDK) -L$(SDK)/usr/lib/system
+LINK = /usr/bin/xcrun clang -o
+LINK_OPTS = -v -L. -arch armv7 --sysroot=$(SDK) -L$(SDK)/usr/lib/system /usr/lib/libc++.dylib
CONSOLE_LINK_OPTS = $(LINK_OPTS)
-LIBRARY_LINK = libtool -s -o
+LIBRARY_LINK = /usr/bin/xcrun libtool -static -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
-EXE =
+EXE =
diff --git a/config.linux-with-shared-libraries b/config.linux-with-shared-libraries
index 687ac77..cc8e64f 100644
--- a/config.linux-with-shared-libraries
+++ b/config.linux-with-shared-libraries
@@ -3,39 +3,39 @@
# At least one interface changes, or is removed => CURRENT += 1; REVISION = 0; AGE = 0
# One or more interfaces were added, but no existing interfaces were changed or removed => CURRENT += 1; REVISION = 0; AGE += 1
-libliveMedia_VERSION_CURRENT=24
-libliveMedia_VERSION_REVISION=0
+libliveMedia_VERSION_CURRENT=51
+libliveMedia_VERSION_REVISION=4
libliveMedia_VERSION_AGE=1
libliveMedia_LIB_SUFFIX=so.$(shell expr $(libliveMedia_VERSION_CURRENT) - $(libliveMedia_VERSION_AGE)).$(libliveMedia_VERSION_AGE).$(libliveMedia_VERSION_REVISION)
-libBasicUsageEnvironment_VERSION_CURRENT=0
-libBasicUsageEnvironment_VERSION_REVISION=2
+libBasicUsageEnvironment_VERSION_CURRENT=1
+libBasicUsageEnvironment_VERSION_REVISION=0
libBasicUsageEnvironment_VERSION_AGE=0
libBasicUsageEnvironment_LIB_SUFFIX=so.$(shell expr $(libBasicUsageEnvironment_VERSION_CURRENT) - $(libBasicUsageEnvironment_VERSION_AGE)).$(libBasicUsageEnvironment_VERSION_AGE).$(libBasicUsageEnvironment_VERSION_REVISION)
-libUsageEnvironment_VERSION_CURRENT=1
+libUsageEnvironment_VERSION_CURRENT=4
libUsageEnvironment_VERSION_REVISION=0
-libUsageEnvironment_VERSION_AGE=0
+libUsageEnvironment_VERSION_AGE=1
libUsageEnvironment_LIB_SUFFIX=so.$(shell expr $(libUsageEnvironment_VERSION_CURRENT) - $(libUsageEnvironment_VERSION_AGE)).$(libUsageEnvironment_VERSION_AGE).$(libUsageEnvironment_VERSION_REVISION)
-libgroupsock_VERSION_CURRENT=1
-libgroupsock_VERSION_REVISION=4
+libgroupsock_VERSION_CURRENT=8
+libgroupsock_VERSION_REVISION=0
libgroupsock_VERSION_AGE=0
libgroupsock_LIB_SUFFIX=so.$(shell expr $(libgroupsock_VERSION_CURRENT) - $(libgroupsock_VERSION_AGE)).$(libgroupsock_VERSION_AGE).$(libgroupsock_VERSION_REVISION)
#####
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC
C = c
-C_COMPILER = cc
+C_COMPILER = $(CC)
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
-CPLUSPLUS_COMPILER = c++
+CPLUSPLUS_COMPILER = $(CXX)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 $(CPPFLAGS) $(CXXFLAGS)
OBJ = o
-LINK = c++ -o
+LINK = $(CXX) -o
LINK_OPTS = -L. $(LDFLAGS)
CONSOLE_LINK_OPTS = $(LINK_OPTS)
-LIBRARY_LINK = gcc -o
+LIBRARY_LINK = $(CC) -o
SHORT_LIB_SUFFIX = so.$(shell expr $($(NAME)_VERSION_CURRENT) - $($(NAME)_VERSION_AGE))
LIB_SUFFIX = $(SHORT_LIB_SUFFIX).$($(NAME)_VERSION_AGE).$($(NAME)_VERSION_REVISION)
LIBRARY_LINK_OPTS = -shared -Wl,-soname,$(NAME).$(SHORT_LIB_SUFFIX) $(LDFLAGS)
diff --git a/config.solaris-32bit b/config.solaris-32bit
index 3f02c67..0229773 100644
--- a/config.solaris-32bit
+++ b/config.solaris-32bit
@@ -1,4 +1,4 @@
-COMPILE_OPTS = $(INCLUDES) -I. -O -DSOLARIS -DSOCKLEN_T=socklen_t
+COMPILE_OPTS = $(INCLUDES) -I. -O -DSOLARIS -DXLOCALE_NOT_USED -DSOCKLEN_T=socklen_t
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
diff --git a/config.solaris-64bit b/config.solaris-64bit
index 60ec3b4..9e8258e 100644
--- a/config.solaris-64bit
+++ b/config.solaris-64bit
@@ -1,4 +1,4 @@
-COMPILE_OPTS = $(INCLUDES) -m64 -I. -O -DSOLARIS -DSOCKLEN_T=socklen_t
+COMPILE_OPTS = $(INCLUDES) -m64 -I. -O -DSOLARIS -DXLOCALE_NOT_USED -DSOCKLEN_T=socklen_t
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
diff --git a/groupsock/GroupEId.cpp b/groupsock/GroupEId.cpp
index 686e547..6a56baa 100644
--- a/groupsock/GroupEId.cpp
+++ b/groupsock/GroupEId.cpp
@@ -13,92 +13,37 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// "Group Endpoint Id"
// Implementation
#include "GroupEId.hh"
-#include "strDup.hh"
-#include <string.h>
-////////// Scope //////////
-
-void Scope::assign(u_int8_t ttl, const char* publicKey) {
- fTTL = ttl;
-
- fPublicKey = strDup(publicKey == NULL ? "nokey" : publicKey);
-}
-
-void Scope::clean() {
- delete[] fPublicKey;
- fPublicKey = NULL;
-}
-
-
-Scope::Scope(u_int8_t ttl, const char* publicKey) {
- assign(ttl, publicKey);
-}
-
-Scope::Scope(const Scope& orig) {
- assign(orig.ttl(), orig.publicKey());
-}
-
-Scope& Scope::operator=(const Scope& rightSide) {
- if (&rightSide != this) {
- if (publicKey() == NULL
- || strcmp(publicKey(), rightSide.publicKey()) != 0) {
- clean();
- assign(rightSide.ttl(), rightSide.publicKey());
- } else { // need to assign TTL only
- fTTL = rightSide.ttl();
- }
- }
-
- return *this;
-}
-
-Scope::~Scope() {
- clean();
-}
-
-unsigned Scope::publicKeySize() const {
- return fPublicKey == NULL ? 0 : strlen(fPublicKey);
-}
-
-////////// GroupEId //////////
GroupEId::GroupEId(struct in_addr const& groupAddr,
- portNumBits portNum, Scope const& scope,
- unsigned numSuccessiveGroupAddrs) {
+ portNumBits portNum, u_int8_t ttl) {
struct in_addr sourceFilterAddr;
sourceFilterAddr.s_addr = ~0; // indicates no source filter
- init(groupAddr, sourceFilterAddr, portNum, scope, numSuccessiveGroupAddrs);
+ init(groupAddr, sourceFilterAddr, portNum, ttl);
}
GroupEId::GroupEId(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
- portNumBits portNum,
- unsigned numSuccessiveGroupAddrs) {
- init(groupAddr, sourceFilterAddr, portNum, 255, numSuccessiveGroupAddrs);
-}
-
-GroupEId::GroupEId() {
+ portNumBits portNum) {
+ init(groupAddr, sourceFilterAddr, portNum, 255);
}
Boolean GroupEId::isSSM() const {
return fSourceFilterAddress.s_addr != netAddressBits(~0);
}
-
void GroupEId::init(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
portNumBits portNum,
- Scope const& scope,
- unsigned numSuccessiveGroupAddrs) {
+ u_int8_t ttl) {
fGroupAddress = groupAddr;
fSourceFilterAddress = sourceFilterAddr;
- fNumSuccessiveGroupAddrs = numSuccessiveGroupAddrs;
fPortNum = portNum;
- fScope = scope;
+ fTTL = ttl;
}
diff --git a/groupsock/Groupsock.cpp b/groupsock/Groupsock.cpp
index 196b5b5..c59fd3e 100644
--- a/groupsock/Groupsock.cpp
+++ b/groupsock/Groupsock.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// 'Group sockets'
// Implementation
@@ -31,29 +31,27 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
OutputSocket::OutputSocket(UsageEnvironment& env)
: Socket(env, 0 /* let kernel choose port */),
- fSourcePort(0), fLastSentTTL(0) {
+ fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
}
OutputSocket::OutputSocket(UsageEnvironment& env, Port port)
: Socket(env, port),
- fSourcePort(0), fLastSentTTL(0) {
+ fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
}
OutputSocket::~OutputSocket() {
}
-Boolean OutputSocket::write(netAddressBits address, Port port, u_int8_t ttl,
+Boolean OutputSocket::write(netAddressBits address, portNumBits portNum, u_int8_t ttl,
unsigned char* buffer, unsigned bufferSize) {
- if (ttl == fLastSentTTL) {
- // Optimization: So we don't do a 'set TTL' system call again
- ttl = 0;
+ struct in_addr destAddr; destAddr.s_addr = address;
+ if ((unsigned)ttl == fLastSentTTL) {
+ // Optimization: Don't do a 'set TTL' system call again
+ if (!writeSocket(env(), socketNum(), destAddr, portNum, buffer, bufferSize)) return False;
} else {
- fLastSentTTL = ttl;
+ if (!writeSocket(env(), socketNum(), destAddr, portNum, ttl, buffer, bufferSize)) return False;
+ fLastSentTTL = (unsigned)ttl;
}
- struct in_addr destAddr; destAddr.s_addr = address;
- if (!writeSocket(env(), socketNum(), destAddr, port, ttl,
- buffer, bufferSize))
- return False;
if (sourcePortNum() == 0) {
// Now that we've sent a packet, we can find out what the
@@ -73,7 +71,7 @@ Boolean OutputSocket::write(netAddressBits address, Port port, u_int8_t ttl,
// By default, we don't do reads:
Boolean OutputSocket
::handleRead(unsigned char* /*buffer*/, unsigned /*bufferMaxSize*/,
- unsigned& /*bytesRead*/, struct sockaddr_in& /*fromAddress*/) {
+ unsigned& /*bytesRead*/, struct sockaddr_in& /*fromAddressAndPort*/) {
return True;
}
@@ -81,9 +79,9 @@ Boolean OutputSocket
///////// destRecord //////////
destRecord
-::destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl,
+::destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId,
destRecord* next)
- : fNext(next), fGroupEId(addr, port.num(), ttl), fPort(port) {
+ : fNext(next), fGroupEId(addr, port.num(), ttl), fSessionId(sessionId) {
}
destRecord::~destRecord() {
@@ -103,8 +101,8 @@ Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
Port port, u_int8_t ttl)
: OutputSocket(env, port),
deleteIfNoMembers(False), isSlave(False),
- fIncomingGroupEId(groupAddr, port.num(), ttl), fDests(NULL), fTTL(ttl) {
- addDestination(groupAddr, port);
+ fDests(new destRecord(groupAddr, port, ttl, 0, NULL)),
+ fIncomingGroupEId(groupAddr, port.num(), ttl) {
if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) {
if (DebugLevel >= 1) {
@@ -130,10 +128,8 @@ Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
Port port)
: OutputSocket(env, port),
deleteIfNoMembers(False), isSlave(False),
- fIncomingGroupEId(groupAddr, sourceFilterAddr, port.num()),
- fDests(NULL), fTTL(255) {
- addDestination(groupAddr, port);
-
+ fDests(new destRecord(groupAddr, port, 255, 0, NULL)),
+ fIncomingGroupEId(groupAddr, sourceFilterAddr, port.num()) {
// First try a SSM join. If that fails, try a regular join:
if (!socketJoinGroupSSM(env, socketNum(), groupAddr.s_addr,
sourceFilterAddr.s_addr)) {
@@ -168,12 +164,26 @@ Groupsock::~Groupsock() {
if (DebugLevel >= 2) env() << *this << ": deleting\n";
}
+destRecord* Groupsock
+::createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl,
+ unsigned sessionId, destRecord* next) {
+ // Default implementation:
+ return new destRecord(addr, port, ttl, sessionId, next);
+}
+
void
Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr,
- Port newDestPort, int newDestTTL) {
- if (fDests == NULL) return;
+ Port newDestPort, int newDestTTL, unsigned sessionId) {
+ destRecord* dest;
+ for (dest = fDests; dest != NULL && dest->fSessionId != sessionId; dest = dest->fNext) {}
+
+ if (dest == NULL) { // There's no existing 'destRecord' for this "sessionId"; add a new one:
+ fDests = createNewDestRecord(newDestAddr, newDestPort, newDestTTL, sessionId, fDests);
+ return;
+ }
- struct in_addr destAddr = fDests->fGroupEId.groupAddress();
+ // "dest" is an existing 'destRecord' for this "sessionId"; change its values to the new ones:
+ struct in_addr destAddr = dest->fGroupEId.groupAddress();
if (newDestAddr.s_addr != 0) {
if (newDestAddr.s_addr != destAddr.s_addr
&& IsMulticastAddress(newDestAddr.s_addr)) {
@@ -186,7 +196,7 @@ Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr,
destAddr.s_addr = newDestAddr.s_addr;
}
- portNumBits destPortNum = fDests->fGroupEId.portNum();
+ portNumBits destPortNum = dest->fGroupEId.portNum();
if (newDestPort.num() != 0) {
if (newDestPort.num() != destPortNum
&& IsMulticastAddress(destAddr.s_addr)) {
@@ -196,40 +206,42 @@ Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr,
socketJoinGroup(env(), socketNum(), destAddr.s_addr);
}
destPortNum = newDestPort.num();
- fDests->fPort = newDestPort;
}
u_int8_t destTTL = ttl();
if (newDestTTL != ~0) destTTL = (u_int8_t)newDestTTL;
- fDests->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
+ dest->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
+
+ // Finally, remove any other 'destRecord's that might also have this "sessionId":
+ removeDestinationFrom(dest->fNext, sessionId);
}
-void Groupsock::addDestination(struct in_addr const& addr, Port const& port) {
- // Check whether this destination is already known:
- for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
- if (addr.s_addr == dests->fGroupEId.groupAddress().s_addr
- && port.num() == dests->fPort.num()) {
- return;
- }
- }
+unsigned Groupsock
+::lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const {
+ destRecord* dest = lookupDestRecordFromDestination(destAddrAndPort);
+ if (dest == NULL) return 0;
- fDests = new destRecord(addr, port, ttl(), fDests);
+ return dest->fSessionId;
}
-void Groupsock::removeDestination(struct in_addr const& addr, Port const& port) {
- for (destRecord** destsPtr = &fDests; *destsPtr != NULL;
- destsPtr = &((*destsPtr)->fNext)) {
- if (addr.s_addr == (*destsPtr)->fGroupEId.groupAddress().s_addr
- && port.num() == (*destsPtr)->fPort.num()) {
- // Remove the record pointed to by *destsPtr :
- destRecord* next = (*destsPtr)->fNext;
- (*destsPtr)->fNext = NULL;
- delete (*destsPtr);
- *destsPtr = next;
+void Groupsock::addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId) {
+ // Default implementation:
+ // If there's no existing 'destRecord' with the same "addr", "port", and "sessionId", add a new one:
+ for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
+ if (sessionId == dest->fSessionId
+ && addr.s_addr == dest->fGroupEId.groupAddress().s_addr
+ && port.num() == dest->fGroupEId.portNum()) {
return;
}
}
+
+ fDests = createNewDestRecord(addr, port, 255, sessionId, fDests);
+}
+
+void Groupsock::removeDestination(unsigned sessionId) {
+ // Default implementation:
+ removeDestinationFrom(fDests, sessionId);
}
void Groupsock::removeAllDestinations() {
@@ -247,14 +259,13 @@ void Groupsock::multicastSendOnly() {
#endif
}
-Boolean Groupsock::output(UsageEnvironment& env, u_int8_t ttlToSend,
- unsigned char* buffer, unsigned bufferSize,
+Boolean Groupsock::output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize,
DirectedNetInterface* interfaceNotToFwdBackTo) {
do {
// First, do the datagram send, to each destination:
Boolean writeSuccess = True;
for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
- if (!write(dests->fGroupEId.groupAddress().s_addr, dests->fPort, ttlToSend,
+ if (!write(dests->fGroupEId.groupAddress().s_addr, dests->fGroupEId.portNum(), dests->fGroupEId.ttl(),
buffer, bufferSize)) {
writeSuccess = False;
break;
@@ -269,14 +280,13 @@ Boolean Groupsock::output(UsageEnvironment& env, u_int8_t ttlToSend,
if (!members().IsEmpty()) {
numMembers =
outputToAllMembersExcept(interfaceNotToFwdBackTo,
- ttlToSend, buffer, bufferSize,
+ ttl(), buffer, bufferSize,
ourIPAddress(env));
if (numMembers < 0) break;
}
if (DebugLevel >= 3) {
- env << *this << ": wrote " << bufferSize << " bytes, ttl "
- << (unsigned)ttlToSend;
+ env << *this << ": wrote " << bufferSize << " bytes, ttl " << (unsigned)ttl();
if (numMembers > 0) {
env << "; relayed to " << numMembers << " members";
}
@@ -286,14 +296,16 @@ Boolean Groupsock::output(UsageEnvironment& env, u_int8_t ttlToSend,
} while (0);
if (DebugLevel >= 0) { // this is a fatal error
- env.setResultMsg("Groupsock write failed: ", env.getResultMsg());
+ UsageEnvironment::MsgString msg = strDup(env.getResultMsg());
+ env.setResultMsg("Groupsock write failed: ", msg);
+ delete[] (char*)msg;
}
return False;
}
Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
- struct sockaddr_in& fromAddress) {
+ struct sockaddr_in& fromAddressAndPort) {
// Read data from the socket, and relay it across any attached tunnels
//##### later make this code more general - independent of tunnels
@@ -301,18 +313,19 @@ Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize;
int numBytes = readSocket(env(), socketNum(),
- buffer, maxBytesToRead, fromAddress);
+ buffer, maxBytesToRead, fromAddressAndPort);
if (numBytes < 0) {
if (DebugLevel >= 0) { // this is a fatal error
- env().setResultMsg("Groupsock read failed: ",
- env().getResultMsg());
+ UsageEnvironment::MsgString msg = strDup(env().getResultMsg());
+ env().setResultMsg("Groupsock read failed: ", msg);
+ delete[] (char*)msg;
}
return False;
}
// If we're a SSM group, make sure the source address matches:
if (isSSM()
- && fromAddress.sin_addr.s_addr != sourceFilterAddress().s_addr) {
+ && fromAddressAndPort.sin_addr.s_addr != sourceFilterAddress().s_addr) {
return True;
}
@@ -322,20 +335,20 @@ Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
bytesRead = numBytes;
int numMembers = 0;
- if (!wasLoopedBackFromUs(env(), fromAddress)) {
+ if (!wasLoopedBackFromUs(env(), fromAddressAndPort)) {
statsIncoming.countPacket(numBytes);
statsGroupIncoming.countPacket(numBytes);
numMembers =
outputToAllMembersExcept(NULL, ttl(),
buffer, bytesRead,
- fromAddress.sin_addr.s_addr);
+ fromAddressAndPort.sin_addr.s_addr);
if (numMembers > 0) {
statsRelayedIncoming.countPacket(numBytes);
statsGroupRelayedIncoming.countPacket(numBytes);
}
}
if (DebugLevel >= 3) {
- env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddress).val();
+ env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddressAndPort).val() << ", port " << ntohs(fromAddressAndPort.sin_port);
if (numMembers > 0) {
env() << "; relayed to " << numMembers << " members";
}
@@ -346,10 +359,10 @@ Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
}
Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env,
- struct sockaddr_in& fromAddress) {
- if (fromAddress.sin_addr.s_addr
- == ourIPAddress(env)) {
- if (fromAddress.sin_port == sourcePortNum()) {
+ struct sockaddr_in& fromAddressAndPort) {
+ if (fromAddressAndPort.sin_addr.s_addr == ourIPAddress(env) ||
+ fromAddressAndPort.sin_addr.s_addr == 0x7F000001/*127.0.0.1*/) {
+ if (fromAddressAndPort.sin_port == sourcePortNum()) {
#ifdef DEBUG_LOOPBACK_CHECKING
if (DebugLevel >= 3) {
env() << *this << ": got looped-back packet\n";
@@ -362,6 +375,32 @@ Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env,
return False;
}
+destRecord* Groupsock
+::lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const {
+ for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
+ if (destAddrAndPort.sin_addr.s_addr == dest->fGroupEId.groupAddress().s_addr
+ && destAddrAndPort.sin_port == dest->fGroupEId.portNum()) {
+ return dest;
+ }
+ }
+ return NULL;
+}
+
+void Groupsock::removeDestinationFrom(destRecord*& dests, unsigned sessionId) {
+ destRecord** destsPtr = &dests;
+ while (*destsPtr != NULL) {
+ if (sessionId == (*destsPtr)->fSessionId) {
+ // Remove the record pointed to by *destsPtr :
+ destRecord* next = (*destsPtr)->fNext;
+ (*destsPtr)->fNext = NULL;
+ delete (*destsPtr);
+ *destsPtr = next;
+ } else {
+ destsPtr = &((*destsPtr)->fNext);
+ }
+ }
+}
+
int Groupsock::outputToAllMembersExcept(DirectedNetInterface* exceptInterface,
u_int8_t ttlToFwd,
unsigned char* data, unsigned size,
@@ -420,7 +459,8 @@ int Groupsock::outputToAllMembersExcept(DirectedNetInterface* exceptInterface,
if (fDests != NULL) {
trailer->address() = fDests->fGroupEId.groupAddress().s_addr;
- trailer->port() = fDests->fPort; // structure copy, outputs in network order
+ Port destPort(ntohs(fDests->fGroupEId.portNum()));
+ trailer->port() = destPort; // structure copy
}
trailer->ttl() = ttlToFwd;
trailer->command() = tunnelCmd;
@@ -514,9 +554,7 @@ static Boolean setGroupsockBySocket(UsageEnvironment& env, int sock,
= (sockets->Lookup((char*)(long)sock) != 0);
if (alreadyExists) {
char buf[100];
- sprintf(buf,
- "Attempting to replace an existing socket (%d",
- sock);
+ sprintf(buf, "Attempting to replace an existing socket (%d)", sock);
env.setResultMsg(buf);
break;
}
diff --git a/groupsock/GroupsockHelper.cpp b/groupsock/GroupsockHelper.cpp
index 856b9c1..bfea860 100644
--- a/groupsock/GroupsockHelper.cpp
+++ b/groupsock/GroupsockHelper.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Helper routines to implement 'group sockets'
// Implementation
@@ -29,6 +29,11 @@ extern "C" int initializeWinsockIfNecessary();
#include <fcntl.h>
#define initializeWinsockIfNecessary() 1
#endif
+#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
+#else
+#include <signal.h>
+#define USE_SIGNALS 1
+#endif
#include <stdio.h>
// By default, use INADDR_ANY for the sending and receiving interfaces:
@@ -181,17 +186,29 @@ Boolean makeSocketNonBlocking(int sock) {
#endif
}
-Boolean makeSocketBlocking(int sock) {
+Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds) {
+ Boolean result;
#if defined(__WIN32__) || defined(_WIN32)
unsigned long arg = 0;
- return ioctlsocket(sock, FIONBIO, &arg) == 0;
+ result = ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
int arg = 0;
- return ioctl(sock, FIONBIO, (int)&arg) == 0;
+ result = ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
int curFlags = fcntl(sock, F_GETFL, 0);
- return fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0;
+ result = fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0;
+#endif
+
+ if (writeTimeoutInMilliseconds > 0) {
+#ifdef SO_SNDTIMEO
+ struct timeval tv;
+ tv.tv_sec = writeTimeoutInMilliseconds/1000;
+ tv.tv_usec = (writeTimeoutInMilliseconds%1000)*1000;
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof tv);
#endif
+ }
+
+ return result;
}
int setupStreamSocket(UsageEnvironment& env,
@@ -301,39 +318,54 @@ int readSocket(UsageEnvironment& env,
}
Boolean writeSocket(UsageEnvironment& env,
- int socket, struct in_addr address, Port port,
+ int socket, struct in_addr address, portNumBits portNum,
u_int8_t ttlArg,
unsigned char* buffer, unsigned bufferSize) {
- do {
- if (ttlArg != 0) {
- // Before sending, set the socket's TTL:
+ // Before sending, set the socket's TTL:
#if defined(__WIN32__) || defined(_WIN32)
#define TTL_TYPE int
#else
#define TTL_TYPE u_int8_t
#endif
- TTL_TYPE ttl = (TTL_TYPE)ttlArg;
- if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL,
- (const char*)&ttl, sizeof ttl) < 0) {
- socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: ");
- break;
- }
- }
-
- MAKE_SOCKADDR_IN(dest, address.s_addr, port.num());
- int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0,
- (struct sockaddr*)&dest, sizeof dest);
- if (bytesSent != (int)bufferSize) {
- char tmpBuf[100];
- sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
- socketErr(env, tmpBuf);
- break;
- }
-
- return True;
- } while (0);
-
- return False;
+ TTL_TYPE ttl = (TTL_TYPE)ttlArg;
+ if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL,
+ (const char*)&ttl, sizeof ttl) < 0) {
+ socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: ");
+ return False;
+ }
+
+ return writeSocket(env, socket, address, portNum, buffer, bufferSize);
+}
+
+Boolean writeSocket(UsageEnvironment& env,
+ int socket, struct in_addr address, portNumBits portNum,
+ unsigned char* buffer, unsigned bufferSize) {
+ do {
+ MAKE_SOCKADDR_IN(dest, address.s_addr, portNum);
+ int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0,
+ (struct sockaddr*)&dest, sizeof dest);
+ if (bytesSent != (int)bufferSize) {
+ char tmpBuf[100];
+ sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
+ socketErr(env, tmpBuf);
+ break;
+ }
+
+ return True;
+ } while (0);
+
+ return False;
+}
+
+void ignoreSigPipeOnSocket(int socketNum) {
+ #ifdef USE_SIGNALS
+ #ifdef SO_NOSIGPIPE
+ int set_option = 1;
+ setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
+ #else
+ signal(SIGPIPE, SIG_IGN);
+ #endif
+ #endif
}
static unsigned getBufferSize(UsageEnvironment& env, int bufOptName,
@@ -595,7 +627,7 @@ netAddressBits ourIPAddress(UsageEnvironment& env) {
unsigned char testString[] = "hostIdTest";
unsigned testStringLength = sizeof testString;
- if (!writeSocket(env, sock, testAddr, testPort, 0,
+ if (!writeSocket(env, sock, testAddr, testPort.num(), 0,
testString, testStringLength)) break;
// Block until the socket is readable (with a 5-second timeout):
@@ -696,7 +728,9 @@ char const* timestampString() {
#if !defined(_WIN32_WCE)
static char timeString[9]; // holds hh:mm:ss plus trailing '\0'
- char const* ctimeResult = ctime((time_t*)&tvNow.tv_sec);
+
+ time_t tvNow_t = tvNow.tv_sec;
+ char const* ctimeResult = ctime(&tvNow_t);
if (ctimeResult == NULL) {
sprintf(timeString, "??:??:??");
} else {
diff --git a/groupsock/IOHandlers.cpp b/groupsock/IOHandlers.cpp
index abd8664..224db4f 100644
--- a/groupsock/IOHandlers.cpp
+++ b/groupsock/IOHandlers.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// IO event handlers
// Implementation
diff --git a/groupsock/Makefile.tail b/groupsock/Makefile.tail
index 23d98dc..89a8593 100644
--- a/groupsock/Makefile.tail
+++ b/groupsock/Makefile.tail
@@ -39,7 +39,7 @@ install1: libgroupsock.$(LIB_SUFFIX)
install -m 644 include/*.hh include/*.h $(DESTDIR)$(PREFIX)/include/groupsock
install -m 644 libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)
install_shared_libraries: libgroupsock.$(LIB_SUFFIX)
- ln -s libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.$(SHORT_LIB_SUFFIX)
- ln -s libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.so
+ ln -fs libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.$(SHORT_LIB_SUFFIX)
+ ln -fs libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.so
##### Any additional, platform-specific rules come here:
diff --git a/groupsock/NetAddress.cpp b/groupsock/NetAddress.cpp
index d250643..0cd5aa6 100644
--- a/groupsock/NetAddress.cpp
+++ b/groupsock/NetAddress.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Network Addresses
// Implementation
diff --git a/groupsock/NetInterface.cpp b/groupsock/NetInterface.cpp
index a1d4592..c07607d 100644
--- a/groupsock/NetInterface.cpp
+++ b/groupsock/NetInterface.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Network Interfaces
// Implementation
diff --git a/groupsock/include/GroupEId.hh b/groupsock/include/GroupEId.hh
index c279b9e..d343c88 100644
--- a/groupsock/include/GroupEId.hh
+++ b/groupsock/include/GroupEId.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "multikit" Multicast Application Shell
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// "Group Endpoint Id"
// C++ header
@@ -29,70 +29,36 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "NetAddress.hh"
#endif
-const u_int8_t MAX_TTL = 255;
-
-class Scope {
- public:
- Scope(u_int8_t ttl = 0, const char* publicKey = NULL);
- Scope(const Scope& orig);
- Scope& operator=(const Scope& rightSide);
- ~Scope();
-
- u_int8_t ttl() const
- { return fTTL; }
-
- const char* publicKey() const
- { return fPublicKey; }
- unsigned publicKeySize() const;
-
- private:
- void assign(u_int8_t ttl, const char* publicKey);
- void clean();
-
- u_int8_t fTTL;
- char* fPublicKey;
-};
-
class GroupEId {
public:
GroupEId(struct in_addr const& groupAddr,
- portNumBits portNum, Scope const& scope,
- unsigned numSuccessiveGroupAddrs = 1);
+ portNumBits portNum, u_int8_t ttl);
// used for a 'source-independent multicast' group
GroupEId(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
- portNumBits portNum,
- unsigned numSuccessiveGroupAddrs = 1);
+ portNumBits portNum);
// used for a 'source-specific multicast' group
- GroupEId(); // used only as a temp constructor prior to initialization
struct in_addr const& groupAddress() const { return fGroupAddress; }
struct in_addr const& sourceFilterAddress() const { return fSourceFilterAddress; }
Boolean isSSM() const;
- unsigned numSuccessiveGroupAddrs() const {
- // could be >1 for hier encoding
- return fNumSuccessiveGroupAddrs;
- }
-
portNumBits portNum() const { return fPortNum; }
- const Scope& scope() const { return fScope; }
+ u_int8_t ttl() const { return fTTL; }
private:
void init(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
portNumBits portNum,
- Scope const& scope,
- unsigned numSuccessiveGroupAddrs);
+ u_int8_t ttl);
private:
struct in_addr fGroupAddress;
struct in_addr fSourceFilterAddress;
- unsigned fNumSuccessiveGroupAddrs;
- portNumBits fPortNum;
- Scope fScope;
+ portNumBits fPortNum; // in network byte order
+ u_int8_t fTTL;
};
#endif
diff --git a/groupsock/include/Groupsock.hh b/groupsock/include/Groupsock.hh
index 4a6b15b..481c1db 100644
--- a/groupsock/include/Groupsock.hh
+++ b/groupsock/include/Groupsock.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// 'Group sockets'
// C++ header
@@ -41,8 +41,12 @@ public:
OutputSocket(UsageEnvironment& env);
virtual ~OutputSocket();
- Boolean write(netAddressBits address, Port port, u_int8_t ttl,
- unsigned char* buffer, unsigned bufferSize);
+ virtual Boolean write(netAddressBits address, portNumBits portNum/*in network order*/, u_int8_t ttl,
+ unsigned char* buffer, unsigned bufferSize);
+ Boolean write(struct sockaddr_in& addressAndPort, u_int8_t ttl,
+ unsigned char* buffer, unsigned bufferSize) {
+ return write(addressAndPort.sin_addr.s_addr, addressAndPort.sin_port, ttl, buffer, bufferSize);
+ }
protected:
OutputSocket(UsageEnvironment& env, Port port);
@@ -52,23 +56,23 @@ protected:
private: // redefined virtual function
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
- struct sockaddr_in& fromAddress);
+ struct sockaddr_in& fromAddressAndPort);
private:
Port fSourcePort;
- u_int8_t fLastSentTTL;
+ unsigned fLastSentTTL;
};
class destRecord {
public:
- destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl,
+ destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId,
destRecord* next);
virtual ~destRecord();
public:
destRecord* fNext;
GroupEId fGroupEId;
- Port fPort;
+ unsigned fSessionId;
};
// A "Groupsock" is used to both send and receive packets.
@@ -86,19 +90,26 @@ public:
// used for a 'source-specific multicast' group
virtual ~Groupsock();
+ virtual destRecord* createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId, destRecord* next);
+ // Can be redefined by subclasses that also subclass "destRecord"
+
void changeDestinationParameters(struct in_addr const& newDestAddr,
- Port newDestPort, int newDestTTL);
+ Port newDestPort, int newDestTTL,
+ unsigned sessionId = 0);
// By default, the destination address, port and ttl for
// outgoing packets are those that were specified in
// the constructor. This works OK for multicast sockets,
// but for unicast we usually want the destination port
// number, at least, to be different from the source port.
- // (If a parameter is 0 (or ~0 for ttl), then no change made.)
+ // (If a parameter is 0 (or ~0 for ttl), then no change is made to that parameter.)
+ // (If no existing "destRecord" exists with this "sessionId", then we add a new "destRecord".)
+ unsigned lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const;
+ // returns 0 if not found
// As a special case, we also allow multiple destinations (addresses & ports)
// (This can be used to implement multi-unicast.)
- void addDestination(struct in_addr const& addr, Port const& port);
- void removeDestination(struct in_addr const& addr, Port const& port);
+ virtual void addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId);
+ virtual void removeDestination(unsigned sessionId);
void removeAllDestinations();
struct in_addr const& groupAddress() const {
@@ -112,13 +123,12 @@ public:
return fIncomingGroupEId.isSSM();
}
- u_int8_t ttl() const { return fTTL; }
+ u_int8_t ttl() const { return fIncomingGroupEId.ttl(); }
void multicastSendOnly(); // send, but don't receive any multicast packets
- Boolean output(UsageEnvironment& env, u_int8_t ttl,
- unsigned char* buffer, unsigned bufferSize,
- DirectedNetInterface* interfaceNotToFwdBackTo = NULL);
+ virtual Boolean output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize,
+ DirectedNetInterface* interfaceNotToFwdBackTo = NULL);
DirectedNetInterfaceSet& members() { return fMembers; }
@@ -134,24 +144,28 @@ public:
NetInterfaceTrafficStats statsGroupRelayedIncoming; // *not* static
NetInterfaceTrafficStats statsGroupRelayedOutgoing; // *not* static
- Boolean wasLoopedBackFromUs(UsageEnvironment& env,
- struct sockaddr_in& fromAddress);
+ Boolean wasLoopedBackFromUs(UsageEnvironment& env, struct sockaddr_in& fromAddressAndPort);
public: // redefined virtual functions
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
- struct sockaddr_in& fromAddress);
+ struct sockaddr_in& fromAddressAndPort);
+
+protected:
+ destRecord* lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const;
private:
+ void removeDestinationFrom(destRecord*& dests, unsigned sessionId);
+ // used to implement (the public) "removeDestination()", and "changeDestinationParameters()"
int outputToAllMembersExcept(DirectedNetInterface* exceptInterface,
u_int8_t ttlToFwd,
unsigned char* data, unsigned size,
netAddressBits sourceAddr);
+protected:
+ destRecord* fDests;
private:
GroupEId fIncomingGroupEId;
- destRecord* fDests;
- u_int8_t fTTL;
DirectedNetInterfaceSet fMembers;
};
diff --git a/groupsock/include/GroupsockHelper.hh b/groupsock/include/GroupsockHelper.hh
index 68bc307..666dc42 100644
--- a/groupsock/include/GroupsockHelper.hh
+++ b/groupsock/include/GroupsockHelper.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Helper routines to implement 'group sockets'
// C++ header
@@ -34,10 +34,17 @@ int readSocket(UsageEnvironment& env,
struct sockaddr_in& fromAddress);
Boolean writeSocket(UsageEnvironment& env,
- int socket, struct in_addr address, Port port,
+ int socket, struct in_addr address, portNumBits portNum/*network byte order*/,
u_int8_t ttlArg,
unsigned char* buffer, unsigned bufferSize);
+Boolean writeSocket(UsageEnvironment& env,
+ int socket, struct in_addr address, portNumBits portNum/*network byte order*/,
+ unsigned char* buffer, unsigned bufferSize);
+ // An optimized version of "writeSocket" that omits the "setsockopt()" call to set the TTL.
+
+void ignoreSigPipeOnSocket(int socketNum);
+
unsigned getSendBufferSize(UsageEnvironment& env, int socket);
unsigned getReceiveBufferSize(UsageEnvironment& env, int socket);
unsigned setSendBufferTo(UsageEnvironment& env,
@@ -50,7 +57,8 @@ unsigned increaseReceiveBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize);
Boolean makeSocketNonBlocking(int sock);
-Boolean makeSocketBlocking(int sock);
+Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds = 0);
+ // A "writeTimeoutInMilliseconds" value of 0 means: Don't timeout
Boolean socketJoinGroup(UsageEnvironment& env, int socket,
netAddressBits groupAddress);
diff --git a/groupsock/include/IOHandlers.hh b/groupsock/include/IOHandlers.hh
index dd00ea3..b22e761 100644
--- a/groupsock/include/IOHandlers.hh
+++ b/groupsock/include/IOHandlers.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// IO event handlers
// C++ header
diff --git a/groupsock/include/NetAddress.hh b/groupsock/include/NetAddress.hh
index 986d9c1..b9d333c 100644
--- a/groupsock/include/NetAddress.hh
+++ b/groupsock/include/NetAddress.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Network Addresses
// C++ header
@@ -39,69 +39,68 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
typedef u_int32_t netAddressBits;
class NetAddress {
- public:
- NetAddress(u_int8_t const* data,
- unsigned length = 4 /* default: 32 bits */);
- NetAddress(unsigned length = 4); // sets address data to all-zeros
- NetAddress(NetAddress const& orig);
- NetAddress& operator=(NetAddress const& rightSide);
- virtual ~NetAddress();
-
- unsigned length() const { return fLength; }
- u_int8_t const* data() const // always in network byte order
- { return fData; }
-
- private:
- void assign(u_int8_t const* data, unsigned length);
- void clean();
-
- unsigned fLength;
- u_int8_t* fData;
+public:
+ NetAddress(u_int8_t const* data,
+ unsigned length = 4 /* default: 32 bits */);
+ NetAddress(unsigned length = 4); // sets address data to all-zeros
+ NetAddress(NetAddress const& orig);
+ NetAddress& operator=(NetAddress const& rightSide);
+ virtual ~NetAddress();
+
+ unsigned length() const { return fLength; }
+ u_int8_t const* data() const // always in network byte order
+ { return fData; }
+
+private:
+ void assign(u_int8_t const* data, unsigned length);
+ void clean();
+
+ unsigned fLength;
+ u_int8_t* fData;
};
class NetAddressList {
- public:
- NetAddressList(char const* hostname);
- NetAddressList(NetAddressList const& orig);
- NetAddressList& operator=(NetAddressList const& rightSide);
- virtual ~NetAddressList();
-
- unsigned numAddresses() const { return fNumAddresses; }
-
- NetAddress const* firstAddress() const;
-
- // Used to iterate through the addresses in a list:
- class Iterator {
- public:
- Iterator(NetAddressList const& addressList);
- NetAddress const* nextAddress(); // NULL iff none
- private:
- NetAddressList const& fAddressList;
- unsigned fNextIndex;
- };
-
- private:
- void assign(netAddressBits numAddresses, NetAddress** addressArray);
- void clean();
-
- friend class Iterator;
- unsigned fNumAddresses;
- NetAddress** fAddressArray;
+public:
+ NetAddressList(char const* hostname);
+ NetAddressList(NetAddressList const& orig);
+ NetAddressList& operator=(NetAddressList const& rightSide);
+ virtual ~NetAddressList();
+
+ unsigned numAddresses() const { return fNumAddresses; }
+
+ NetAddress const* firstAddress() const;
+
+ // Used to iterate through the addresses in a list:
+ class Iterator {
+ public:
+ Iterator(NetAddressList const& addressList);
+ NetAddress const* nextAddress(); // NULL iff none
+ private:
+ NetAddressList const& fAddressList;
+ unsigned fNextIndex;
+ };
+
+private:
+ void assign(netAddressBits numAddresses, NetAddress** addressArray);
+ void clean();
+
+ friend class Iterator;
+ unsigned fNumAddresses;
+ NetAddress** fAddressArray;
};
typedef u_int16_t portNumBits;
class Port {
- public:
- Port(portNumBits num /* in host byte order */);
-
- portNumBits num() const // in network byte order
- { return fPortNum; }
-
- private:
- portNumBits fPortNum; // stored in network byte order
+public:
+ Port(portNumBits num /* in host byte order */);
+
+ portNumBits num() const { return fPortNum; } // in network byte order
+
+private:
+ portNumBits fPortNum; // stored in network byte order
#ifdef IRIX
- portNumBits filler; // hack to overcome a bug in IRIX C++ compiler
+ portNumBits filler; // hack to overcome a bug in IRIX C++ compiler
#endif
};
@@ -110,34 +109,32 @@ UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p);
// A generic table for looking up objects by (address1, address2, port)
class AddressPortLookupTable {
- public:
- AddressPortLookupTable();
- virtual ~AddressPortLookupTable();
-
- void* Add(netAddressBits address1, netAddressBits address2,
- Port port, void* value);
- // Returns the old value if different, otherwise 0
- Boolean Remove(netAddressBits address1, netAddressBits address2,
- Port port);
- void* Lookup(netAddressBits address1, netAddressBits address2,
- Port port);
- // Returns 0 if not found
-
- // Used to iterate through the entries in the table
- class Iterator {
- public:
- Iterator(AddressPortLookupTable& table);
- virtual ~Iterator();
-
- void* next(); // NULL iff none
-
- private:
- HashTable::Iterator* fIter;
- };
-
- private:
- friend class Iterator;
- HashTable* fTable;
+public:
+ AddressPortLookupTable();
+ virtual ~AddressPortLookupTable();
+
+ void* Add(netAddressBits address1, netAddressBits address2, Port port, void* value);
+ // Returns the old value if different, otherwise 0
+ Boolean Remove(netAddressBits address1, netAddressBits address2, Port port);
+ void* Lookup(netAddressBits address1, netAddressBits address2, Port port);
+ // Returns 0 if not found
+ void* RemoveNext() { return fTable->RemoveNext(); }
+
+ // Used to iterate through the entries in the table
+ class Iterator {
+ public:
+ Iterator(AddressPortLookupTable& table);
+ virtual ~Iterator();
+
+ void* next(); // NULL iff none
+
+ private:
+ HashTable::Iterator* fIter;
+ };
+
+private:
+ friend class Iterator;
+ HashTable* fTable;
};
diff --git a/groupsock/include/NetCommon.h b/groupsock/include/NetCommon.h
index d794e0e..eec71fd 100644
--- a/groupsock/include/NetCommon.h
+++ b/groupsock/include/NetCommon.h
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
/* "groupsock" interface
- * Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+ * Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
* Common include files, typically used for networking
*/
@@ -57,9 +57,15 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
/* Definitions of size-specific types: */
typedef __int64 int64_t;
typedef unsigned __int64 u_int64_t;
+
+typedef int int32_t;
typedef unsigned u_int32_t;
+
+typedef short int16_t;
typedef unsigned short u_int16_t;
+
typedef unsigned char u_int8_t;
+
// For "uintptr_t" and "intptr_t", we assume that if they're not already defined, then this must be
// an old, 32-bit version of Windows:
#if !defined(_MSC_STDINT_H_) && !defined(_UINTPTR_T_DEFINED) && !defined(_UINTPTR_T_DECLARED) && !defined(_UINTPTR_T)
diff --git a/groupsock/include/NetInterface.hh b/groupsock/include/NetInterface.hh
index b0ecc22..bb00b0b 100644
--- a/groupsock/include/NetInterface.hh
+++ b/groupsock/include/NetInterface.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Network Interfaces
// C++ header
diff --git a/groupsock/include/TunnelEncaps.hh b/groupsock/include/TunnelEncaps.hh
index 62efad5..3916f73 100644
--- a/groupsock/include/TunnelEncaps.hh
+++ b/groupsock/include/TunnelEncaps.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Encapsulation trailer for tunnels
// C++ header
diff --git a/groupsock/include/groupsock_version.hh b/groupsock/include/groupsock_version.hh
index cbf51d6..87659e0 100644
--- a/groupsock/include/groupsock_version.hh
+++ b/groupsock/include/groupsock_version.hh
@@ -1,10 +1,10 @@
// Version information for the "groupsock" library
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2015 Live Networks, Inc. All rights reserved.
#ifndef _GROUPSOCK_VERSION_HH
#define _GROUPSOCK_VERSION_HH
-#define GROUPSOCK_LIBRARY_VERSION_STRING "2014.01.13"
-#define GROUPSOCK_LIBRARY_VERSION_INT 1389571200
+#define GROUPSOCK_LIBRARY_VERSION_STRING "2015.12.22"
+#define GROUPSOCK_LIBRARY_VERSION_INT 1450742400
#endif
diff --git a/liveMedia/AACAudioMatroskaFileServerMediaSubsession.cpp b/liveMedia/AACAudioMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index d92f8e9..0000000
--- a/liveMedia/AACAudioMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an AAC audio track within a Matroska file.
-// Implementation
-
-#include "AACAudioMatroskaFileServerMediaSubsession.hh"
-#include "MPEG4GenericRTPSink.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-AACAudioMatroskaFileServerMediaSubsession* AACAudioMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new AACAudioMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-AACAudioMatroskaFileServerMediaSubsession
-::AACAudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber) {
- // The Matroska file's 'Codec Private' data is assumed to be the AAC configuration information.
- // Use this to generate a 'config string':
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
- fConfigStr = new char[2*track->codecPrivateSize + 1]; // 2 hex digits per byte, plus the trailing '\0'
- for (unsigned i = 0; i < track->codecPrivateSize; ++i) sprintf(&fConfigStr[2*i], "%02X", track->codecPrivate[i]);
-}
-
-AACAudioMatroskaFileServerMediaSubsession
-::~AACAudioMatroskaFileServerMediaSubsession() {
- delete[] fConfigStr;
-}
-
-float AACAudioMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void AACAudioMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- ((MatroskaDemuxedTrack*)inputSource)->seekToTime(seekNPT);
-}
-
-FramedSource* AACAudioMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- estBitrate = 96; // kbps, estimate
-
- return fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
-}
-
-RTPSink* AACAudioMatroskaFileServerMediaSubsession
-::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
- return MPEG4GenericRTPSink::createNew(envir(), rtpGroupsock,
- rtpPayloadTypeIfDynamic,
- track->samplingFrequency,
- "audio", "AAC-hbr", fConfigStr,
- track->numChannels);
-}
diff --git a/liveMedia/AACAudioMatroskaFileServerMediaSubsession.hh b/liveMedia/AACAudioMatroskaFileServerMediaSubsession.hh
deleted file mode 100644
index 23f4515..0000000
--- a/liveMedia/AACAudioMatroskaFileServerMediaSubsession.hh
+++ /dev/null
@@ -1,55 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an AAC audio track within a Matroska file.
-// C++ header
-
-#ifndef _AAC_AUDIO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _AAC_AUDIO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-
-#ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
-#include "FileServerMediaSubsession.hh"
-#endif
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#include "MatroskaFileServerDemux.hh"
-#endif
-
-class AACAudioMatroskaFileServerMediaSubsession: public FileServerMediaSubsession {
-public:
- static AACAudioMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
-
-private:
- AACAudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~AACAudioMatroskaFileServerMediaSubsession();
-
-private: // redefined virtual functions
- virtual float duration() const;
- virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
- virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
- unsigned& estBitrate);
- virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
-
-private:
- MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
- char* fConfigStr;
-};
-
-#endif
diff --git a/liveMedia/AC3AudioFileServerMediaSubsession.cpp b/liveMedia/AC3AudioFileServerMediaSubsession.cpp
index 8eb6e6b..208bc73 100644
--- a/liveMedia/AC3AudioFileServerMediaSubsession.cpp
+++ b/liveMedia/AC3AudioFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an AC3 audio file.
// Implementation
diff --git a/liveMedia/AC3AudioMatroskaFileServerMediaSubsession.cpp b/liveMedia/AC3AudioMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index 7b99a5a..0000000
--- a/liveMedia/AC3AudioMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an AC3 audio track within a Matroska file.
-// Implementation
-
-#include "AC3AudioMatroskaFileServerMediaSubsession.hh"
-#include "AC3AudioRTPSink.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-AC3AudioMatroskaFileServerMediaSubsession* AC3AudioMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new AC3AudioMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-AC3AudioMatroskaFileServerMediaSubsession
-::AC3AudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber) {
-}
-
-AC3AudioMatroskaFileServerMediaSubsession
-::~AC3AudioMatroskaFileServerMediaSubsession() {
-}
-
-float AC3AudioMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void AC3AudioMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- ((MatroskaDemuxedTrack*)inputSource)->seekToTime(seekNPT);
-}
-
-FramedSource* AC3AudioMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- estBitrate = 48; // kbps, estimate
-
- return fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
-}
-
-RTPSink* AC3AudioMatroskaFileServerMediaSubsession
-::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
- return AC3AudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, track->samplingFrequency);
-}
diff --git a/liveMedia/AC3AudioRTPSink.cpp b/liveMedia/AC3AudioRTPSink.cpp
index 871501c..f1e5c06 100644
--- a/liveMedia/AC3AudioRTPSink.cpp
+++ b/liveMedia/AC3AudioRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for AC3 audio
// Implementation
diff --git a/liveMedia/AC3AudioRTPSource.cpp b/liveMedia/AC3AudioRTPSource.cpp
index cf27299..d846d45 100644
--- a/liveMedia/AC3AudioRTPSource.cpp
+++ b/liveMedia/AC3AudioRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// AC3 Audio RTP Sources
// Implementation
diff --git a/liveMedia/AC3AudioStreamFramer.cpp b/liveMedia/AC3AudioStreamFramer.cpp
index dbb72de..5800040 100644
--- a/liveMedia/AC3AudioStreamFramer.cpp
+++ b/liveMedia/AC3AudioStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an AC3 audio elementary stream into frames
// Implementation
diff --git a/liveMedia/ADTSAudioFileServerMediaSubsession.cpp b/liveMedia/ADTSAudioFileServerMediaSubsession.cpp
index a3a1bd2..ca69949 100644
--- a/liveMedia/ADTSAudioFileServerMediaSubsession.cpp
+++ b/liveMedia/ADTSAudioFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an AAC audio file in ADTS format
// Implementation
diff --git a/liveMedia/ADTSAudioFileSource.cpp b/liveMedia/ADTSAudioFileSource.cpp
index 8c2fa04..1470d67 100644
--- a/liveMedia/ADTSAudioFileSource.cpp
+++ b/liveMedia/ADTSAudioFileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source object for AAC audio files in ADTS format
// Implementation
@@ -120,7 +120,7 @@ void ADTSAudioFileSource::doGetNextFrame() {
if (fread(headers, 1, sizeof headers, fFid) < sizeof headers
|| feof(fFid) || ferror(fFid)) {
// The input source has ended:
- handleClosure(this);
+ handleClosure();
return;
}
diff --git a/liveMedia/AMRAudioFileServerMediaSubsession.cpp b/liveMedia/AMRAudioFileServerMediaSubsession.cpp
index 4572b96..d12c49d 100644
--- a/liveMedia/AMRAudioFileServerMediaSubsession.cpp
+++ b/liveMedia/AMRAudioFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an AMR audio file.
// Implementation
diff --git a/liveMedia/AMRAudioFileSink.cpp b/liveMedia/AMRAudioFileSink.cpp
index 4a68569..f549201 100644
--- a/liveMedia/AMRAudioFileSink.cpp
+++ b/liveMedia/AMRAudioFileSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// AMR Audio File sinks
// Implementation
diff --git a/liveMedia/AMRAudioFileSource.cpp b/liveMedia/AMRAudioFileSource.cpp
index 6d020d9..23fff99 100644
--- a/liveMedia/AMRAudioFileSource.cpp
+++ b/liveMedia/AMRAudioFileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source object for AMR audio files (as defined in RFC 4867, section 5)
// Implementation
@@ -115,14 +115,14 @@ static unsigned short const frameSizeWideband[16] = {
// as we now do with ByteStreamFileSource. #####
void AMRAudioFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid)) {
- handleClosure(this);
+ handleClosure();
return;
}
// Begin by reading the 1-byte frame header (and checking it for validity)
while (1) {
if (fread(&fLastFrameHeader, 1, 1, fFid) < 1) {
- handleClosure(this);
+ handleClosure();
return;
}
if ((fLastFrameHeader&0x83) != 0) {
diff --git a/liveMedia/AMRAudioRTPSink.cpp b/liveMedia/AMRAudioRTPSink.cpp
index acabe93..44d975e 100644
--- a/liveMedia/AMRAudioRTPSink.cpp
+++ b/liveMedia/AMRAudioRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for AMR audio (RFC 4867)
// Implementation
diff --git a/liveMedia/AMRAudioRTPSource.cpp b/liveMedia/AMRAudioRTPSource.cpp
index 8bbd0a3..0f49d89 100644
--- a/liveMedia/AMRAudioRTPSource.cpp
+++ b/liveMedia/AMRAudioRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// AMR Audio RTP Sources (RFC 4867)
// Implementation
diff --git a/liveMedia/AMRAudioSource.cpp b/liveMedia/AMRAudioSource.cpp
index 8f4d978..4ef853a 100644
--- a/liveMedia/AMRAudioSource.cpp
+++ b/liveMedia/AMRAudioSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source object for AMR audio sources
// Implementation
diff --git a/liveMedia/AVIFileSink.cpp b/liveMedia/AVIFileSink.cpp
index 6c0e7fc..6ea3c22 100644
--- a/liveMedia/AVIFileSink.cpp
+++ b/liveMedia/AVIFileSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A sink that generates an AVI file from a composite media session
// Implementation
@@ -178,7 +178,7 @@ AVIFileSink::~AVIFileSink() {
MediaSubsessionIterator iter(fInputSession);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
- subsession->readSource()->stopGettingFrames();
+ if (subsession->readSource() != NULL) subsession->readSource()->stopGettingFrames();
AVISubsessionIOState* ioState
= (AVISubsessionIOState*)(subsession->miscPtr);
diff --git a/liveMedia/AudioRTPSink.cpp b/liveMedia/AudioRTPSink.cpp
index d053dd1..ea38ea3 100644
--- a/liveMedia/AudioRTPSink.cpp
+++ b/liveMedia/AudioRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTP sink for audio codecs (abstract base class)
// Implementation
diff --git a/liveMedia/Base64.cpp b/liveMedia/Base64.cpp
index 866f9d9..0e27ba5 100644
--- a/liveMedia/Base64.cpp
+++ b/liveMedia/Base64.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Base64 encoding and decoding
// implementation
diff --git a/liveMedia/BasicUDPSink.cpp b/liveMedia/BasicUDPSink.cpp
index 473801d..cfb41a3 100644
--- a/liveMedia/BasicUDPSink.cpp
+++ b/liveMedia/BasicUDPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simple UDP sink (i.e., without RTP or other headers added); one frame per packet
// Implementation
@@ -72,7 +72,7 @@ void BasicUDPSink::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedB
}
// Send the packet:
- fGS->output(envir(), fGS->ttl(), fOutputBuffer, frameSize);
+ fGS->output(envir(), fOutputBuffer, frameSize);
// Figure out the time at which the next packet should be sent, based
// on the duration of the payload that we just read:
diff --git a/liveMedia/BasicUDPSource.cpp b/liveMedia/BasicUDPSource.cpp
index 85d9a8c..47904b0 100644
--- a/liveMedia/BasicUDPSource.cpp
+++ b/liveMedia/BasicUDPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simple UDP source, where every UDP payload is a complete frame
// Implementation
diff --git a/liveMedia/BitVector.cpp b/liveMedia/BitVector.cpp
index 691c4f1..307ca10 100644
--- a/liveMedia/BitVector.cpp
+++ b/liveMedia/BitVector.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Bit Vector data structure
// Implementation
diff --git a/liveMedia/ByteStreamFileSource.cpp b/liveMedia/ByteStreamFileSource.cpp
index f21924e..c3d4efd 100644
--- a/liveMedia/ByteStreamFileSource.cpp
+++ b/liveMedia/ByteStreamFileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A file source that is a plain byte stream (rather than frames)
// Implementation
@@ -57,8 +57,11 @@ void ByteStreamFileSource::seekToByteAbsolute(u_int64_t byteNumber, u_int64_t nu
fLimitNumBytesToStream = fNumBytesToStream > 0;
}
-void ByteStreamFileSource::seekToByteRelative(int64_t offset) {
+void ByteStreamFileSource::seekToByteRelative(int64_t offset, u_int64_t numBytesToStream) {
SeekFile64(fFid, offset, SEEK_CUR);
+
+ fNumBytesToStream = numBytesToStream;
+ fLimitNumBytesToStream = fNumBytesToStream > 0;
}
void ByteStreamFileSource::seekToEnd() {
@@ -91,7 +94,7 @@ ByteStreamFileSource::~ByteStreamFileSource() {
void ByteStreamFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
- handleClosure(this);
+ handleClosure();
return;
}
@@ -142,7 +145,7 @@ void ByteStreamFileSource::doReadFromFile() {
}
#endif
if (fFrameSize == 0) {
- handleClosure(this);
+ handleClosure();
return;
}
fNumBytesToStream -= fFrameSize;
diff --git a/liveMedia/ByteStreamMemoryBufferSource.cpp b/liveMedia/ByteStreamMemoryBufferSource.cpp
index 5f311cd..3b8db17 100644
--- a/liveMedia/ByteStreamMemoryBufferSource.cpp
+++ b/liveMedia/ByteStreamMemoryBufferSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class for streaming data from a (static) memory buffer, as if it were a file.
// Implementation
@@ -56,7 +56,7 @@ void ByteStreamMemoryBufferSource::seekToByteAbsolute(u_int64_t byteNumber, u_in
fLimitNumBytesToStream = fNumBytesToStream > 0;
}
-void ByteStreamMemoryBufferSource::seekToByteRelative(int64_t offset) {
+void ByteStreamMemoryBufferSource::seekToByteRelative(int64_t offset, u_int64_t numBytesToStream) {
int64_t newIndex = fCurIndex + offset;
if (newIndex < 0) {
fCurIndex = 0;
@@ -64,11 +64,14 @@ void ByteStreamMemoryBufferSource::seekToByteRelative(int64_t offset) {
fCurIndex = (u_int64_t)offset;
if (fCurIndex > fBufferSize) fCurIndex = fBufferSize;
}
+
+ fNumBytesToStream = numBytesToStream;
+ fLimitNumBytesToStream = fNumBytesToStream > 0;
}
void ByteStreamMemoryBufferSource::doGetNextFrame() {
if (fCurIndex >= fBufferSize || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
- handleClosure(this);
+ handleClosure();
return;
}
diff --git a/liveMedia/ByteStreamMultiFileSource.cpp b/liveMedia/ByteStreamMultiFileSource.cpp
index 985a96a..ca8c704 100644
--- a/liveMedia/ByteStreamMultiFileSource.cpp
+++ b/liveMedia/ByteStreamMultiFileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source that consists of multiple byte-stream files, read sequentially
// Implementation
@@ -96,7 +96,7 @@ void ByteStreamMultiFileSource::doGetNextFrame() {
} while (0);
// An error occurred; consider ourselves closed:
- handleClosure(this);
+ handleClosure();
}
void ByteStreamMultiFileSource
diff --git a/liveMedia/DVVideoFileServerMediaSubsession.cpp b/liveMedia/DVVideoFileServerMediaSubsession.cpp
index d8e820b..9086e50 100644
--- a/liveMedia/DVVideoFileServerMediaSubsession.cpp
+++ b/liveMedia/DVVideoFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a DV video file.
// Implementation
@@ -88,3 +88,16 @@ void DVVideoFileServerMediaSubsession
fileSource->seekToByteAbsolute(seekByteNumber, numBytes);
}
}
+
+void DVVideoFileServerMediaSubsession
+::setStreamSourceDuration(FramedSource* inputSource, double streamDuration, u_int64_t& numBytes) {
+ // First, get the file source from "inputSource" (a framer):
+ DVVideoStreamFramer* framer = (DVVideoStreamFramer*)inputSource;
+ ByteStreamFileSource* fileSource = (ByteStreamFileSource*)(framer->inputSource());
+
+ // Then figure out how many bytes to limit the streaming to:
+ if (fFileDuration > 0.0) {
+ numBytes = (u_int64_t)(((int64_t)fFileSize*streamDuration)/fFileDuration);
+ fileSource->seekToByteRelative(0, numBytes);
+ }
+}
diff --git a/liveMedia/DVVideoRTPSink.cpp b/liveMedia/DVVideoRTPSink.cpp
index 12bd64d..22cde13 100644
--- a/liveMedia/DVVideoRTPSink.cpp
+++ b/liveMedia/DVVideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for DV video (RFC 3189)
// (Thanks to Ben Hutchings for prototyping this.)
// Implementation
diff --git a/liveMedia/DVVideoRTPSource.cpp b/liveMedia/DVVideoRTPSource.cpp
index ebfc2f3..2cffda9 100644
--- a/liveMedia/DVVideoRTPSource.cpp
+++ b/liveMedia/DVVideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// DV Video RTP Sources
// Implementation
diff --git a/liveMedia/DVVideoStreamFramer.cpp b/liveMedia/DVVideoStreamFramer.cpp
index 0a3d719..53a0b74 100644
--- a/liveMedia/DVVideoStreamFramer.cpp
+++ b/liveMedia/DVVideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that parses a DV input stream into DV frames to deliver to the downstream object
// Implementation
// (Thanks to Ben Hutchings for his help, including a prototype implementation.)
diff --git a/liveMedia/DarwinInjector.cpp b/liveMedia/DarwinInjector.cpp
deleted file mode 100644
index 758a188..0000000
--- a/liveMedia/DarwinInjector.cpp
+++ /dev/null
@@ -1,349 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// An object that redirects one or more RTP/RTCP streams - forming a single
-// multimedia session - into a 'Darwin Streaming Server' (for subsequent
-// reflection to potentially arbitrarily many remote RTSP clients).
-// Implementation
-
-#include "DarwinInjector.hh"
-#include <GroupsockHelper.hh>
-
-////////// SubstreamDescriptor definition //////////
-
-class SubstreamDescriptor {
-public:
- SubstreamDescriptor(RTPSink* rtpSink, RTCPInstance* rtcpInstance, unsigned trackId);
- ~SubstreamDescriptor();
-
- SubstreamDescriptor*& next() { return fNext; }
- RTPSink* rtpSink() const { return fRTPSink; }
- RTCPInstance* rtcpInstance() const { return fRTCPInstance; }
- char const* sdpLines() const { return fSDPLines; }
-
-private:
- SubstreamDescriptor* fNext;
- RTPSink* fRTPSink;
- RTCPInstance* fRTCPInstance;
- char* fSDPLines;
-};
-
-
-////////// DarwinInjector implementation //////////
-
-DarwinInjector* DarwinInjector::createNew(UsageEnvironment& env,
- char const* applicationName,
- int verbosityLevel) {
- return new DarwinInjector(env, applicationName, verbosityLevel);
-}
-
-Boolean DarwinInjector::lookupByName(UsageEnvironment& env, char const* name,
- DarwinInjector*& result) {
- result = NULL; // unless we succeed
-
- Medium* medium;
- if (!Medium::lookupByName(env, name, medium)) return False;
-
- if (!medium->isDarwinInjector()) {
- env.setResultMsg(name, " is not a 'Darwin injector'");
- return False;
- }
-
- result = (DarwinInjector*)medium;
- return True;
-}
-
-DarwinInjector::DarwinInjector(UsageEnvironment& env,
- char const* applicationName, int verbosityLevel)
- : Medium(env),
- fApplicationName(strDup(applicationName)), fVerbosityLevel(verbosityLevel),
- fRTSPClient(NULL), fSubstreamSDPSizes(0),
- fHeadSubstream(NULL), fTailSubstream(NULL), fSession(NULL), fLastTrackId(0), fResultString(NULL) {
-}
-
-DarwinInjector::~DarwinInjector() {
- if (fSession != NULL) { // close down and delete the session
- fRTSPClient->sendTeardownCommand(*fSession, NULL);
- Medium::close(fSession);
- }
-
- delete fHeadSubstream;
- delete[] (char*)fApplicationName;
- Medium::close(fRTSPClient);
-}
-
-void DarwinInjector::addStream(RTPSink* rtpSink, RTCPInstance* rtcpInstance) {
- if (rtpSink == NULL) return; // "rtpSink" should be non-NULL
-
- SubstreamDescriptor* newDescriptor = new SubstreamDescriptor(rtpSink, rtcpInstance, ++fLastTrackId);
- if (fHeadSubstream == NULL) {
- fHeadSubstream = fTailSubstream = newDescriptor;
- } else {
- fTailSubstream->next() = newDescriptor;
- fTailSubstream = newDescriptor;
- }
-
- fSubstreamSDPSizes += strlen(newDescriptor->sdpLines());
-}
-
-// Define a special subclass of "RTSPClient" that has a pointer field to a "DarwinInjector". We'll use this to implement RTSP ops:
-class RTSPClientForDarwinInjector: public RTSPClient {
-public:
- RTSPClientForDarwinInjector(UsageEnvironment& env, char const* rtspURL, int verbosityLevel, char const* applicationName,
- DarwinInjector* ourDarwinInjector)
- : RTSPClient(env, rtspURL, verbosityLevel, applicationName, 0, -1),
- fOurDarwinInjector(ourDarwinInjector) {}
- virtual ~RTSPClientForDarwinInjector() {}
- DarwinInjector* fOurDarwinInjector;
-};
-
-Boolean DarwinInjector
-::setDestination(char const* remoteRTSPServerNameOrAddress,
- char const* remoteFileName,
- char const* sessionName,
- char const* sessionInfo,
- portNumBits remoteRTSPServerPortNumber,
- char const* remoteUserName,
- char const* remotePassword,
- char const* sessionAuthor,
- char const* sessionCopyright,
- int timeout) {
- char* sdp = NULL;
- char* url = NULL;
- Boolean success = False; // until we learn otherwise
-
- do {
- // Construct a RTSP URL for the remote stream:
- char const* const urlFmt = "rtsp://%s:%u/%s";
- unsigned urlLen
- = strlen(urlFmt) + strlen(remoteRTSPServerNameOrAddress) + 5 /* max short len */ + strlen(remoteFileName);
- url = new char[urlLen];
- sprintf(url, urlFmt, remoteRTSPServerNameOrAddress, remoteRTSPServerPortNumber, remoteFileName);
-
- // Begin by creating our RTSP client object:
- fRTSPClient = new RTSPClientForDarwinInjector(envir(), url, fVerbosityLevel, fApplicationName, this);
- if (fRTSPClient == NULL) break;
-
- // Get the remote RTSP server's IP address:
- struct in_addr addr;
- {
- NetAddressList addresses(remoteRTSPServerNameOrAddress);
- if (addresses.numAddresses() == 0) break;
- NetAddress const* address = addresses.firstAddress();
- addr.s_addr = *(unsigned*)(address->data());
- }
- AddressString remoteRTSPServerAddressStr(addr);
-
- // Construct a SDP description for the session that we'll be streaming:
- char const* const sdpFmt =
- "v=0\r\n"
- "o=- %u %u IN IP4 127.0.0.1\r\n"
- "s=%s\r\n"
- "i=%s\r\n"
- "c=IN IP4 %s\r\n"
- "t=0 0\r\n"
- "a=x-qt-text-nam:%s\r\n"
- "a=x-qt-text-inf:%s\r\n"
- "a=x-qt-text-cmt:source application:%s\r\n"
- "a=x-qt-text-aut:%s\r\n"
- "a=x-qt-text-cpy:%s\r\n";
- // plus, %s for each substream SDP
- unsigned sdpLen = strlen(sdpFmt)
- + 20 /* max int len */ + 20 /* max int len */
- + strlen(sessionName)
- + strlen(sessionInfo)
- + strlen(remoteRTSPServerAddressStr.val())
- + strlen(sessionName)
- + strlen(sessionInfo)
- + strlen(fApplicationName)
- + strlen(sessionAuthor)
- + strlen(sessionCopyright)
- + fSubstreamSDPSizes;
- unsigned const sdpSessionId = our_random32();
- unsigned const sdpVersion = sdpSessionId;
- sdp = new char[sdpLen];
- sprintf(sdp, sdpFmt,
- sdpSessionId, sdpVersion, // o= line
- sessionName, // s= line
- sessionInfo, // i= line
- remoteRTSPServerAddressStr.val(), // c= line
- sessionName, // a=x-qt-text-nam: line
- sessionInfo, // a=x-qt-text-inf: line
- fApplicationName, // a=x-qt-text-cmt: line
- sessionAuthor, // a=x-qt-text-aut: line
- sessionCopyright // a=x-qt-text-cpy: line
- );
- char* p = &sdp[strlen(sdp)];
- SubstreamDescriptor* ss;
- for (ss = fHeadSubstream; ss != NULL; ss = ss->next()) {
- sprintf(p, "%s", ss->sdpLines());
- p += strlen(p);
- }
-
- // Do a RTSP "ANNOUNCE" with this SDP description:
- Authenticator auth;
- Authenticator* authToUse = NULL;
- if (remoteUserName[0] != '\0' || remotePassword[0] != '\0') {
- auth.setUsernameAndPassword(remoteUserName, remotePassword);
- authToUse = &auth;
- }
- fWatchVariable = 0;
- (void)fRTSPClient->sendAnnounceCommand(sdp, genericResponseHandler, authToUse);
-
- // Now block (but handling events) until we get a response:
- envir().taskScheduler().doEventLoop(&fWatchVariable);
-
- delete[] fResultString;
- if (fResultCode != 0) break; // an error occurred with the RTSP "ANNOUNCE" command
-
- // Next, tell the remote server to start receiving the stream from us.
- // (To do this, we first create a "MediaSession" object from the SDP description.)
- fSession = MediaSession::createNew(envir(), sdp);
- if (fSession == NULL) break;
-
- ss = fHeadSubstream;
- MediaSubsessionIterator iter(*fSession);
- MediaSubsession* subsession;
- ss = fHeadSubstream;
- unsigned streamChannelId = 0;
- while ((subsession = iter.next()) != NULL) {
- if (!subsession->initiate()) break;
-
- fWatchVariable = 0;
- (void)fRTSPClient->sendSetupCommand(*subsession, genericResponseHandler,
- True /*streamOutgoing*/,
- True /*streamUsingTCP*/);
- // Now block (but handling events) until we get a response:
- envir().taskScheduler().doEventLoop(&fWatchVariable);
-
- delete[] fResultString;
- if (fResultCode != 0) break; // an error occurred with the RTSP "SETUP" command
-
- // Tell this subsession's RTPSink and RTCPInstance to use
- // the RTSP TCP connection:
- ss->rtpSink()->setStreamSocket(fRTSPClient->socketNum(), streamChannelId++);
- if (ss->rtcpInstance() != NULL) {
- ss->rtcpInstance()->setStreamSocket(fRTSPClient->socketNum(),
- streamChannelId++);
- }
- ss = ss->next();
- }
- if (subsession != NULL) break; // an error occurred above
-
- // Tell the RTSP server to start:
- fWatchVariable = 0;
- (void)fRTSPClient->sendPlayCommand(*fSession, genericResponseHandler);
-
- // Now block (but handling events) until we get a response:
- envir().taskScheduler().doEventLoop(&fWatchVariable);
-
- delete[] fResultString;
- if (fResultCode != 0) break; // an error occurred with the RTSP "PLAY" command
-
- // Finally, make sure that the output TCP buffer is a reasonable size:
- increaseSendBufferTo(envir(), fRTSPClient->socketNum(), 100*1024);
-
- success = True;
- } while (0);
-
- delete[] sdp;
- delete[] url;
- return success;
-}
-
-Boolean DarwinInjector::isDarwinInjector() const {
- return True;
-}
-
-void DarwinInjector::genericResponseHandler(RTSPClient* rtspClient, int responseCode, char* responseString) {
- DarwinInjector* di = ((RTSPClientForDarwinInjector*)rtspClient)-> fOurDarwinInjector;
- di->genericResponseHandler1(responseCode, responseString);
-}
-
-void DarwinInjector::genericResponseHandler1(int responseCode, char* responseString) {
- // Set result values:
- fResultCode = responseCode;
- fResultString = responseString;
-
- // Signal a break from the event loop (thereby returning from the blocking command):
- fWatchVariable = ~0;
-}
-
-////////// SubstreamDescriptor implementation //////////
-
-SubstreamDescriptor::SubstreamDescriptor(RTPSink* rtpSink,
- RTCPInstance* rtcpInstance, unsigned trackId)
- : fNext(NULL), fRTPSink(rtpSink), fRTCPInstance(rtcpInstance) {
- // Create the SDP description for this substream
- char const* mediaType = fRTPSink->sdpMediaType();
- unsigned char rtpPayloadType = fRTPSink->rtpPayloadType();
- char const* rtpPayloadFormatName = fRTPSink->rtpPayloadFormatName();
- unsigned rtpTimestampFrequency = fRTPSink->rtpTimestampFrequency();
- unsigned numChannels = fRTPSink->numChannels();
- char* rtpmapLine;
- if (rtpPayloadType >= 96) {
- char* encodingParamsPart;
- if (numChannels != 1) {
- encodingParamsPart = new char[1 + 20 /* max int len */];
- sprintf(encodingParamsPart, "/%d", numChannels);
- } else {
- encodingParamsPart = strDup("");
- }
- char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n";
- unsigned rtpmapFmtSize = strlen(rtpmapFmt)
- + 3 /* max char len */ + strlen(rtpPayloadFormatName)
- + 20 /* max int len */ + strlen(encodingParamsPart);
- rtpmapLine = new char[rtpmapFmtSize];
- sprintf(rtpmapLine, rtpmapFmt,
- rtpPayloadType, rtpPayloadFormatName,
- rtpTimestampFrequency, encodingParamsPart);
- delete[] encodingParamsPart;
- } else {
- // Static payload type => no "a=rtpmap:" line
- rtpmapLine = strDup("");
- }
- unsigned rtpmapLineSize = strlen(rtpmapLine);
- char const* auxSDPLine = fRTPSink->auxSDPLine();
- if (auxSDPLine == NULL) auxSDPLine = "";
- unsigned auxSDPLineSize = strlen(auxSDPLine);
-
- char const* const sdpFmt =
- "m=%s 0 RTP/AVP %u\r\n"
- "%s" // "a=rtpmap:" line (if present)
- "%s" // auxilliary (e.g., "a=fmtp:") line (if present)
- "a=control:trackID=%u\r\n";
- unsigned sdpFmtSize = strlen(sdpFmt)
- + strlen(mediaType) + 3 /* max char len */
- + rtpmapLineSize
- + auxSDPLineSize
- + 20 /* max int len */;
- char* sdpLines = new char[sdpFmtSize];
- sprintf(sdpLines, sdpFmt,
- mediaType, // m= <media>
- rtpPayloadType, // m= <fmt list>
- rtpmapLine, // a=rtpmap:... (if present)
- auxSDPLine, // optional extra SDP line
- trackId); // a=control:<track-id>
- fSDPLines = strDup(sdpLines);
- delete[] sdpLines;
- delete[] rtpmapLine;
-}
-
-SubstreamDescriptor::~SubstreamDescriptor() {
- delete fSDPLines;
- delete fNext;
-}
diff --git a/liveMedia/DeviceSource.cpp b/liveMedia/DeviceSource.cpp
index cfb0835..87f6a8a 100644
--- a/liveMedia/DeviceSource.cpp
+++ b/liveMedia/DeviceSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A template for a MediaSource encapsulating an audio/video input device
//
// NOTE: Sections of this code labeled "%%% TO BE WRITTEN %%%" are incomplete, and need to be written by the programmer
@@ -80,7 +80,7 @@ void DeviceSource::doGetNextFrame() {
// Note: If, for some reason, the source device stops being readable (e.g., it gets closed), then you do the following:
if (0 /* the source stops being readable */ /*%%% TO BE WRITTEN %%%*/) {
- handleClosure(this);
+ handleClosure();
return;
}
diff --git a/liveMedia/DigestAuthentication.cpp b/liveMedia/DigestAuthentication.cpp
index 06a09bb..a92eb26 100644
--- a/liveMedia/DigestAuthentication.cpp
+++ b/liveMedia/DigestAuthentication.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class used for digest authentication.
// Implementation
@@ -48,6 +48,19 @@ Authenticator& Authenticator::operator=(const Authenticator& rightSide) {
return *this;
}
+Boolean Authenticator::operator<(const Authenticator* rightSide) {
+ // Returns True if "rightSide" is 'newer' than us:
+ if (rightSide != NULL && rightSide != this &&
+ (rightSide->realm() != NULL || rightSide->nonce() != NULL ||
+ username() == NULL || password() == NULL ||
+ strcmp(rightSide->username(), username()) != 0 ||
+ strcmp(rightSide->password(), password()) != 0)) {
+ return True;
+ }
+
+ return False;
+}
+
Authenticator::~Authenticator() {
reset();
}
@@ -145,6 +158,9 @@ void Authenticator::assignRealmAndNonce(char const* realm, char const* nonce) {
}
void Authenticator::assignUsernameAndPassword(char const* username, char const* password, Boolean passwordIsMD5) {
+ if (username == NULL) username = "";
+ if (password == NULL) password = "";
+
fUsername = strDup(username);
fPassword = strDup(password);
fPasswordIsMD5 = passwordIsMD5;
diff --git a/liveMedia/EBMLNumber.cpp b/liveMedia/EBMLNumber.cpp
index 577f977..62651ba 100644
--- a/liveMedia/EBMLNumber.cpp
+++ b/liveMedia/EBMLNumber.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// EBML numbers (ids and sizes)
// Implementation
@@ -135,6 +135,9 @@ char const* EBMLId::stringName() const {
case MATROSKA_ID_CUE_CLUSTER_POSITION: { return "Cue Cluster Position"; }
case MATROSKA_ID_CUE_BLOCK_NUMBER: { return "Cue Block Number"; }
case MATROSKA_ID_TAGS: { return "Tags"; }
+ case MATROSKA_ID_SEEK_PRE_ROLL: { return "SeekPreRoll"; }
+ case MATROSKA_ID_CODEC_DELAY: { return "CodecDelay"; }
+ case MATROSKA_ID_DISCARD_PADDING: { return "DiscardPadding"; }
default: { return "*****unknown*****"; }
}
}
diff --git a/liveMedia/EBMLNumber.hh b/liveMedia/EBMLNumber.hh
index 3e1fd6f..c8fa46c 100644
--- a/liveMedia/EBMLNumber.hh
+++ b/liveMedia/EBMLNumber.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// EBML numbers (ids and sizes)
// C++ header
@@ -121,6 +121,9 @@ public:
#define MATROSKA_ID_CUE_CLUSTER_POSITION 0xF1
#define MATROSKA_ID_CUE_BLOCK_NUMBER 0x5378
#define MATROSKA_ID_TAGS 0x1254C367
+#define MATROSKA_ID_SEEK_PRE_ROLL 0x56BB
+#define MATROSKA_ID_CODEC_DELAY 0x56AA
+#define MATROSKA_ID_DISCARD_PADDING 0x75A2
class EBMLId: public EBMLNumber {
public:
diff --git a/liveMedia/FileServerMediaSubsession.cpp b/liveMedia/FileServerMediaSubsession.cpp
index 5404919..4cc5383 100644
--- a/liveMedia/FileServerMediaSubsession.cpp
+++ b/liveMedia/FileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a file.
// Implementation
diff --git a/liveMedia/FileSink.cpp b/liveMedia/FileSink.cpp
index 04ae66f..0986d55 100644
--- a/liveMedia/FileSink.cpp
+++ b/liveMedia/FileSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// File sinks
// Implementation
diff --git a/liveMedia/FramedFileSource.cpp b/liveMedia/FramedFileSource.cpp
index 1f21646..7594942 100644
--- a/liveMedia/FramedFileSource.cpp
+++ b/liveMedia/FramedFileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Framed File Sources
// Implementation
diff --git a/liveMedia/FramedFilter.cpp b/liveMedia/FramedFilter.cpp
index fa1ae66..e16c000 100644
--- a/liveMedia/FramedFilter.cpp
+++ b/liveMedia/FramedFilter.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Framed Filters
// Implementation
diff --git a/liveMedia/FramedSource.cpp b/liveMedia/FramedSource.cpp
index 30a59a4..02e583c 100644
--- a/liveMedia/FramedSource.cpp
+++ b/liveMedia/FramedSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Framed Sources
// Implementation
@@ -94,9 +94,13 @@ void FramedSource::afterGetting(FramedSource* source) {
void FramedSource::handleClosure(void* clientData) {
FramedSource* source = (FramedSource*)clientData;
- source->fIsCurrentlyAwaitingData = False; // because we got a close instead
- if (source->fOnCloseFunc != NULL) {
- (*(source->fOnCloseFunc))(source->fOnCloseClientData);
+ source->handleClosure();
+}
+
+void FramedSource::handleClosure() {
+ fIsCurrentlyAwaitingData = False; // because we got a close instead
+ if (fOnCloseFunc != NULL) {
+ (*fOnCloseFunc)(fOnCloseClientData);
}
}
diff --git a/liveMedia/GSMAudioRTPSink.cpp b/liveMedia/GSMAudioRTPSink.cpp
index aa81693..d2c71ef 100644
--- a/liveMedia/GSMAudioRTPSink.cpp
+++ b/liveMedia/GSMAudioRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for GSM audio
// Implementation
diff --git a/liveMedia/GenericMediaServer.cpp b/liveMedia/GenericMediaServer.cpp
new file mode 100644
index 0000000..c063472
--- /dev/null
+++ b/liveMedia/GenericMediaServer.cpp
@@ -0,0 +1,396 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A generic media server class, used to implement a RTSP server, and any other server that uses
+// "ServerMediaSession" objects to describe media to be served.
+// Implementation
+
+#include "GenericMediaServer.hh"
+#include <GroupsockHelper.hh>
+#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
+#define snprintf _snprintf
+#endif
+
+////////// GenericMediaServer implementation //////////
+
+void GenericMediaServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
+ if (serverMediaSession == NULL) return;
+
+ char const* sessionName = serverMediaSession->streamName();
+ if (sessionName == NULL) sessionName = "";
+ removeServerMediaSession(sessionName); // in case an existing "ServerMediaSession" with this name already exists
+
+ fServerMediaSessions->Add(sessionName, (void*)serverMediaSession);
+}
+
+ServerMediaSession* GenericMediaServer
+::lookupServerMediaSession(char const* streamName, Boolean /*isFirstLookupInSession*/) {
+ // Default implementation:
+ return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
+}
+
+void GenericMediaServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) {
+ if (serverMediaSession == NULL) return;
+
+ fServerMediaSessions->Remove(serverMediaSession->streamName());
+ if (serverMediaSession->referenceCount() == 0) {
+ Medium::close(serverMediaSession);
+ } else {
+ serverMediaSession->deleteWhenUnreferenced() = True;
+ }
+}
+
+void GenericMediaServer::removeServerMediaSession(char const* streamName) {
+ removeServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
+}
+
+void GenericMediaServer::closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession) {
+ if (serverMediaSession == NULL) return;
+
+ HashTable::Iterator* iter = HashTable::Iterator::create(*fClientSessions);
+ GenericMediaServer::ClientSession* clientSession;
+ char const* key; // dummy
+ while ((clientSession = (GenericMediaServer::ClientSession*)(iter->next(key))) != NULL) {
+ if (clientSession->fOurServerMediaSession == serverMediaSession) {
+ delete clientSession;
+ }
+ }
+ delete iter;
+}
+
+void GenericMediaServer::closeAllClientSessionsForServerMediaSession(char const* streamName) {
+ closeAllClientSessionsForServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
+}
+
+void GenericMediaServer::deleteServerMediaSession(ServerMediaSession* serverMediaSession) {
+ if (serverMediaSession == NULL) return;
+
+ closeAllClientSessionsForServerMediaSession(serverMediaSession);
+ removeServerMediaSession(serverMediaSession);
+}
+
+void GenericMediaServer::deleteServerMediaSession(char const* streamName) {
+ deleteServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
+}
+
+GenericMediaServer
+::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort,
+ unsigned reclamationSeconds)
+ : Medium(env),
+ fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds),
+ fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
+ fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
+ fClientSessions(HashTable::create(STRING_HASH_KEYS)) {
+ ignoreSigPipeOnSocket(fServerSocket); // so that clients on the same host that are killed don't also kill us
+
+ // Arrange to handle connections from others:
+ env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);
+}
+
+GenericMediaServer::~GenericMediaServer() {
+ // Turn off background read handling:
+ envir().taskScheduler().turnOffBackgroundReadHandling(fServerSocket);
+ ::closeSocket(fServerSocket);
+}
+
+void GenericMediaServer::cleanup() {
+ // This member function must be called in the destructor of any subclass of
+ //"GenericMediaServer". (We don't call this in the destructor of "GenericMediaServer" itself,
+ // because by that time, the subclass destructor will already have been called, and this may
+ // affect (break) the destruction of the "ClientSession" and "ClientConnection" objects, which
+ // themselves will have been subclassed.)
+
+ // Close all client session objects:
+ GenericMediaServer::ClientSession* clientSession;
+ while ((clientSession = (GenericMediaServer::ClientSession*)fClientSessions->getFirst()) != NULL) {
+ delete clientSession;
+ }
+ delete fClientSessions;
+
+ // Close all client connection objects:
+ GenericMediaServer::ClientConnection* connection;
+ while ((connection = (GenericMediaServer::ClientConnection*)fClientConnections->getFirst()) != NULL) {
+ delete connection;
+ }
+ delete fClientConnections;
+
+ // Delete all server media sessions
+ ServerMediaSession* serverMediaSession;
+ while ((serverMediaSession = (ServerMediaSession*)fServerMediaSessions->getFirst()) != NULL) {
+ removeServerMediaSession(serverMediaSession); // will delete it, because it no longer has any 'client session' objects using it
+ }
+ delete fServerMediaSessions;
+}
+
+#define LISTEN_BACKLOG_SIZE 20
+
+int GenericMediaServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
+ int ourSocket = -1;
+
+ do {
+ // The following statement is enabled by default.
+ // Don't disable it (by defining ALLOW_SERVER_PORT_REUSE) unless you know what you're doing.
+#if !defined(ALLOW_SERVER_PORT_REUSE) && !defined(ALLOW_RTSP_SERVER_PORT_REUSE)
+ // ALLOW_RTSP_SERVER_PORT_REUSE is for backwards-compatibility #####
+ NoReuse dummy(env); // Don't use this socket if there's already a local server using it
+#endif
+
+ ourSocket = setupStreamSocket(env, ourPort);
+ if (ourSocket < 0) break;
+
+ // Make sure we have a big send buffer:
+ if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
+
+ // Allow multiple simultaneous connections:
+ if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
+ env.setResultErrMsg("listen() failed: ");
+ break;
+ }
+
+ if (ourPort.num() == 0) {
+ // bind() will have chosen a port for us; return it also:
+ if (!getSourcePort(env, ourSocket, ourPort)) break;
+ }
+
+ return ourSocket;
+ } while (0);
+
+ if (ourSocket != -1) ::closeSocket(ourSocket);
+ return -1;
+}
+
+void GenericMediaServer::incomingConnectionHandler(void* instance, int /*mask*/) {
+ GenericMediaServer* server = (GenericMediaServer*)instance;
+ server->incomingConnectionHandler();
+}
+void GenericMediaServer::incomingConnectionHandler() {
+ incomingConnectionHandlerOnSocket(fServerSocket);
+}
+
+void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {
+ struct sockaddr_in clientAddr;
+ SOCKLEN_T clientAddrLen = sizeof clientAddr;
+ int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
+ if (clientSocket < 0) {
+ int err = envir().getErrno();
+ if (err != EWOULDBLOCK) {
+ envir().setResultErrMsg("accept() failed: ");
+ }
+ return;
+ }
+ ignoreSigPipeOnSocket(clientSocket); // so that clients on the same host that are killed don't also kill us
+ makeSocketNonBlocking(clientSocket);
+ increaseSendBufferTo(envir(), clientSocket, 50*1024);
+
+#ifdef DEBUG
+ envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
+#endif
+
+ // Create a new object for handling this connection:
+ (void)createNewClientConnection(clientSocket, clientAddr);
+}
+
+
+////////// GenericMediaServer::ClientConnection implementation //////////
+
+GenericMediaServer::ClientConnection
+::ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
+ : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) {
+ // Add ourself to our 'client connections' table:
+ fOurServer.fClientConnections->Add((char const*)this, this);
+
+ // Arrange to handle incoming requests:
+ resetRequestBuffer();
+ envir().taskScheduler()
+ .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
+}
+
+GenericMediaServer::ClientConnection::~ClientConnection() {
+ // Remove ourself from the server's 'client connections' hash table before we go:
+ fOurServer.fClientConnections->Remove((char const*)this);
+
+ closeSockets();
+}
+
+void GenericMediaServer::ClientConnection::closeSockets() {
+ // Turn off background handling on our socket:
+ envir().taskScheduler().disableBackgroundHandling(fOurSocket);
+ ::closeSocket(fOurSocket);
+
+ fOurSocket = -1;
+}
+
+void GenericMediaServer::ClientConnection::incomingRequestHandler(void* instance, int /*mask*/) {
+ ClientConnection* connection = (ClientConnection*)instance;
+ connection->incomingRequestHandler();
+}
+
+void GenericMediaServer::ClientConnection::incomingRequestHandler() {
+ struct sockaddr_in dummy; // 'from' address, meaningless in this case
+
+ int bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
+ handleRequestBytes(bytesRead);
+}
+
+void GenericMediaServer::ClientConnection::resetRequestBuffer() {
+ fRequestBytesAlreadySeen = 0;
+ fRequestBufferBytesLeft = sizeof fRequestBuffer;
+}
+
+
+////////// GenericMediaServer::ClientSession implementation //////////
+
+GenericMediaServer::ClientSession
+::ClientSession(GenericMediaServer& ourServer, u_int32_t sessionId)
+ : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL),
+ fLivenessCheckTask(NULL) {
+ noteLiveness();
+}
+
+GenericMediaServer::ClientSession::~ClientSession() {
+ // Turn off any liveness checking:
+ envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
+
+ // Remove ourself from the server's 'client sessions' hash table before we go:
+ char sessionIdStr[8+1];
+ sprintf(sessionIdStr, "%08X", fOurSessionId);
+ fOurServer.fClientSessions->Remove(sessionIdStr);
+
+ if (fOurServerMediaSession != NULL) {
+ fOurServerMediaSession->decrementReferenceCount();
+ if (fOurServerMediaSession->referenceCount() == 0
+ && fOurServerMediaSession->deleteWhenUnreferenced()) {
+ fOurServer.removeServerMediaSession(fOurServerMediaSession);
+ fOurServerMediaSession = NULL;
+ }
+ }
+}
+
+void GenericMediaServer::ClientSession::noteLiveness() {
+#ifdef DEBUG
+ char const* streamName
+ = (fOurServerMediaSession == NULL) ? "???" : fOurServerMediaSession->streamName();
+ fprintf(stderr, "Client session (id \"%08X\", stream name \"%s\"): Liveness indication\n",
+ fOurSessionId, streamName);
+#endif
+ if (fOurServer.fReclamationSeconds > 0) {
+ envir().taskScheduler().rescheduleDelayedTask(fLivenessCheckTask,
+ fOurServer.fReclamationSeconds*1000000,
+ (TaskFunc*)livenessTimeoutTask, this);
+ }
+}
+
+void GenericMediaServer::ClientSession::noteClientLiveness(ClientSession* clientSession) {
+ clientSession->noteLiveness();
+}
+
+void GenericMediaServer::ClientSession::livenessTimeoutTask(ClientSession* clientSession) {
+ // If this gets called, the client session is assumed to have timed out, so delete it:
+#ifdef DEBUG
+ char const* streamName
+ = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
+ fprintf(stderr, "Client session (id \"%08X\", stream name \"%s\") has timed out (due to inactivity)\n",
+ clientSession->fOurSessionId, streamName);
+#endif
+ delete clientSession;
+}
+
+GenericMediaServer::ClientSession* GenericMediaServer::createNewClientSessionWithId() {
+ u_int32_t sessionId;
+ char sessionIdStr[8+1];
+
+ // Choose a random (unused) 32-bit integer for the session id
+ // (it will be encoded as a 8-digit hex number). (We avoid choosing session id 0,
+ // because that has a special use by some servers.)
+ do {
+ sessionId = (u_int32_t)our_random32();
+ snprintf(sessionIdStr, sizeof sessionIdStr, "%08X", sessionId);
+ } while (sessionId == 0 || lookupClientSession(sessionIdStr) != NULL);
+
+ ClientSession* clientSession = createNewClientSession(sessionId);
+ fClientSessions->Add(sessionIdStr, clientSession);
+
+ return clientSession;
+}
+
+GenericMediaServer::ClientSession*
+GenericMediaServer::lookupClientSession(u_int32_t sessionId) {
+ char sessionIdStr[8+1];
+ snprintf(sessionIdStr, sizeof sessionIdStr, "%08X", sessionId);
+ return lookupClientSession(sessionIdStr);
+}
+
+GenericMediaServer::ClientSession*
+GenericMediaServer::lookupClientSession(char const* sessionIdStr) {
+ return (GenericMediaServer::ClientSession*)fClientSessions->Lookup(sessionIdStr);
+}
+
+
+////////// ServerMediaSessionIterator implementation //////////
+
+GenericMediaServer::ServerMediaSessionIterator
+::ServerMediaSessionIterator(GenericMediaServer& server)
+ : fOurIterator((server.fServerMediaSessions == NULL)
+ ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) {
+}
+
+GenericMediaServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() {
+ delete fOurIterator;
+}
+
+ServerMediaSession* GenericMediaServer::ServerMediaSessionIterator::next() {
+ if (fOurIterator == NULL) return NULL;
+
+ char const* key; // dummy
+ return (ServerMediaSession*)(fOurIterator->next(key));
+}
+
+
+////////// UserAuthenticationDatabase implementation //////////
+
+UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm,
+ Boolean passwordsAreMD5)
+ : fTable(HashTable::create(STRING_HASH_KEYS)),
+ fRealm(strDup(realm == NULL ? "LIVE555 Streaming Media" : realm)),
+ fPasswordsAreMD5(passwordsAreMD5) {
+}
+
+UserAuthenticationDatabase::~UserAuthenticationDatabase() {
+ delete[] fRealm;
+
+ // Delete the allocated 'password' strings that we stored in the table, and then the table itself:
+ char* password;
+ while ((password = (char*)fTable->RemoveNext()) != NULL) {
+ delete[] password;
+ }
+ delete fTable;
+}
+
+void UserAuthenticationDatabase::addUserRecord(char const* username,
+ char const* password) {
+ fTable->Add(username, (void*)(strDup(password)));
+}
+
+void UserAuthenticationDatabase::removeUserRecord(char const* username) {
+ char* password = (char*)(fTable->Lookup(username));
+ fTable->Remove(username);
+ delete[] password;
+}
+
+char const* UserAuthenticationDatabase::lookupPassword(char const* username) {
+ return (char const*)(fTable->Lookup(username));
+}
diff --git a/liveMedia/H261VideoRTPSource.cpp b/liveMedia/H261VideoRTPSource.cpp
index 48c1462..06b07ab 100644
--- a/liveMedia/H261VideoRTPSource.cpp
+++ b/liveMedia/H261VideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.261 Video RTP Sources
// Implementation
diff --git a/liveMedia/H263plusVideoFileServerMediaSubsession.cpp b/liveMedia/H263plusVideoFileServerMediaSubsession.cpp
index 121e157..be13deb 100644
--- a/liveMedia/H263plusVideoFileServerMediaSubsession.cpp
+++ b/liveMedia/H263plusVideoFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a H263 video file.
// Implementation
diff --git a/liveMedia/H263plusVideoRTPSink.cpp b/liveMedia/H263plusVideoRTPSink.cpp
index 5f587b9..65c6e50 100644
--- a/liveMedia/H263plusVideoRTPSink.cpp
+++ b/liveMedia/H263plusVideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.263+ video (RFC 4629)
// Implementation
@@ -63,8 +63,7 @@ void H263plusVideoRTPSink
return;
}
if (frameStart[0] != 0 || frameStart[1] != 0) {
- envir() << "H263plusVideoRTPSink::doSpecialFrameHandling(): unexpected non-zero first two bytes: "
- << (void*)(frameStart[0]) << "," << (void*)(frameStart[1]) << "\n";
+ envir() << "H263plusVideoRTPSink::doSpecialFrameHandling(): unexpected non-zero first two bytes!\n";
}
frameStart[0] = specialHeader>>8;
frameStart[1] = (unsigned char)specialHeader;
diff --git a/liveMedia/H263plusVideoRTPSource.cpp b/liveMedia/H263plusVideoRTPSource.cpp
index 67f00df..e538bd6 100644
--- a/liveMedia/H263plusVideoRTPSource.cpp
+++ b/liveMedia/H263plusVideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.263+ Video RTP Sources
// Implementation
diff --git a/liveMedia/H263plusVideoStreamFramer.cpp b/liveMedia/H263plusVideoStreamFramer.cpp
index 17ce365..1b23375 100644
--- a/liveMedia/H263plusVideoStreamFramer.cpp
+++ b/liveMedia/H263plusVideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Author Bernhard Feiten
// A filter that breaks up an H.263plus video stream into frames.
//
diff --git a/liveMedia/H263plusVideoStreamParser.cpp b/liveMedia/H263plusVideoStreamParser.cpp
index 04c8af4..e59539f 100644
--- a/liveMedia/H263plusVideoStreamParser.cpp
+++ b/liveMedia/H263plusVideoStreamParser.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Author Bernhard Feiten
// A filter that breaks up an H.263plus video stream into frames.
// Based on MPEG4IP/mp4creator/h263.c
@@ -197,7 +197,7 @@ int H263plusVideoStreamParser::parseH263Frame( )
((row = fStates[(unsigned char)row][*(bufferIndex++)]) != -1)); // Start code was not found
if (row != -1) {
- fprintf(stderr, "%s: Buffer too small (%lu)\n",
+ fprintf(stderr, "%s: Buffer too small (%u)\n",
"h263reader:", bufferEnd - fTo + ADDITIONAL_BYTES_NEEDED);
return 0;
}
@@ -632,7 +632,7 @@ static int LoadNextH263Object( FILE *inputFileHandle,
// This table and the following loop implements a state machine enabling
// us to read bytes from the file untill (and inclusing) the requested
// start code (00 00 8X) is found
- int8_t row = 0;
+ char row = 0;
u_int8_t *bufferStart = frameBuffer;
// The buffer end which will allow the loop to leave place for
// the additionalBytesNeeded
diff --git a/liveMedia/H263plusVideoStreamParser.hh b/liveMedia/H263plusVideoStreamParser.hh
index 6e8b400..475a5e7 100644
--- a/liveMedia/H263plusVideoStreamParser.hh
+++ b/liveMedia/H263plusVideoStreamParser.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an H263 video stream into frames.
// derived from MPEG4IP h263.c
// Author Benhard Feiten
diff --git a/liveMedia/H264VideoFileServerMediaSubsession.cpp b/liveMedia/H264VideoFileServerMediaSubsession.cpp
index b860d06..0d431d6 100644
--- a/liveMedia/H264VideoFileServerMediaSubsession.cpp
+++ b/liveMedia/H264VideoFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a H264 video file.
// Implementation
@@ -70,7 +70,7 @@ void H264VideoFileServerMediaSubsession::checkForAuxSDPLine1() {
// Signal the event loop that we're done:
setDoneFlag();
- } else {
+ } else if (!fDoneFlag) {
// try again after a brief delay:
int uSecsToDelay = 100000; // 100 ms
nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,
diff --git a/liveMedia/H264VideoFileSink.cpp b/liveMedia/H264VideoFileSink.cpp
index af7bc74..5c0a098 100644
--- a/liveMedia/H264VideoFileSink.cpp
+++ b/liveMedia/H264VideoFileSink.cpp
@@ -14,13 +14,12 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.264 Video File sinks
// Implementation
#include "H264VideoFileSink.hh"
#include "OutputFile.hh"
-#include "H264VideoRTPSource.hh"
////////// H264VideoFileSink //////////
@@ -28,8 +27,8 @@ H264VideoFileSink
::H264VideoFileSink(UsageEnvironment& env, FILE* fid,
char const* sPropParameterSetsStr,
unsigned bufferSize, char const* perFrameFileNamePrefix)
- : FileSink(env, fid, bufferSize, perFrameFileNamePrefix),
- fSPropParameterSetsStr(sPropParameterSetsStr), fHaveWrittenFirstFrame(False) {
+ : H264or5VideoFileSink(env, fid, bufferSize, perFrameFileNamePrefix,
+ sPropParameterSetsStr, NULL, NULL) {
}
H264VideoFileSink::~H264VideoFileSink() {
@@ -58,25 +57,3 @@ H264VideoFileSink::createNew(UsageEnvironment& env, char const* fileName,
return NULL;
}
-
-void H264VideoFileSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
- unsigned char const start_code[4] = {0x00, 0x00, 0x00, 0x01};
-
- if (!fHaveWrittenFirstFrame) {
- // If we have PPS/SPS NAL units encoded in a "sprop parameter string", prepend these to the file:
- unsigned numSPropRecords;
- SPropRecord* sPropRecords = parseSPropParameterSets(fSPropParameterSetsStr, numSPropRecords);
- for (unsigned i = 0; i < numSPropRecords; ++i) {
- addData(start_code, 4, presentationTime);
- addData(sPropRecords[i].sPropBytes, sPropRecords[i].sPropLength, presentationTime);
- }
- delete[] sPropRecords;
- fHaveWrittenFirstFrame = True; // for next time
- }
-
- // Write the input data to the file, with the start code in front:
- addData(start_code, 4, presentationTime);
-
- // Call the parent class to complete the normal file write with the input data:
- FileSink::afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
-}
diff --git a/liveMedia/H264VideoMatroskaFileServerMediaSubsession.cpp b/liveMedia/H264VideoMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index 56f693a..0000000
--- a/liveMedia/H264VideoMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an H264 video track within a Matroska file.
-// Implementation
-
-#include "H264VideoMatroskaFileServerMediaSubsession.hh"
-#include "H264VideoStreamDiscreteFramer.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-H264VideoMatroskaFileServerMediaSubsession* H264VideoMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new H264VideoMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-#define CHECK_PTR if (ptr >= limit) return
-#define NUM_BYTES_REMAINING (unsigned)(limit - ptr)
-
-H264VideoMatroskaFileServerMediaSubsession
-::H264VideoMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : H264VideoFileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber),
- fSPSSize(0), fSPS(NULL), fPPSSize(0), fPPS(NULL) {
- // Use our track's 'Codec Private' data: Bytes 5 and beyond contain SPS and PPSs:
- unsigned numSPSandPPSBytes;
- u_int8_t* SPSandPPSBytes;
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
-
- if (track->codecPrivateSize >= 6) {
- numSPSandPPSBytes = track->codecPrivateSize - 5;
- SPSandPPSBytes = &track->codecPrivate[5];
- } else {
- numSPSandPPSBytes = 0;
- SPSandPPSBytes = NULL;
- }
-
- // Extract, from "SPSandPPSBytes", one SPS NAL unit, and one PPS NAL unit.
- // (I hope one is all we need of each.)
- if (numSPSandPPSBytes == 0 || SPSandPPSBytes == NULL) return; // sanity check
- unsigned i;
- u_int8_t* ptr = SPSandPPSBytes;
- u_int8_t* limit = &SPSandPPSBytes[numSPSandPPSBytes];
-
- unsigned numSPSs = (*ptr++)&0x1F; CHECK_PTR;
- for (i = 0; i < numSPSs; ++i) {
- unsigned spsSize = (*ptr++)<<8; CHECK_PTR;
- spsSize |= *ptr++; CHECK_PTR;
-
- if (spsSize > NUM_BYTES_REMAINING) return;
- u_int8_t nal_unit_type = ptr[0]&0x1F;
- if (fSPS == NULL && nal_unit_type == 7/*sanity check*/) { // save the first one
- fSPSSize = spsSize;
- fSPS = new u_int8_t[spsSize];
- memmove(fSPS, ptr, spsSize);
- }
- ptr += spsSize;
- }
-
- unsigned numPPSs = (*ptr++)&0x1F; CHECK_PTR;
- for (i = 0; i < numPPSs; ++i) {
- unsigned ppsSize = (*ptr++)<<8; CHECK_PTR;
- ppsSize |= *ptr++; CHECK_PTR;
-
- if (ppsSize > NUM_BYTES_REMAINING) return;
- u_int8_t nal_unit_type = ptr[0]&0x1F;
- if (fPPS == NULL && nal_unit_type == 8/*sanity check*/) { // save the first one
- fPPSSize = ppsSize;
- fPPS = new u_int8_t[ppsSize];
- memmove(fPPS, ptr, ppsSize);
- }
- ptr += ppsSize;
- }
-}
-
-H264VideoMatroskaFileServerMediaSubsession
-::~H264VideoMatroskaFileServerMediaSubsession() {
- delete[] fSPS;
- delete[] fPPS;
-}
-
-float H264VideoMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void H264VideoMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- // "inputSource" is a framer. *Its* source is the demuxed track that we seek on:
- H264VideoStreamDiscreteFramer* framer = (H264VideoStreamDiscreteFramer*)inputSource;
-
- MatroskaDemuxedTrack* demuxedTrack = (MatroskaDemuxedTrack*)(framer->inputSource());
- demuxedTrack->seekToTime(seekNPT);
-}
-
-FramedSource* H264VideoMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- // Allow for the possibility of very large NAL units being fed to our "RTPSink" objects:
- OutPacketBuffer::maxSize = 300000; // bytes
- estBitrate = 500; // kbps, estimate
-
- // Create the video source:
- FramedSource* baseH264VideoSource = fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
- if (baseH264VideoSource == NULL) return NULL;
-
- // Create a framer for the Video stream:
- H264VideoStreamDiscreteFramer* framer
- = H264VideoStreamDiscreteFramer::createNew(envir(), baseH264VideoSource);
- framer->setVPSandSPSandPPS(NULL, 0, fSPS, fSPSSize, fPPS, fPPSSize);
-
- return framer;
-}
diff --git a/liveMedia/H264VideoMatroskaFileServerMediaSubsession.hh b/liveMedia/H264VideoMatroskaFileServerMediaSubsession.hh
deleted file mode 100644
index 1b8e88d..0000000
--- a/liveMedia/H264VideoMatroskaFileServerMediaSubsession.hh
+++ /dev/null
@@ -1,59 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an H264 video track within a Matroska file.
-// C++ header
-
-#ifndef _H264_VIDEO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _H264_VIDEO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-
-#ifndef _H264_VIDEO_FILE_SERVER_MEDIA_SUBSESSION_HH
-#include "H264VideoFileServerMediaSubsession.hh"
-#endif
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#include "MatroskaFileServerDemux.hh"
-#endif
-
-class H264VideoMatroskaFileServerMediaSubsession: public H264VideoFileServerMediaSubsession {
-public:
- static H264VideoMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
-
-private:
- H264VideoMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~H264VideoMatroskaFileServerMediaSubsession();
-
-private: // redefined virtual functions
- virtual float duration() const;
- virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
- virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
- unsigned& estBitrate);
-
-private:
- MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
-
- // We store one SPS, and one PPS, for use in our input 'framer's:
- unsigned fSPSSize;
- u_int8_t* fSPS;
- unsigned fPPSSize;
- u_int8_t* fPPS;
-};
-
-#endif
diff --git a/liveMedia/H264VideoRTPSink.cpp b/liveMedia/H264VideoRTPSink.cpp
index 0c57151..cc26425 100644
--- a/liveMedia/H264VideoRTPSink.cpp
+++ b/liveMedia/H264VideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.264 video (RFC 3984)
// Implementation
@@ -97,8 +97,18 @@ char const* H264VideoRTPSink::auxSDPLine() {
}
// Set up the "a=fmtp:" SDP line for this stream:
+ u_int8_t* spsWEB = new u_int8_t[spsSize]; // "WEB" means "Without Emulation Bytes"
+ unsigned spsWEBSize = removeH264or5EmulationBytes(spsWEB, spsSize, sps, spsSize);
+ if (spsWEBSize < 4) { // Bad SPS size => assume our source isn't ready
+ delete[] spsWEB;
+ return NULL;
+ }
+ u_int32_t profileLevelId = (spsWEB[1]<<16) | (spsWEB[2]<<8) | spsWEB[3];
+ delete[] spsWEB;
+
char* sps_base64 = base64Encode((char*)sps, spsSize);
char* pps_base64 = base64Encode((char*)pps, ppsSize);
+
char const* fmtpFmt =
"a=fmtp:%d packetization-mode=1"
";profile-level-id=%06X"
@@ -110,8 +120,9 @@ char const* H264VideoRTPSink::auxSDPLine() {
char* fmtp = new char[fmtpFmtSize];
sprintf(fmtp, fmtpFmt,
rtpPayloadType(),
- framerSource->profileLevelId(),
+ profileLevelId,
sps_base64, pps_base64);
+
delete[] sps_base64;
delete[] pps_base64;
diff --git a/liveMedia/H264VideoRTPSource.cpp b/liveMedia/H264VideoRTPSource.cpp
index 7998660..68cd9e7 100644
--- a/liveMedia/H264VideoRTPSource.cpp
+++ b/liveMedia/H264VideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.264 Video RTP Sources
// Implementation
@@ -67,51 +67,48 @@ Boolean H264VideoRTPSource
unsigned& resultSpecialHeaderSize) {
unsigned char* headerStart = packet->data();
unsigned packetSize = packet->dataSize();
+ unsigned numBytesToSkip;
- // The header has a minimum size of 0, since the NAL header is used
- // as a payload header
- unsigned expectedHeaderSize = 0;
-
- // Check if the type field is 28 (FU-A) or 29 (FU-B)
+ // Check the 'nal_unit_type' for special 'aggregation' or 'fragmentation' packets:
+ if (packetSize < 1) return False;
fCurPacketNALUnitType = (headerStart[0]&0x1F);
switch (fCurPacketNALUnitType) {
case 24: { // STAP-A
- expectedHeaderSize = 1; // discard the type byte
+ numBytesToSkip = 1; // discard the type byte
break;
}
case 25: case 26: case 27: { // STAP-B, MTAP16, or MTAP24
- expectedHeaderSize = 3; // discard the type byte, and the initial DON
+ numBytesToSkip = 3; // discard the type byte, and the initial DON
break;
}
case 28: case 29: { // // FU-A or FU-B
// For these NALUs, the first two bytes are the FU indicator and the FU header.
- // If the start bit is set, we reconstruct the original NAL header:
+ // If the start bit is set, we reconstruct the original NAL header into byte 1:
+ if (packetSize < 2) return False;
unsigned char startBit = headerStart[1]&0x80;
unsigned char endBit = headerStart[1]&0x40;
if (startBit) {
- expectedHeaderSize = 1;
- if (packetSize < expectedHeaderSize) return False;
-
- headerStart[1] = (headerStart[0]&0xE0)+(headerStart[1]&0x1F);
fCurrentPacketBeginsFrame = True;
+
+ headerStart[1] = (headerStart[0]&0xE0)|(headerStart[1]&0x1F);
+ numBytesToSkip = 1;
} else {
- // If the startbit is not set, both the FU indicator and header
- // can be discarded
- expectedHeaderSize = 2;
- if (packetSize < expectedHeaderSize) return False;
+ // The start bit is not set, so we skip both the FU indicator and header:
fCurrentPacketBeginsFrame = False;
+ numBytesToSkip = 2;
}
fCurrentPacketCompletesFrame = (endBit != 0);
break;
}
default: {
- // This packet contains one or more complete, decodable NAL units
+ // This packet contains one complete NAL unit:
fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame = True;
+ numBytesToSkip = 0;
break;
}
}
- resultSpecialHeaderSize = expectedHeaderSize;
+ resultSpecialHeaderSize = numBytesToSkip;
return True;
}
diff --git a/liveMedia/H264VideoStreamDiscreteFramer.cpp b/liveMedia/H264VideoStreamDiscreteFramer.cpp
index ad15947..6b95c5f 100644
--- a/liveMedia/H264VideoStreamDiscreteFramer.cpp
+++ b/liveMedia/H264VideoStreamDiscreteFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "H264VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/H264VideoStreamFramer.cpp b/liveMedia/H264VideoStreamFramer.cpp
index 2be9d47..bf7f5cb 100644
--- a/liveMedia/H264VideoStreamFramer.cpp
+++ b/liveMedia/H264VideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up a H.264 Video Elementary Stream into NAL units.
// Implementation
diff --git a/liveMedia/H264or5VideoFileSink.cpp b/liveMedia/H264or5VideoFileSink.cpp
new file mode 100644
index 0000000..f73d769
--- /dev/null
+++ b/liveMedia/H264or5VideoFileSink.cpp
@@ -0,0 +1,65 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// H.264 or H.265 Video File sinks
+// Implementation
+
+#include "H264or5VideoFileSink.hh"
+#include "H264VideoRTPSource.hh" // for "parseSPropParameterSets()"
+
+////////// H264or5VideoFileSink //////////
+
+H264or5VideoFileSink
+::H264or5VideoFileSink(UsageEnvironment& env, FILE* fid,
+ unsigned bufferSize, char const* perFrameFileNamePrefix,
+ char const* sPropParameterSetsStr1,
+ char const* sPropParameterSetsStr2,
+ char const* sPropParameterSetsStr3)
+ : FileSink(env, fid, bufferSize, perFrameFileNamePrefix),
+ fHaveWrittenFirstFrame(False) {
+ fSPropParameterSetsStr[0] = sPropParameterSetsStr1;
+ fSPropParameterSetsStr[1] = sPropParameterSetsStr2;
+ fSPropParameterSetsStr[2] = sPropParameterSetsStr3;
+}
+
+H264or5VideoFileSink::~H264or5VideoFileSink() {
+}
+
+void H264or5VideoFileSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
+ unsigned char const start_code[4] = {0x00, 0x00, 0x00, 0x01};
+
+ if (!fHaveWrittenFirstFrame) {
+ // If we have NAL units encoded in "sprop parameter strings", prepend these to the file:
+ for (unsigned j = 0; j < 3; ++j) {
+ unsigned numSPropRecords;
+ SPropRecord* sPropRecords
+ = parseSPropParameterSets(fSPropParameterSetsStr[j], numSPropRecords);
+ for (unsigned i = 0; i < numSPropRecords; ++i) {
+ addData(start_code, 4, presentationTime);
+ addData(sPropRecords[i].sPropBytes, sPropRecords[i].sPropLength, presentationTime);
+ }
+ delete[] sPropRecords;
+ }
+ fHaveWrittenFirstFrame = True; // for next time
+ }
+
+ // Write the input data to the file, with the start code in front:
+ addData(start_code, 4, presentationTime);
+
+ // Call the parent class to complete the normal file write with the input data:
+ FileSink::afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
+}
diff --git a/liveMedia/H264or5VideoRTPSink.cpp b/liveMedia/H264or5VideoRTPSink.cpp
index 8e8742a..b8c16d3 100644
--- a/liveMedia/H264or5VideoRTPSink.cpp
+++ b/liveMedia/H264or5VideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.264 or H.265 video
// Implementation
@@ -40,6 +40,7 @@ public:
private: // redefined virtual functions:
virtual void doGetNextFrame();
+ virtual void doStopGettingFrames();
private:
static void afterGettingFrame(void* clientData, unsigned frameSize,
@@ -50,6 +51,7 @@ private:
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned durationInMicroseconds);
+ void reset();
private:
int fHNumber;
@@ -161,10 +163,9 @@ H264or5Fragmenter::H264or5Fragmenter(int hNumber,
unsigned inputBufferMax, unsigned maxOutputPacketSize)
: FramedFilter(env, inputSource),
fHNumber(hNumber),
- fInputBufferSize(inputBufferMax+1), fMaxOutputPacketSize(maxOutputPacketSize),
- fNumValidDataBytes(1), fCurDataOffset(1), fSaveNumTruncatedBytes(0),
- fLastFragmentCompletedNALUnit(True) {
+ fInputBufferSize(inputBufferMax+1), fMaxOutputPacketSize(maxOutputPacketSize) {
fInputBuffer = new unsigned char[fInputBufferSize];
+ reset();
}
H264or5Fragmenter::~H264or5Fragmenter() {
@@ -263,6 +264,12 @@ void H264or5Fragmenter::doGetNextFrame() {
}
}
+void H264or5Fragmenter::doStopGettingFrames() {
+ // Make sure that we don't have any stale data fragments lying around, should we later resume:
+ reset();
+ FramedFilter::doStopGettingFrames();
+}
+
void H264or5Fragmenter::afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime,
@@ -284,3 +291,9 @@ void H264or5Fragmenter::afterGettingFrame1(unsigned frameSize,
// Deliver data to the client:
doGetNextFrame();
}
+
+void H264or5Fragmenter::reset() {
+ fNumValidDataBytes = fCurDataOffset = 1;
+ fSaveNumTruncatedBytes = 0;
+ fLastFragmentCompletedNALUnit = True;
+}
diff --git a/liveMedia/H264or5VideoStreamDiscreteFramer.cpp b/liveMedia/H264or5VideoStreamDiscreteFramer.cpp
index 0f5c1b7..dae5368 100644
--- a/liveMedia/H264or5VideoStreamDiscreteFramer.cpp
+++ b/liveMedia/H264or5VideoStreamDiscreteFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "H264or5VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
@@ -79,11 +79,7 @@ void H264or5VideoStreamDiscreteFramer
saveCopyOfPPS(fTo, frameSize);
}
- // Next, check whether this NAL unit ends the current 'access unit' (basically, a video frame).
- // Unfortunately, we can't do this reliably, because we don't yet know anything about the
- // *next* NAL unit that we'll see. So, we guess this as best as we can, by assuming that
- // if this NAL unit is a VCL NAL unit, then it ends the current 'access unit'.
- if (isVCL(nal_unit_type)) fPictureEndMarker = True;
+ fPictureEndMarker = nalUnitEndsAccessUnit(nal_unit_type);
// Finally, complete delivery to the client:
fFrameSize = frameSize;
@@ -92,3 +88,15 @@ void H264or5VideoStreamDiscreteFramer
fDurationInMicroseconds = durationInMicroseconds;
afterGetting(this);
}
+
+Boolean H264or5VideoStreamDiscreteFramer::nalUnitEndsAccessUnit(u_int8_t nal_unit_type) {
+ // Check whether this NAL unit ends the current 'access unit' (basically, a video frame).
+ // Unfortunately, we can't do this reliably, because we don't yet know anything about the
+ // *next* NAL unit that we'll see. So, we guess this as best as we can, by assuming that
+ // if this NAL unit is a VCL NAL unit, then it ends the current 'access unit'.
+ //
+ // This will be wrong if you are streaming multiple 'slices' per picture. In that case,
+ // you can define a subclass that reimplements this virtual function to do the right thing.
+
+ return isVCL(nal_unit_type);
+}
diff --git a/liveMedia/H264or5VideoStreamFramer.cpp b/liveMedia/H264or5VideoStreamFramer.cpp
index 055f62d..b027275 100644
--- a/liveMedia/H264or5VideoStreamFramer.cpp
+++ b/liveMedia/H264or5VideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up a H.264 or H.265 Video Elementary Stream into NAL units.
// Implementation
@@ -53,7 +53,9 @@ private:
void analyze_seq_parameter_set_data(unsigned& num_units_in_tick, unsigned& time_scale);
void profile_tier_level(BitVector& bv, unsigned max_sub_layers_minus1);
void analyze_vui_parameters(BitVector& bv, unsigned& num_units_in_tick, unsigned& time_scale);
+ void analyze_hrd_parameters(BitVector& bv);
void analyze_sei_data(u_int8_t nal_unit_type);
+ void analyze_sei_payload(unsigned payloadType, unsigned payloadSize, u_int8_t* payload);
private:
int fHNumber; // 264 or 265
@@ -61,6 +63,10 @@ private:
Boolean fHaveSeenFirstStartCode, fHaveSeenFirstByteOfNALUnit;
u_int8_t fFirstByteOfNALUnit;
double fParsedFrameRate;
+ // variables set & used in the specification:
+ unsigned cpb_removal_delay_length_minus1, dpb_output_delay_length_minus1;
+ Boolean CpbDpbDelaysPresentFlag, pic_struct_present_flag;
+ double DeltaTfiDivisor;
};
@@ -73,10 +79,7 @@ H264or5VideoStreamFramer
fHNumber(hNumber),
fLastSeenVPS(NULL), fLastSeenVPSSize(0),
fLastSeenSPS(NULL), fLastSeenSPSSize(0),
- fLastSeenPPS(NULL), fLastSeenPPSSize(0),
- fProfileLevelId(0) {
- for (unsigned i = 0; i < 12; ++i) fProfileTierLevelHeaderBytes[i] = 0;
-
+ fLastSeenPPS(NULL), fLastSeenPPSSize(0) {
fParser = createParser
? new H264or5VideoStreamParser(hNumber, this, inputSource, includeStartCodeInOutput)
: NULL;
@@ -99,16 +102,6 @@ void H264or5VideoStreamFramer::saveCopyOfVPS(u_int8_t* from, unsigned size) {
memmove(fLastSeenVPS, from, size);
fLastSeenVPSSize = size;
-
- // We also make another copy - without 'emulation bytes', to extract parameters that we need:
- u_int8_t vps[VPS_MAX_SIZE];
- unsigned vpsSize
- = removeH264or5EmulationBytes(vps, VPS_MAX_SIZE, fLastSeenVPS, fLastSeenVPSSize);
-
- // Extract the first 12 'profile_tier_level' bytes:
- if (vpsSize >= 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
- memmove(fProfileTierLevelHeaderBytes, &vps[6], 12);
- }
}
#define SPS_MAX_SIZE 1000 // larger than the largest possible SPS (Sequence Parameter Set) NAL unit
@@ -120,22 +113,6 @@ void H264or5VideoStreamFramer::saveCopyOfSPS(u_int8_t* from, unsigned size) {
memmove(fLastSeenSPS, from, size);
fLastSeenSPSSize = size;
-
- // We also make another copy - without 'emulation bytes', to extract parameters that we need:
- u_int8_t sps[SPS_MAX_SIZE];
- unsigned spsSize
- = removeH264or5EmulationBytes(sps, SPS_MAX_SIZE, fLastSeenSPS, fLastSeenSPSSize);
- if (fHNumber == 264) {
- // Extract the first 3 bytes of the SPS (after the nal_unit_header byte) as 'profile_level_id'
- if (spsSize >= 1/*'profile_level_id' offset within SPS*/ + 3/*num bytes needed*/) {
- fProfileLevelId = (sps[1]<<16) | (sps[2]<<8) | sps[3];
- }
- } else { // 265
- // Extract the first 12 'profile_tier_level' bytes:
- if (spsSize >= 3/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
- memmove(fProfileTierLevelHeaderBytes, &sps[3], 12);
- }
- }
}
void H264or5VideoStreamFramer::saveCopyOfPPS(u_int8_t* from, unsigned size) {
@@ -173,7 +150,10 @@ H264or5VideoStreamParser
::H264or5VideoStreamParser(int hNumber, H264or5VideoStreamFramer* usingSource,
FramedSource* inputSource, Boolean includeStartCodeInOutput)
: MPEGVideoStreamParser(usingSource, inputSource),
- fHNumber(hNumber), fOutputStartCodeSize(includeStartCodeInOutput ? 4 : 0), fHaveSeenFirstStartCode(False), fHaveSeenFirstByteOfNALUnit(False), fParsedFrameRate(0.0) {
+ fHNumber(hNumber), fOutputStartCodeSize(includeStartCodeInOutput ? 4 : 0), fHaveSeenFirstStartCode(False), fHaveSeenFirstByteOfNALUnit(False), fParsedFrameRate(0.0),
+ cpb_removal_delay_length_minus1(23), dpb_output_delay_length_minus1(23),
+ CpbDpbDelaysPresentFlag(0), pic_struct_present_flag(0),
+ DeltaTfiDivisor(2.0) {
}
H264or5VideoStreamParser::~H264or5VideoStreamParser() {
@@ -389,7 +369,10 @@ void H264or5VideoStreamParser
(void)bv.get_expGolomb(); // chroma_sample_loc_type_bottom_field
}
if (fHNumber == 265) {
- bv.skipBits(3); // neutral_chroma_indication_flag, field_seq_flag, frame_field_info_present_flag
+ bv.skipBits(2); // neutral_chroma_indication_flag, field_seq_flag
+ Boolean frame_field_info_present_flag = bv.get1BitBoolean();
+ DEBUG_PRINT(frame_field_info_present_flag);
+ pic_struct_present_flag = frame_field_info_present_flag; // hack to make H.265 like H.264
Boolean default_display_window_flag = bv.get1BitBoolean();
DEBUG_PRINT(default_display_window_flag);
if (default_display_window_flag) {
@@ -417,8 +400,50 @@ void H264or5VideoStreamParser
unsigned vui_num_ticks_poc_diff_one_minus1 = bv.get_expGolomb();
DEBUG_PRINT(vui_num_ticks_poc_diff_one_minus1);
}
+ return; // For H.265, don't bother parsing any more of this #####
}
}
+ // The following is H.264 only: #####
+ Boolean nal_hrd_parameters_present_flag = bv.get1BitBoolean();
+ DEBUG_PRINT(nal_hrd_parameters_present_flag);
+ if (nal_hrd_parameters_present_flag) analyze_hrd_parameters(bv);
+ Boolean vcl_hrd_parameters_present_flag = bv.get1BitBoolean();
+ DEBUG_PRINT(vcl_hrd_parameters_present_flag);
+ if (vcl_hrd_parameters_present_flag) analyze_hrd_parameters(bv);
+ CpbDpbDelaysPresentFlag = nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag;
+ if (CpbDpbDelaysPresentFlag) {
+ bv.skipBits(1); // low_delay_hrd_flag
+ }
+ pic_struct_present_flag = bv.get1BitBoolean();
+ DEBUG_PRINT(pic_struct_present_flag);
+}
+
+void H264or5VideoStreamParser::analyze_hrd_parameters(BitVector& bv) {
+ DEBUG_TAB;
+ unsigned cpb_cnt_minus1 = bv.get_expGolomb();
+ DEBUG_PRINT(cpb_cnt_minus1);
+ unsigned bit_rate_scale = bv.getBits(4);
+ DEBUG_PRINT(bit_rate_scale);
+ unsigned cpb_size_scale = bv.getBits(4);
+ DEBUG_PRINT(cpb_size_scale);
+ for (unsigned SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; ++SchedSelIdx) {
+ DEBUG_TAB;
+ DEBUG_PRINT(SchedSelIdx);
+ unsigned bit_rate_value_minus1 = bv.get_expGolomb();
+ DEBUG_PRINT(bit_rate_value_minus1);
+ unsigned cpb_size_value_minus1 = bv.get_expGolomb();
+ DEBUG_PRINT(cpb_size_value_minus1);
+ Boolean cbr_flag = bv.get1BitBoolean();
+ DEBUG_PRINT(cbr_flag);
+ }
+ unsigned initial_cpb_removal_delay_length_minus1 = bv.getBits(5);
+ DEBUG_PRINT(initial_cpb_removal_delay_length_minus1);
+ cpb_removal_delay_length_minus1 = bv.getBits(5);
+ DEBUG_PRINT(cpb_removal_delay_length_minus1);
+ dpb_output_delay_length_minus1 = bv.getBits(5);
+ DEBUG_PRINT(dpb_output_delay_length_minus1);
+ unsigned time_offset_length = bv.getBits(5);
+ DEBUG_PRINT(time_offset_length);
}
void H264or5VideoStreamParser
@@ -674,12 +699,12 @@ void H264or5VideoStreamParser
}
unsigned num_short_term_ref_pic_sets = bv.get_expGolomb();
DEBUG_PRINT(num_short_term_ref_pic_sets);
+ unsigned num_negative_pics = 0, prev_num_negative_pics = 0;
+ unsigned num_positive_pics = 0, prev_num_positive_pics = 0;
for (i = 0; i < num_short_term_ref_pic_sets; ++i) {
// short_term_ref_pic_set(i):
DEBUG_TAB;
DEBUG_PRINT(i);
- unsigned num_negative_pics = 0;
- unsigned num_positive_pics = 0;
Boolean inter_ref_pic_set_prediction_flag = False;
if (i != 0) {
inter_ref_pic_set_prediction_flag = bv.get1BitBoolean();
@@ -693,15 +718,18 @@ void H264or5VideoStreamParser
}
bv.skipBits(1); // delta_rps_sign
(void)bv.get_expGolomb(); // abs_delta_rps_minus1
- for (unsigned j = 0; j < num_negative_pics+num_positive_pics; ++j) {
+ unsigned NumDeltaPocs = prev_num_negative_pics + prev_num_positive_pics; // correct???
+ for (unsigned j = 0; j < NumDeltaPocs; ++j) {
DEBUG_PRINT(j);
Boolean used_by_curr_pic_flag = bv.get1BitBoolean();
DEBUG_PRINT(used_by_curr_pic_flag);
if (!used_by_curr_pic_flag) bv.skipBits(1); // use_delta_flag[j]
}
} else {
+ prev_num_negative_pics = num_negative_pics;
num_negative_pics = bv.get_expGolomb();
DEBUG_PRINT(num_negative_pics);
+ prev_num_positive_pics = num_positive_pics;
num_positive_pics = bv.get_expGolomb();
DEBUG_PRINT(num_positive_pics);
unsigned k;
@@ -848,10 +876,63 @@ void H264or5VideoStreamParser::analyze_sei_data(u_int8_t nal_unit_type) {
}
fprintf(stderr, "\tpayloadType %d (\"%s\"); payloadSize %d\n", payloadType, description, payloadSize);
#endif
+
+ analyze_sei_payload(payloadType, payloadSize, &sei[j]);
j += payloadSize;
}
}
+void H264or5VideoStreamParser
+::analyze_sei_payload(unsigned payloadType, unsigned payloadSize, u_int8_t* payload) {
+ if (payloadType == 1/* pic_timing, for both H.264 and H.265 */) {
+ BitVector bv(payload, 0, 8*payloadSize);
+
+ DEBUG_TAB;
+ if (CpbDpbDelaysPresentFlag) {
+ unsigned cpb_removal_delay = bv.getBits(cpb_removal_delay_length_minus1 + 1);
+ DEBUG_PRINT(cpb_removal_delay);
+ unsigned dpb_output_delay = bv.getBits(dpb_output_delay_length_minus1 + 1);
+ DEBUG_PRINT(dpb_output_delay);
+ }
+ if (pic_struct_present_flag) {
+ unsigned pic_struct = bv.getBits(4);
+ DEBUG_PRINT(pic_struct);
+ // Use this to set "DeltaTfiDivisor" (which is used to compute the frame rate):
+ double prevDeltaTfiDivisor = DeltaTfiDivisor;
+ if (fHNumber == 264) {
+ DeltaTfiDivisor =
+ pic_struct == 0 ? 2.0 :
+ pic_struct <= 2 ? 1.0 :
+ pic_struct <= 4 ? 2.0 :
+ pic_struct <= 6 ? 3.0 :
+ pic_struct == 7 ? 4.0 :
+ pic_struct == 8 ? 6.0 :
+ 2.0;
+ } else { // H.265
+ DeltaTfiDivisor =
+ pic_struct == 0 ? 2.0 :
+ pic_struct <= 2 ? 1.0 :
+ pic_struct <= 4 ? 2.0 :
+ pic_struct <= 6 ? 3.0 :
+ pic_struct == 7 ? 2.0 :
+ pic_struct == 8 ? 3.0 :
+ pic_struct <= 12 ? 1.0 :
+ 2.0;
+ }
+ // If "DeltaTfiDivisor" has changed, and we've already computed the frame rate, then
+ // adjust it, based on the new value of "DeltaTfiDivisor":
+ if (DeltaTfiDivisor != prevDeltaTfiDivisor && fParsedFrameRate != 0.0) {
+ usingSource()->fFrameRate = fParsedFrameRate
+ = fParsedFrameRate*(prevDeltaTfiDivisor/DeltaTfiDivisor);
+#ifdef DEBUG
+ fprintf(stderr, "Changed frame rate to %f fps\n", usingSource()->fFrameRate);
+#endif
+ }
+ }
+ // Ignore the rest of the payload (timestamps) for now... #####
+ }
+}
+
void H264or5VideoStreamParser::flushInput() {
fHaveSeenFirstStartCode = False;
fHaveSeenFirstByteOfNALUnit = False;
@@ -964,7 +1045,7 @@ unsigned H264or5VideoStreamParser::parse() {
// Now that we have found (& copied) a NAL unit, process it if it's of special interest to us:
if (isVPS(nal_unit_type)) { // Video parameter set
// First, save a copy of this NAL unit, in case the downstream object wants to see it:
- usingSource()->saveCopyOfVPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);
+ usingSource()->saveCopyOfVPS(fStartOfFrame + fOutputStartCodeSize, curFrameSize() - fOutputStartCodeSize);
if (fParsedFrameRate == 0.0) {
// We haven't yet parsed a frame rate from the stream.
@@ -972,19 +1053,20 @@ unsigned H264or5VideoStreamParser::parse() {
unsigned num_units_in_tick, time_scale;
analyze_video_parameter_set_data(num_units_in_tick, time_scale);
if (time_scale > 0 && num_units_in_tick > 0) {
- usingSource()->fFrameRate = fParsedFrameRate = time_scale/(2.0*num_units_in_tick);
+ usingSource()->fFrameRate = fParsedFrameRate
+ = time_scale/(DeltaTfiDivisor*num_units_in_tick);
#ifdef DEBUG
fprintf(stderr, "Set frame rate to %f fps\n", usingSource()->fFrameRate);
#endif
} else {
#ifdef DEBUG
- fprintf(stderr, "\tThis \"Picture Parameter Set\" NAL unit contained no frame rate information, so we use a default frame rate of %f fps\n", usingSource()->fFrameRate);
+ fprintf(stderr, "\tThis \"Video Parameter Set\" NAL unit contained no frame rate information, so we use a default frame rate of %f fps\n", usingSource()->fFrameRate);
#endif
}
}
} else if (isSPS(nal_unit_type)) { // Sequence parameter set
// First, save a copy of this NAL unit, in case the downstream object wants to see it:
- usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);
+ usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize, curFrameSize() - fOutputStartCodeSize);
if (fParsedFrameRate == 0.0) {
// We haven't yet parsed a frame rate from the stream.
@@ -992,7 +1074,8 @@ unsigned H264or5VideoStreamParser::parse() {
unsigned num_units_in_tick, time_scale;
analyze_seq_parameter_set_data(num_units_in_tick, time_scale);
if (time_scale > 0 && num_units_in_tick > 0) {
- usingSource()->fFrameRate = fParsedFrameRate = time_scale/(2.0*num_units_in_tick);
+ usingSource()->fFrameRate = fParsedFrameRate
+ = time_scale/(DeltaTfiDivisor*num_units_in_tick);
#ifdef DEBUG
fprintf(stderr, "Set frame rate to %f fps\n", usingSource()->fFrameRate);
#endif
@@ -1004,7 +1087,7 @@ unsigned H264or5VideoStreamParser::parse() {
}
} else if (isPPS(nal_unit_type)) { // Picture parameter set
// Save a copy of this NAL unit, in case the downstream object wants to see it:
- usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize, fTo - fStartOfFrame - fOutputStartCodeSize);
+ usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize, curFrameSize() - fOutputStartCodeSize);
} else if (isSEI(nal_unit_type)) { // Supplemental enhancement information (SEI)
analyze_sei_data(nal_unit_type);
// Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? #####
diff --git a/liveMedia/H265VideoFileServerMediaSubsession.cpp b/liveMedia/H265VideoFileServerMediaSubsession.cpp
index 89dbfea..23a390c 100644
--- a/liveMedia/H265VideoFileServerMediaSubsession.cpp
+++ b/liveMedia/H265VideoFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a H265 video file.
// Implementation
@@ -70,7 +70,7 @@ void H265VideoFileServerMediaSubsession::checkForAuxSDPLine1() {
// Signal the event loop that we're done:
setDoneFlag();
- } else {
+ } else if (!fDoneFlag) {
// try again after a brief delay:
int uSecsToDelay = 100000; // 100 ms
nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,
diff --git a/liveMedia/H265VideoFileSink.cpp b/liveMedia/H265VideoFileSink.cpp
new file mode 100644
index 0000000..35bf32f
--- /dev/null
+++ b/liveMedia/H265VideoFileSink.cpp
@@ -0,0 +1,63 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// H.265 Video File sinks
+// Implementation
+
+#include "H265VideoFileSink.hh"
+#include "OutputFile.hh"
+
+////////// H265VideoFileSink //////////
+
+H265VideoFileSink
+::H265VideoFileSink(UsageEnvironment& env, FILE* fid,
+ char const* sPropVPSStr,
+ char const* sPropSPSStr,
+ char const* sPropPPSStr,
+ unsigned bufferSize, char const* perFrameFileNamePrefix)
+ : H264or5VideoFileSink(env, fid, bufferSize, perFrameFileNamePrefix,
+ sPropVPSStr, sPropSPSStr, sPropPPSStr) {
+}
+
+H265VideoFileSink::~H265VideoFileSink() {
+}
+
+H265VideoFileSink*
+H265VideoFileSink::createNew(UsageEnvironment& env, char const* fileName,
+ char const* sPropVPSStr,
+ char const* sPropSPSStr,
+ char const* sPropPPSStr,
+ unsigned bufferSize, Boolean oneFilePerFrame) {
+ do {
+ FILE* fid;
+ char const* perFrameFileNamePrefix;
+ if (oneFilePerFrame) {
+ // Create the fid for each frame
+ fid = NULL;
+ perFrameFileNamePrefix = fileName;
+ } else {
+ // Normal case: create the fid once
+ fid = OpenOutputFile(env, fileName);
+ if (fid == NULL) break;
+ perFrameFileNamePrefix = NULL;
+ }
+
+ return new H265VideoFileSink(env, fid, sPropVPSStr, sPropSPSStr, sPropPPSStr, bufferSize, perFrameFileNamePrefix);
+ } while (0);
+
+ return NULL;
+}
diff --git a/liveMedia/H265VideoMatroskaFileServerMediaSubsession.cpp b/liveMedia/H265VideoMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index e014072..0000000
--- a/liveMedia/H265VideoMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an H265 video track within a Matroska file.
-// Implementation
-
-#include "H265VideoMatroskaFileServerMediaSubsession.hh"
-#include "H265VideoStreamDiscreteFramer.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-H265VideoMatroskaFileServerMediaSubsession* H265VideoMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new H265VideoMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-#define CHECK_PTR if (ptr >= limit) return
-#define NUM_BYTES_REMAINING (unsigned)(limit - ptr)
-
-H265VideoMatroskaFileServerMediaSubsession
-::H265VideoMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : H265VideoFileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber),
- fVPSSize(0), fVPS(NULL), fSPSSize(0), fSPS(NULL), fPPSSize(0), fPPS(NULL) {
- // Our track's 'Codec Private' data should contain VPS, SPS, and PPS NAL units. Copy these:
- unsigned numVPS_SPS_PPSBytes = 0;
- u_int8_t* VPS_SPS_PPSBytes = NULL;
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
-
- if (track->codecPrivateUsesH264FormatForH265) {
- // The data uses the H.264-style format (but including VPS NAL unit(s)).
- // The VPS,SPS,PPS NAL unit information starts at byte #5:
- if (track->codecPrivateSize >= 6) {
- numVPS_SPS_PPSBytes = track->codecPrivateSize - 5;
- VPS_SPS_PPSBytes = &track->codecPrivate[5];
- }
- } else {
- // The data uses the proper H.265-style format.
- // The VPS,SPS,PPS NAL unit information starts at byte #22:
- if (track->codecPrivateSize >= 23) {
- numVPS_SPS_PPSBytes = track->codecPrivateSize - 22;
- VPS_SPS_PPSBytes = &track->codecPrivate[22];
- }
- }
-
- // Extract, from "VPS_SPS_PPSBytes", one VPS NAL unit, one SPS NAL unit, and one PPS NAL unit.
- // (I hope one is all we need of each.)
- if (numVPS_SPS_PPSBytes == 0 || VPS_SPS_PPSBytes == NULL) return; // sanity check
- unsigned i;
- u_int8_t* ptr = VPS_SPS_PPSBytes;
- u_int8_t* limit = &VPS_SPS_PPSBytes[numVPS_SPS_PPSBytes];
-
- if (track->codecPrivateUsesH264FormatForH265) {
- // The data uses the H.264-style format (but including VPS NAL unit(s)).
- while (NUM_BYTES_REMAINING > 0) {
- unsigned numNALUnits = (*ptr++)&0x1F; CHECK_PTR;
- for (i = 0; i < numNALUnits; ++i) {
- unsigned nalUnitLength = (*ptr++)<<8; CHECK_PTR;
- nalUnitLength |= *ptr++; CHECK_PTR;
-
- if (nalUnitLength > NUM_BYTES_REMAINING) return;
- u_int8_t nal_unit_type = (ptr[0]&0x7E)>>1;
- if (nal_unit_type == 32) { // VPS
- fVPSSize = nalUnitLength;
- delete[] fVPS; fVPS = new u_int8_t[nalUnitLength];
- memmove(fVPS, ptr, nalUnitLength);
- } else if (nal_unit_type == 33) { // SPS
- fSPSSize = nalUnitLength;
- delete[] fSPS; fSPS = new u_int8_t[nalUnitLength];
- memmove(fSPS, ptr, nalUnitLength);
- } else if (nal_unit_type == 34) { // PPS
- fPPSSize = nalUnitLength;
- delete[] fPPS; fPPS = new u_int8_t[nalUnitLength];
- memmove(fPPS, ptr, nalUnitLength);
- }
- ptr += nalUnitLength;
- }
- }
- } else {
- // The data uses the proper H.265-style format.
- unsigned numOfArrays = *ptr++; CHECK_PTR;
- for (unsigned j = 0; j < numOfArrays; ++j) {
- ++ptr; CHECK_PTR; // skip the 'array_completeness'|'reserved'|'NAL_unit_type' byte
-
- unsigned numNalus = (*ptr++)<<8; CHECK_PTR;
- numNalus |= *ptr++; CHECK_PTR;
-
- for (i = 0; i < numNalus; ++i) {
- unsigned nalUnitLength = (*ptr++)<<8; CHECK_PTR;
- nalUnitLength |= *ptr++; CHECK_PTR;
-
- if (nalUnitLength > NUM_BYTES_REMAINING) return;
- u_int8_t nal_unit_type = (ptr[0]&0x7E)>>1;
- if (nal_unit_type == 32) { // VPS
- fVPSSize = nalUnitLength;
- delete[] fVPS; fVPS = new u_int8_t[nalUnitLength];
- memmove(fVPS, ptr, nalUnitLength);
- } else if (nal_unit_type == 33) { // SPS
- fSPSSize = nalUnitLength;
- delete[] fSPS; fSPS = new u_int8_t[nalUnitLength];
- memmove(fSPS, ptr, nalUnitLength);
- } else if (nal_unit_type == 34) { // PPS
- fPPSSize = nalUnitLength;
- delete[] fPPS; fPPS = new u_int8_t[nalUnitLength];
- memmove(fPPS, ptr, nalUnitLength);
- }
- ptr += nalUnitLength;
- }
- }
- }
-}
-
-H265VideoMatroskaFileServerMediaSubsession
-::~H265VideoMatroskaFileServerMediaSubsession() {
- delete[] fVPS;
- delete[] fSPS;
- delete[] fPPS;
-}
-
-float H265VideoMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void H265VideoMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- // "inputSource" is a framer. *Its* source is the demuxed track that we seek on:
- H265VideoStreamDiscreteFramer* framer = (H265VideoStreamDiscreteFramer*)inputSource;
-
- MatroskaDemuxedTrack* demuxedTrack = (MatroskaDemuxedTrack*)(framer->inputSource());
- demuxedTrack->seekToTime(seekNPT);
-}
-
-FramedSource* H265VideoMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- // Allow for the possibility of very large NAL units being fed to our "RTPSink" objects:
- OutPacketBuffer::maxSize = 300000; // bytes
- estBitrate = 500; // kbps, estimate
-
- // Create the video source:
- FramedSource* baseH265VideoSource = fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
- if (baseH265VideoSource == NULL) return NULL;
-
- // Create a framer for the Video stream:
- H265VideoStreamDiscreteFramer* framer
- = H265VideoStreamDiscreteFramer::createNew(envir(), baseH265VideoSource);
- framer->setVPSandSPSandPPS(fVPS, fVPSSize, fSPS, fSPSSize, fPPS, fPPSSize);
-
- return framer;
-}
diff --git a/liveMedia/H265VideoMatroskaFileServerMediaSubsession.hh b/liveMedia/H265VideoMatroskaFileServerMediaSubsession.hh
deleted file mode 100644
index 527e004..0000000
--- a/liveMedia/H265VideoMatroskaFileServerMediaSubsession.hh
+++ /dev/null
@@ -1,61 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an H265 video track within a Matroska file.
-// C++ header
-
-#ifndef _H265_VIDEO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _H265_VIDEO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-
-#ifndef _H265_VIDEO_FILE_SERVER_MEDIA_SUBSESSION_HH
-#include "H265VideoFileServerMediaSubsession.hh"
-#endif
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#include "MatroskaFileServerDemux.hh"
-#endif
-
-class H265VideoMatroskaFileServerMediaSubsession: public H265VideoFileServerMediaSubsession {
-public:
- static H265VideoMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
-
-private:
- H265VideoMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~H265VideoMatroskaFileServerMediaSubsession();
-
-private: // redefined virtual functions
- virtual float duration() const;
- virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
- virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
- unsigned& estBitrate);
-
-private:
- MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
-
- // We store one VPS, one SPS, and one PPS, for use in our input 'framer's:
- unsigned fVPSSize;
- u_int8_t* fVPS;
- unsigned fSPSSize;
- u_int8_t* fSPS;
- unsigned fPPSSize;
- u_int8_t* fPPS;
-};
-
-#endif
diff --git a/liveMedia/H265VideoRTPSink.cpp b/liveMedia/H265VideoRTPSink.cpp
index b1f75e0..8ad124f 100644
--- a/liveMedia/H265VideoRTPSink.cpp
+++ b/liveMedia/H265VideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.265 video
// Implementation
@@ -115,18 +115,31 @@ char const* H265VideoRTPSink::auxSDPLine() {
if (framerSource == NULL) return NULL; // we don't yet have a source
framerSource->getVPSandSPSandPPS(vps, vpsSize, sps, spsSize, pps, ppsSize);
- if (vps == NULL || sps == NULL || pps == NULL) return NULL; // our source isn't ready
+ if (vps == NULL || sps == NULL || pps == NULL) {
+ return NULL; // our source isn't ready
+ }
}
// Set up the "a=fmtp:" SDP line for this stream.
- // First, extract from our 'profile_tier_level' bytes (that were set by our upstream 'framer')
- // several parameters that we'll put in this line:
- u_int8_t const* profileTierLevelHeaderBytes = framerSource->profileTierLevelHeaderBytes();
- unsigned profile_space = profileTierLevelHeaderBytes[0]>>6; // general_profile_space
- unsigned profile_id = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc
- unsigned tier_flag = (profileTierLevelHeaderBytes[0]>>5)&0x1; // general_tier_flag
- unsigned level_id = profileTierLevelHeaderBytes[11]; // general_level_idc
+ u_int8_t* vpsWEB = new u_int8_t[vpsSize]; // "WEB" means "Without Emulation Bytes"
+ unsigned vpsWEBSize = removeH264or5EmulationBytes(vpsWEB, vpsSize, vps, vpsSize);
+ if (vpsWEBSize < 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
+ // Bad VPS size => assume our source isn't ready
+ delete[] vpsWEB;
+ return NULL;
+ }
+ u_int8_t const* profileTierLevelHeaderBytes = &vpsWEB[6];
+ unsigned profileSpace = profileTierLevelHeaderBytes[0]>>6; // general_profile_space
+ unsigned profileId = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc
+ unsigned tierFlag = (profileTierLevelHeaderBytes[0]>>5)&0x1; // general_tier_flag
+ unsigned levelId = profileTierLevelHeaderBytes[11]; // general_level_idc
u_int8_t const* interop_constraints = &profileTierLevelHeaderBytes[5];
+ char interopConstraintsStr[100];
+ sprintf(interopConstraintsStr, "%02X%02X%02X%02X%02X%02X",
+ interop_constraints[0], interop_constraints[1], interop_constraints[2],
+ interop_constraints[3], interop_constraints[4], interop_constraints[5]);
+ delete[] vpsWEB;
+
char* sprop_vps = base64Encode((char*)vps, vpsSize);
char* sprop_sps = base64Encode((char*)sps, spsSize);
char* sprop_pps = base64Encode((char*)pps, ppsSize);
@@ -136,31 +149,30 @@ char const* H265VideoRTPSink::auxSDPLine() {
";profile-id=%u"
";tier-flag=%u"
";level-id=%u"
- ";interop-constraints=%02X%02X%02X%02X%02X%02X"
- ";tx-mode=SST"
+ ";interop-constraints=%s"
";sprop-vps=%s"
";sprop-sps=%s"
";sprop-pps=%s\r\n";
unsigned fmtpFmtSize = strlen(fmtpFmt)
- + 3 /* max num chars: rtpPayloadType */ + 1 /* num chars: profile_space */
- + 2 /* max num chars: profile_id */
- + 1 /* num chars: tier_flag */
- + 3 /* max num chars: level_id */
- + 12 /* num chars: interop_constraints */
+ + 3 /* max num chars: rtpPayloadType */ + 20 /* max num chars: profile_space */
+ + 20 /* max num chars: profile_id */
+ + 20 /* max num chars: tier_flag */
+ + 20 /* max num chars: level_id */
+ + strlen(interopConstraintsStr)
+ strlen(sprop_vps)
+ strlen(sprop_sps)
+ strlen(sprop_pps);
char* fmtp = new char[fmtpFmtSize];
sprintf(fmtp, fmtpFmt,
- rtpPayloadType(), profile_space,
- profile_id,
- tier_flag,
- level_id,
- interop_constraints[0], interop_constraints[1], interop_constraints[2],
- interop_constraints[3], interop_constraints[4], interop_constraints[5],
+ rtpPayloadType(), profileSpace,
+ profileId,
+ tierFlag,
+ levelId,
+ interopConstraintsStr,
sprop_vps,
sprop_sps,
sprop_pps);
+
delete[] sprop_vps;
delete[] sprop_sps;
delete[] sprop_pps;
diff --git a/liveMedia/H265VideoRTPSource.cpp b/liveMedia/H265VideoRTPSource.cpp
new file mode 100644
index 0000000..a29b6a9
--- /dev/null
+++ b/liveMedia/H265VideoRTPSource.cpp
@@ -0,0 +1,218 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// H.265 Video RTP Sources
+// Implementation
+
+#include "H265VideoRTPSource.hh"
+
+////////// H265BufferedPacket and H265BufferedPacketFactory //////////
+
+class H265BufferedPacket: public BufferedPacket {
+public:
+ H265BufferedPacket(H265VideoRTPSource& ourSource);
+ virtual ~H265BufferedPacket();
+
+private: // redefined virtual functions
+ virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
+ unsigned dataSize);
+private:
+ H265VideoRTPSource& fOurSource;
+};
+
+class H265BufferedPacketFactory: public BufferedPacketFactory {
+private: // redefined virtual functions
+ virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
+};
+
+
+///////// H265VideoRTPSource implementation ////////
+
+H265VideoRTPSource*
+H265VideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat,
+ Boolean expectDONFields,
+ unsigned rtpTimestampFrequency) {
+ return new H265VideoRTPSource(env, RTPgs, rtpPayloadFormat,
+ expectDONFields, rtpTimestampFrequency);
+}
+
+H265VideoRTPSource
+::H265VideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat,
+ Boolean expectDONFields,
+ unsigned rtpTimestampFrequency)
+ : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency,
+ new H265BufferedPacketFactory),
+ fExpectDONFields(expectDONFields),
+ fPreviousNALUnitDON(0), fCurrentNALUnitAbsDon((u_int64_t)(~0)) {
+}
+
+H265VideoRTPSource::~H265VideoRTPSource() {
+}
+
+Boolean H265VideoRTPSource
+::processSpecialHeader(BufferedPacket* packet,
+ unsigned& resultSpecialHeaderSize) {
+ unsigned char* headerStart = packet->data();
+ unsigned packetSize = packet->dataSize();
+ u_int16_t DONL = 0;
+ unsigned numBytesToSkip;
+
+ // Check the Payload Header's 'nal_unit_type' for special aggregation or fragmentation packets:
+ if (packetSize < 2) return False;
+ fCurPacketNALUnitType = (headerStart[0]&0x7E)>>1;
+ switch (fCurPacketNALUnitType) {
+ case 48: { // Aggregation Packet (AP)
+ // We skip over the 2-byte Payload Header, and the DONL header (if any).
+ if (fExpectDONFields) {
+ if (packetSize < 4) return False;
+ DONL = (headerStart[2]<<8)|headerStart[3];
+ numBytesToSkip = 4;
+ } else {
+ numBytesToSkip = 2;
+ }
+ break;
+ }
+ case 49: { // Fragmentation Unit (FU)
+ // This NALU begins with the 2-byte Payload Header, the 1-byte FU header, and (optionally)
+ // the 2-byte DONL header.
+ // If the start bit is set, we reconstruct the original NAL header at the end of these
+ // 3 (or 5) bytes, and skip over the first 1 (or 3) bytes.
+ if (packetSize < 3) return False;
+ u_int8_t startBit = headerStart[2]&0x80; // from the FU header
+ u_int8_t endBit = headerStart[2]&0x40; // from the FU header
+ if (startBit) {
+ fCurrentPacketBeginsFrame = True;
+
+ u_int8_t nal_unit_type = headerStart[2]&0x3F; // the last 6 bits of the FU header
+ u_int8_t newNALHeader[2];
+ newNALHeader[0] = (headerStart[0]&0x81)|(nal_unit_type<<1);
+ newNALHeader[1] = headerStart[1];
+
+ if (fExpectDONFields) {
+ if (packetSize < 5) return False;
+ DONL = (headerStart[3]<<8)|headerStart[4];
+ headerStart[3] = newNALHeader[0];
+ headerStart[4] = newNALHeader[1];
+ numBytesToSkip = 3;
+ } else {
+ headerStart[1] = newNALHeader[0];
+ headerStart[2] = newNALHeader[1];
+ numBytesToSkip = 1;
+ }
+ } else {
+ // The start bit is not set, so we skip over all headers:
+ fCurrentPacketBeginsFrame = False;
+ if (fExpectDONFields) {
+ if (packetSize < 5) return False;
+ DONL = (headerStart[3]<<8)|headerStart[4];
+ numBytesToSkip = 5;
+ } else {
+ numBytesToSkip = 3;
+ }
+ }
+ fCurrentPacketCompletesFrame = (endBit != 0);
+ break;
+ }
+ default: {
+ // This packet contains one complete NAL unit:
+ fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame = True;
+ numBytesToSkip = 0;
+ break;
+ }
+ }
+
+ computeAbsDonFromDON(DONL);
+ resultSpecialHeaderSize = numBytesToSkip;
+ return True;
+}
+
+char const* H265VideoRTPSource::MIMEtype() const {
+ return "video/H265";
+}
+
+void H265VideoRTPSource::computeAbsDonFromDON(u_int16_t DON) {
+ if (!fExpectDONFields) {
+ // Without DON fields in the input stream, we just increment our "AbsDon" count each time:
+ ++fCurrentNALUnitAbsDon;
+ } else {
+ if (fCurrentNALUnitAbsDon == (u_int64_t)(~0)) {
+ // This is the very first NAL unit, so "AbsDon" is just "DON":
+ fCurrentNALUnitAbsDon = (u_int64_t)DON;
+ } else {
+ // Use the previous NAL unit's DON and the current DON to compute "AbsDon":
+ // AbsDon[n] = AbsDon[n-1] + (DON[n] - DON[n-1]) mod 2^16
+ short signedDiff16 = (short)(DON - fPreviousNALUnitDON);
+ int64_t signedDiff64 = (int64_t)signedDiff16;
+ fCurrentNALUnitAbsDon += signedDiff64;
+ }
+
+ fPreviousNALUnitDON = DON; // for next time
+ }
+}
+
+
+////////// H265BufferedPacket and H265BufferedPacketFactory implementation //////////
+
+H265BufferedPacket::H265BufferedPacket(H265VideoRTPSource& ourSource)
+ : fOurSource(ourSource) {
+}
+
+H265BufferedPacket::~H265BufferedPacket() {
+}
+
+unsigned H265BufferedPacket
+::nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
+ unsigned resultNALUSize = 0; // if an error occurs
+
+ switch (fOurSource.fCurPacketNALUnitType) {
+ case 48: { // Aggregation Packet (AP)
+ if (useCount() > 0) {
+ // We're other than the first NAL unit inside this Aggregation Packet.
+ // Update our 'decoding order number':
+ u_int16_t DONL = 0;
+ if (fOurSource.fExpectDONFields) {
+ // There's a 1-byte DOND field next:
+ if (dataSize < 1) break;
+ u_int8_t DOND = framePtr[0];
+ DONL = fOurSource.fPreviousNALUnitDON + (u_int16_t)(DOND + 1);
+ ++framePtr;
+ --dataSize;
+ }
+ fOurSource.computeAbsDonFromDON(DONL);
+ }
+
+ // The next 2 bytes are the NAL unit size:
+ if (dataSize < 2) break;
+ resultNALUSize = (framePtr[0]<<8)|framePtr[1];
+ framePtr += 2;
+ break;
+ }
+ default: {
+ // Common case: We use the entire packet data:
+ return dataSize;
+ }
+ }
+
+ return (resultNALUSize <= dataSize) ? resultNALUSize : dataSize;
+}
+
+BufferedPacket* H265BufferedPacketFactory
+::createNewPacket(MultiFramedRTPSource* ourSource) {
+ return new H265BufferedPacket((H265VideoRTPSource&)(*ourSource));
+}
diff --git a/liveMedia/H265VideoStreamDiscreteFramer.cpp b/liveMedia/H265VideoStreamDiscreteFramer.cpp
index 3611e2b..c6b8cf0 100644
--- a/liveMedia/H265VideoStreamDiscreteFramer.cpp
+++ b/liveMedia/H265VideoStreamDiscreteFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "H265VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/H265VideoStreamFramer.cpp b/liveMedia/H265VideoStreamFramer.cpp
index f69f0ec..02d52bf 100644
--- a/liveMedia/H265VideoStreamFramer.cpp
+++ b/liveMedia/H265VideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up a H.265 Video Elementary Stream into NAL units.
// Implementation
diff --git a/liveMedia/InputFile.cpp b/liveMedia/InputFile.cpp
index 96ee20c..b91fb81 100644
--- a/liveMedia/InputFile.cpp
+++ b/liveMedia/InputFile.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Common routines for opening/closing named input files
// Implementation
diff --git a/liveMedia/JPEGVideoRTPSink.cpp b/liveMedia/JPEGVideoRTPSink.cpp
index 60b694f..9076e67 100644
--- a/liveMedia/JPEGVideoRTPSink.cpp
+++ b/liveMedia/JPEGVideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for JPEG video (RFC 2435)
// Implementation
diff --git a/liveMedia/JPEGVideoRTPSource.cpp b/liveMedia/JPEGVideoRTPSource.cpp
index 8b4dde3..4e92427 100644
--- a/liveMedia/JPEGVideoRTPSource.cpp
+++ b/liveMedia/JPEGVideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// JPEG Video (RFC 2435) RTP Sources
// Implementation
diff --git a/liveMedia/JPEGVideoSource.cpp b/liveMedia/JPEGVideoSource.cpp
index 1ff853f..fa7e587 100644
--- a/liveMedia/JPEGVideoSource.cpp
+++ b/liveMedia/JPEGVideoSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// JPEG video sources
// Implementation
diff --git a/liveMedia/Locale.cpp b/liveMedia/Locale.cpp
index 0bf1963..8e675de 100644
--- a/liveMedia/Locale.cpp
+++ b/liveMedia/Locale.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Support for temporarily setting the locale (e.g., to "C" or "POSIX") for (e.g.) parsing or printing
// floating-point numbers in protocol headers, or calling toupper()/tolower() on human-input strings.
// Implementation
diff --git a/liveMedia/MP3ADU.cpp b/liveMedia/MP3ADU.cpp
index e398149..14c6605 100644
--- a/liveMedia/MP3ADU.cpp
+++ b/liveMedia/MP3ADU.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// 'ADU' MP3 streams (for improved loss-tolerance)
// Implementation
@@ -162,7 +162,7 @@ void ADUFromMP3Source::doGetNextFrame() {
if (!doGetNextFrame1()) {
// An internal error occurred; act as if our source went away:
- FramedSource::handleClosure(this);
+ handleClosure();
}
}
}
@@ -512,7 +512,7 @@ void SegmentQueue::enqueueNewSegment(FramedSource* inputSource,
FramedSource* usingSource) {
if (isFull()) {
usingSource->envir() << "SegmentQueue::enqueueNewSegment() overflow\n";
- FramedSource::handleClosure(usingSource);
+ usingSource->handleClosure();
return;
}
diff --git a/liveMedia/MP3ADURTPSink.cpp b/liveMedia/MP3ADURTPSink.cpp
index 483c0b6..b52b32b 100644
--- a/liveMedia/MP3ADURTPSink.cpp
+++ b/liveMedia/MP3ADURTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for 'ADUized' MP3 frames ("mpa-robust")
// Implementation
diff --git a/liveMedia/MP3ADURTPSource.cpp b/liveMedia/MP3ADURTPSource.cpp
index 4229e98..696cafd 100644
--- a/liveMedia/MP3ADURTPSource.cpp
+++ b/liveMedia/MP3ADURTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP source for 'ADUized' MP3 frames ("mpa-robust")
// Implementation
diff --git a/liveMedia/MP3ADUTranscoder.cpp b/liveMedia/MP3ADUTranscoder.cpp
index 4d3a9d5..4557a9e 100644
--- a/liveMedia/MP3ADUTranscoder.cpp
+++ b/liveMedia/MP3ADUTranscoder.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Transcoder for ADUized MP3 frames
// Implementation
@@ -82,7 +82,7 @@ void MP3ADUTranscoder::afterGettingFrame1(unsigned numBytesRead,
fFrameSize = TranscodeMP3ADU(fOrigADU, numBytesRead, fOutBitrate,
fTo, fMaxSize, fAvailableBytesForBackpointer);
if (fFrameSize == 0) { // internal error - bad ADU data?
- handleClosure(this);
+ handleClosure();
return;
}
diff --git a/liveMedia/MP3ADUdescriptor.cpp b/liveMedia/MP3ADUdescriptor.cpp
index fffa087..5669647 100644
--- a/liveMedia/MP3ADUdescriptor.cpp
+++ b/liveMedia/MP3ADUdescriptor.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Descriptor preceding frames of 'ADU' MP3 streams (for improved loss-tolerance)
// Implementation
diff --git a/liveMedia/MP3ADUdescriptor.hh b/liveMedia/MP3ADUdescriptor.hh
index a87fbf3..c4576be 100644
--- a/liveMedia/MP3ADUdescriptor.hh
+++ b/liveMedia/MP3ADUdescriptor.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Descriptor preceding frames of 'ADU' MP3 streams (for improved loss-tolerance)
// C++ header
diff --git a/liveMedia/MP3ADUinterleaving.cpp b/liveMedia/MP3ADUinterleaving.cpp
index 723740d..eb3077e 100644
--- a/liveMedia/MP3ADUinterleaving.cpp
+++ b/liveMedia/MP3ADUinterleaving.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Interleaving of MP3 ADUs
// Implementation
diff --git a/liveMedia/MP3AudioFileServerMediaSubsession.cpp b/liveMedia/MP3AudioFileServerMediaSubsession.cpp
index f5b0cba..30460c0 100644
--- a/liveMedia/MP3AudioFileServerMediaSubsession.cpp
+++ b/liveMedia/MP3AudioFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MP3 audio file.
// (Actually, any MPEG-1 or MPEG-2 audio file should work.)
diff --git a/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.cpp b/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.cpp
index 53bdb90..4f86d9d 100644
--- a/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.cpp
+++ b/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.cpp
@@ -14,25 +14,27 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an MP3 audio track within a Matroska file.
// (Actually, MPEG-1 or MPEG-2 audio file should also work.)
// Implementation
+#include "FileServerMediaSubsession.hh"
#include "MP3AudioMatroskaFileServerMediaSubsession.hh"
#include "MatroskaDemuxedTrack.hh"
MP3AudioMatroskaFileServerMediaSubsession* MP3AudioMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber, Boolean generateADUs, Interleaving* interleaving) {
- return new MP3AudioMatroskaFileServerMediaSubsession(demux, trackNumber, generateADUs, interleaving);
+::createNew(MatroskaFileServerDemux& demux, MatroskaTrack* track,
+ Boolean generateADUs, Interleaving* interleaving) {
+ return new MP3AudioMatroskaFileServerMediaSubsession(demux, track, generateADUs, interleaving);
}
MP3AudioMatroskaFileServerMediaSubsession
-::MP3AudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber,
+::MP3AudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, MatroskaTrack* track,
Boolean generateADUs, Interleaving* interleaving)
: MP3AudioFileServerMediaSubsession(demux.envir(), demux.fileName(), False, generateADUs, interleaving),
- fOurDemux(demux), fTrackNumber(trackNumber) {
+ fOurDemux(demux), fTrackNumber(track->trackNumber) {
fFileDuration = fOurDemux.fileDuration();
}
diff --git a/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.hh b/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.hh
index c429c67..5216997 100644
--- a/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.hh
+++ b/liveMedia/MP3AudioMatroskaFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an MP3 audio track within a Matroska file.
// (Actually, MPEG-1 or MPEG-2 audio should also work.)
@@ -33,12 +33,13 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
class MP3AudioMatroskaFileServerMediaSubsession: public MP3AudioFileServerMediaSubsession {
public:
static MP3AudioMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber, Boolean generateADUs, Interleaving* interleaving);
+ createNew(MatroskaFileServerDemux& demux, MatroskaTrack* track,
+ Boolean generateADUs = False, Interleaving* interleaving = NULL);
// Note: "interleaving" is used only if "generateADUs" is True,
// (and a value of NULL means 'no interleaving')
private:
- MP3AudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber,
+ MP3AudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, MatroskaTrack* track,
Boolean generateADUs, Interleaving* interleaving);
// called only by createNew();
virtual ~MP3AudioMatroskaFileServerMediaSubsession();
diff --git a/liveMedia/MP3FileSource.cpp b/liveMedia/MP3FileSource.cpp
index d6ddebd..84fcf6c 100644
--- a/liveMedia/MP3FileSource.cpp
+++ b/liveMedia/MP3FileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 File Sources
// Implementation
@@ -99,7 +99,6 @@ void MP3FileSource::seekWithinFile(double seekNPT, double streamDuration) {
fNumBytesToStream = endByteNumber - seekByteNumber;
fLimitNumBytesToStream = True;
}
- } else {
}
}
@@ -111,7 +110,7 @@ void MP3FileSource::getAttributes() const {
void MP3FileSource::doGetNextFrame() {
if (!doGetNextFrame1()) {
- handleClosure(this);
+ handleClosure();
return;
}
diff --git a/liveMedia/MP3Internals.cpp b/liveMedia/MP3Internals.cpp
index 2bb71f2..80f3f7b 100644
--- a/liveMedia/MP3Internals.cpp
+++ b/liveMedia/MP3Internals.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 internal implementation details
// Implementation
diff --git a/liveMedia/MP3Internals.hh b/liveMedia/MP3Internals.hh
index 28472de..90724f5 100644
--- a/liveMedia/MP3Internals.hh
+++ b/liveMedia/MP3Internals.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 internal implementation details
// C++ header
diff --git a/liveMedia/MP3InternalsHuffman.cpp b/liveMedia/MP3InternalsHuffman.cpp
index e88bde3..3f578ad 100644
--- a/liveMedia/MP3InternalsHuffman.cpp
+++ b/liveMedia/MP3InternalsHuffman.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 internal implementation details (Huffman encoding)
// Implementation
diff --git a/liveMedia/MP3InternalsHuffman.hh b/liveMedia/MP3InternalsHuffman.hh
index 926cfab..ce7d989 100644
--- a/liveMedia/MP3InternalsHuffman.hh
+++ b/liveMedia/MP3InternalsHuffman.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 internal implementation details (Huffman encoding)
// C++ header
diff --git a/liveMedia/MP3InternalsHuffmanTable.cpp b/liveMedia/MP3InternalsHuffmanTable.cpp
index 8ba869d..e1902ab 100644
--- a/liveMedia/MP3InternalsHuffmanTable.cpp
+++ b/liveMedia/MP3InternalsHuffmanTable.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 internal implementation details (Huffman encoding)
// Table
diff --git a/liveMedia/MP3StreamState.cpp b/liveMedia/MP3StreamState.cpp
index ddcbf52..c5aac48 100644
--- a/liveMedia/MP3StreamState.cpp
+++ b/liveMedia/MP3StreamState.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class encapsulating the state of a MP3 stream
// Implementation
diff --git a/liveMedia/MP3StreamState.hh b/liveMedia/MP3StreamState.hh
index 9ce5f49..0358f8f 100644
--- a/liveMedia/MP3StreamState.hh
+++ b/liveMedia/MP3StreamState.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class encapsulating the state of a MP3 stream
// C++ header
diff --git a/liveMedia/MP3Transcoder.cpp b/liveMedia/MP3Transcoder.cpp
index 8c437d4..ec82c17 100644
--- a/liveMedia/MP3Transcoder.cpp
+++ b/liveMedia/MP3Transcoder.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 Transcoder
// Implementation
diff --git a/liveMedia/MPEG1or2AudioRTPSink.cpp b/liveMedia/MPEG1or2AudioRTPSink.cpp
index 316cadb..ec9585e 100644
--- a/liveMedia/MPEG1or2AudioRTPSink.cpp
+++ b/liveMedia/MPEG1or2AudioRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG audio (RFC 2250)
// Implementation
diff --git a/liveMedia/MPEG1or2AudioRTPSource.cpp b/liveMedia/MPEG1or2AudioRTPSource.cpp
index c3773a5..f43930b 100644
--- a/liveMedia/MPEG1or2AudioRTPSource.cpp
+++ b/liveMedia/MPEG1or2AudioRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG-1 or MPEG-2 Audio RTP Sources
// Implementation
diff --git a/liveMedia/MPEG1or2AudioStreamFramer.cpp b/liveMedia/MPEG1or2AudioStreamFramer.cpp
index 26fcee5..e7539f9 100644
--- a/liveMedia/MPEG1or2AudioStreamFramer.cpp
+++ b/liveMedia/MPEG1or2AudioStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG (1,2) audio elementary stream into frames
// Implementation
diff --git a/liveMedia/MPEG1or2Demux.cpp b/liveMedia/MPEG1or2Demux.cpp
index 0e8ee73..8a507eb 100644
--- a/liveMedia/MPEG1or2Demux.cpp
+++ b/liveMedia/MPEG1or2Demux.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Demultiplexer for a MPEG 1 or 2 Program Stream
// Implementation
@@ -160,8 +160,7 @@ void MPEG1or2Demux::registerReadInterest(u_int8_t streamIdTag,
// Make sure this stream is not already being read:
if (out.isCurrentlyAwaitingData) {
- envir() << "MPEG1or2Demux::registerReadInterest(): attempt to read stream id "
- << (void*)streamIdTag << " more than once!\n";
+ envir() << "MPEG1or2Demux::registerReadInterest(): attempt to read stream more than once!\n";
envir().internalError();
}
@@ -459,9 +458,7 @@ void MPEGProgramStreamParser::parsePackHeader() {
unsigned char pack_stuffing_length = getBits(3);
skipBytes(pack_stuffing_length);
} else { // unknown
- fUsingDemux->envir() << "StreamParser::parsePack() saw strange byte "
- << (void*)nextByte
- << " following pack_start_code\n";
+ fUsingDemux->envir() << "StreamParser::parsePack() saw strange byte following pack_start_code\n";
}
// Check for a System Header next:
diff --git a/liveMedia/MPEG1or2DemuxedElementaryStream.cpp b/liveMedia/MPEG1or2DemuxedElementaryStream.cpp
index b0d9020..23fb0bb 100644
--- a/liveMedia/MPEG1or2DemuxedElementaryStream.cpp
+++ b/liveMedia/MPEG1or2DemuxedElementaryStream.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A MPEG 1 or 2 Elementary Stream, demultiplexed from a Program Stream
// Implementation
diff --git a/liveMedia/MPEG1or2DemuxedServerMediaSubsession.cpp b/liveMedia/MPEG1or2DemuxedServerMediaSubsession.cpp
index d6b32d8..724bc65 100644
--- a/liveMedia/MPEG1or2DemuxedServerMediaSubsession.cpp
+++ b/liveMedia/MPEG1or2DemuxedServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-1 or 2 demuxer.
// Implementation
diff --git a/liveMedia/MPEG1or2FileServerDemux.cpp b/liveMedia/MPEG1or2FileServerDemux.cpp
index 036b128..e22f8ae 100644
--- a/liveMedia/MPEG1or2FileServerDemux.cpp
+++ b/liveMedia/MPEG1or2FileServerDemux.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A server demultiplexer for a MPEG 1 or 2 Program Stream
// Implementation
diff --git a/liveMedia/MPEG1or2VideoFileServerMediaSubsession.cpp b/liveMedia/MPEG1or2VideoFileServerMediaSubsession.cpp
index ba86f13..eeaf456 100644
--- a/liveMedia/MPEG1or2VideoFileServerMediaSubsession.cpp
+++ b/liveMedia/MPEG1or2VideoFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-1 or 2 Elementary Stream video file.
// Implementation
diff --git a/liveMedia/MPEG1or2VideoRTPSink.cpp b/liveMedia/MPEG1or2VideoRTPSink.cpp
index 8b597ac..646a942 100644
--- a/liveMedia/MPEG1or2VideoRTPSink.cpp
+++ b/liveMedia/MPEG1or2VideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG video (RFC 2250)
// Implementation
diff --git a/liveMedia/MPEG1or2VideoRTPSource.cpp b/liveMedia/MPEG1or2VideoRTPSource.cpp
index 8b7eed0..0a7d613 100644
--- a/liveMedia/MPEG1or2VideoRTPSource.cpp
+++ b/liveMedia/MPEG1or2VideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG-1 or MPEG-2 Video RTP Sources
// Implementation
diff --git a/liveMedia/MPEG1or2VideoStreamDiscreteFramer.cpp b/liveMedia/MPEG1or2VideoStreamDiscreteFramer.cpp
index ded2d83..62b80ab 100644
--- a/liveMedia/MPEG1or2VideoStreamDiscreteFramer.cpp
+++ b/liveMedia/MPEG1or2VideoStreamDiscreteFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "MPEG1or2VideoStreamFramer" that takes only
// complete, discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/MPEG1or2VideoStreamFramer.cpp b/liveMedia/MPEG1or2VideoStreamFramer.cpp
index fa8f7ce..836b565 100644
--- a/liveMedia/MPEG1or2VideoStreamFramer.cpp
+++ b/liveMedia/MPEG1or2VideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG 1 or 2 video elementary stream into
// frames for: Video_Sequence_Header, GOP_Header, Picture_Header
// Implementation
diff --git a/liveMedia/MPEG2IndexFromTransportStream.cpp b/liveMedia/MPEG2IndexFromTransportStream.cpp
index e9005b4..084e3f8 100644
--- a/liveMedia/MPEG2IndexFromTransportStream.cpp
+++ b/liveMedia/MPEG2IndexFromTransportStream.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that produces a sequence of I-frame indices from a MPEG-2 Transport Stream
// Implementation
@@ -202,7 +202,13 @@ void MPEG2IFrameIndexFromTransportStream
// Figure out how much of this Transport Packet contains PES data:
u_int8_t adaptation_field_control = (fInputBuffer[3]&0x30)>>4;
u_int8_t totalHeaderSize
- = adaptation_field_control == 1 ? 4 : 5 + fInputBuffer[4];
+ = adaptation_field_control <= 1 ? 4 : 5 + fInputBuffer[4];
+ if ((adaptation_field_control == 2 && totalHeaderSize != TRANSPORT_PACKET_SIZE) ||
+ (adaptation_field_control == 3 && totalHeaderSize >= TRANSPORT_PACKET_SIZE)) {
+ envir() << "Bad \"adaptation_field_length\": " << fInputBuffer[4] << "\n";
+ doGetNextFrame();
+ return;
+ }
// Check for a PCR:
if (totalHeaderSize > 5 && (fInputBuffer[5]&0x10) != 0) {
@@ -241,7 +247,7 @@ void MPEG2IFrameIndexFromTransportStream
// or packets with no data, or packets that duplicate the previous packet:
u_int8_t continuity_counter = fInputBuffer[3]&0x0F;
if ((PID != fVideo_PID) ||
- !(adaptation_field_control == 1 || adaptation_field_control == 3) ||
+ !(adaptation_field_control == 1 || adaptation_field_control == 3) ||
continuity_counter == fLastContinuityCounter) {
doGetNextFrame();
return;
@@ -250,8 +256,9 @@ void MPEG2IFrameIndexFromTransportStream
// Also, if this is the start of a PES packet, then skip over the PES header:
Boolean payload_unit_start_indicator = (fInputBuffer[1]&0x40) != 0;
- if (payload_unit_start_indicator) {
- // Note: The following works only for MPEG-2 data #####
+ if (payload_unit_start_indicator && totalHeaderSize < TRANSPORT_PACKET_SIZE - 8
+ && fInputBuffer[totalHeaderSize] == 0x00 && fInputBuffer[totalHeaderSize+1] == 0x00
+ && fInputBuffer[totalHeaderSize+2] == 0x01) {
u_int8_t PES_header_data_length = fInputBuffer[totalHeaderSize+8];
totalHeaderSize += 9 + PES_header_data_length;
if (totalHeaderSize >= TRANSPORT_PACKET_SIZE) {
@@ -303,7 +310,7 @@ void MPEG2IFrameIndexFromTransportStream::handleInputClosure1() {
doGetNextFrame();
} else {
// Handle closure in the regular way:
- FramedSource::handleClosure(this);
+ handleClosure();
}
}
diff --git a/liveMedia/MPEG2TransportFileServerMediaSubsession.cpp b/liveMedia/MPEG2TransportFileServerMediaSubsession.cpp
index b113aaf..65210b9 100644
--- a/liveMedia/MPEG2TransportFileServerMediaSubsession.cpp
+++ b/liveMedia/MPEG2TransportFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-2 Transport Stream file.
// Implementation
diff --git a/liveMedia/MPEG2TransportStreamFramer.cpp b/liveMedia/MPEG2TransportStreamFramer.cpp
index d0be467..18c7fb1 100644
--- a/liveMedia/MPEG2TransportStreamFramer.cpp
+++ b/liveMedia/MPEG2TransportStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that passes through (unchanged) chunks that contain an integral number
// of MPEG-2 Transport Stream packets, but returning (in "fDurationInMicroseconds")
// an updated estimate of the time gap between chunks.
@@ -104,7 +104,7 @@ void MPEG2TransportStreamFramer::setPCRLimit(float pcrLimit) {
void MPEG2TransportStreamFramer::doGetNextFrame() {
if (fLimitNumTSPacketsToStream) {
if (fNumTSPacketsToStream == 0) {
- handleClosure(this);
+ handleClosure();
return;
}
if (fNumTSPacketsToStream*TRANSPORT_PACKET_SIZE < fMaxSize) {
@@ -146,7 +146,7 @@ void MPEG2TransportStreamFramer::afterGettingFrame1(unsigned frameSize,
fFrameSize = numTSPackets*TRANSPORT_PACKET_SIZE; // an integral # of TS packets
if (fFrameSize == 0) {
// We didn't read a complete TS packet; assume that the input source has closed.
- handleClosure(this);
+ handleClosure();
return;
}
@@ -157,7 +157,7 @@ void MPEG2TransportStreamFramer::afterGettingFrame1(unsigned frameSize,
}
if (syncBytePosition == fFrameSize) {
envir() << "No Transport Stream sync byte in data.";
- handleClosure(this);
+ handleClosure();
return;
} else if (syncBytePosition > 0) {
// There's a sync byte, but not at the start of the data. Move the good data
@@ -180,7 +180,7 @@ void MPEG2TransportStreamFramer::afterGettingFrame1(unsigned frameSize,
for (unsigned i = 0; i < numTSPackets; ++i) {
if (!updateTSPacketDurationEstimate(&fTo[i*TRANSPORT_PACKET_SIZE], timeNow)) {
// We hit a preset limit (based on PCR) within the stream. Handle this as if the input source has closed:
- handleClosure(this);
+ handleClosure();
return;
}
}
diff --git a/liveMedia/MPEG2TransportStreamFromESSource.cpp b/liveMedia/MPEG2TransportStreamFromESSource.cpp
index b189d9f..4008e8b 100644
--- a/liveMedia/MPEG2TransportStreamFromESSource.cpp
+++ b/liveMedia/MPEG2TransportStreamFromESSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter for converting one or more MPEG Elementary Streams
// to a MPEG-2 Transport Stream
// Implementation
@@ -33,7 +33,7 @@ public:
InputESSourceRecord(MPEG2TransportStreamFromESSource& parent,
FramedSource* inputSource,
u_int8_t streamId, int mpegVersion,
- InputESSourceRecord* next);
+ InputESSourceRecord* next, int16_t PID = -1);
virtual ~InputESSourceRecord();
InputESSourceRecord* next() const { return fNext; }
@@ -68,6 +68,7 @@ private:
unsigned fInputBufferBytesAvailable;
Boolean fInputBufferInUse;
MPEG1or2Demux::SCR fSCR;
+ int16_t fPID;
};
@@ -79,22 +80,23 @@ MPEG2TransportStreamFromESSource* MPEG2TransportStreamFromESSource
}
void MPEG2TransportStreamFromESSource
-::addNewVideoSource(FramedSource* inputSource, int mpegVersion) {
+::addNewVideoSource(FramedSource* inputSource, int mpegVersion, int16_t PID) {
u_int8_t streamId = 0xE0 | (fVideoSourceCounter++&0x0F);
- addNewInputSource(inputSource, streamId, mpegVersion);
+ addNewInputSource(inputSource, streamId, mpegVersion, PID);
fHaveVideoStreams = True;
}
void MPEG2TransportStreamFromESSource
-::addNewAudioSource(FramedSource* inputSource, int mpegVersion) {
+::addNewAudioSource(FramedSource* inputSource, int mpegVersion, int16_t PID) {
u_int8_t streamId = 0xC0 | (fAudioSourceCounter++&0x0F);
- addNewInputSource(inputSource, streamId, mpegVersion);
+ addNewInputSource(inputSource, streamId, mpegVersion, PID);
}
MPEG2TransportStreamFromESSource
::MPEG2TransportStreamFromESSource(UsageEnvironment& env)
: MPEG2TransportStreamMultiplexor(env),
- fInputSources(NULL), fVideoSourceCounter(0), fAudioSourceCounter(0) {
+ fInputSources(NULL), fVideoSourceCounter(0), fAudioSourceCounter(0),
+ fAwaitingBackgroundDelivery(False) {
fHaveVideoStreams = False; // unless we add a video source
}
@@ -123,14 +125,16 @@ void MPEG2TransportStreamFromESSource
break;
}
}
+ fAwaitingBackgroundDelivery = False;
}
if (isCurrentlyAwaitingData()) {
// Try to deliver one filled-in buffer to the client:
for (sourceRec = fInputSources; sourceRec != NULL;
sourceRec = sourceRec->next()) {
- if (sourceRec->deliverBufferToClient()) break;
+ if (sourceRec->deliverBufferToClient()) return;
}
+ fAwaitingBackgroundDelivery = True;
}
// No filled-in buffers are available. Ask each of our inputs for data:
@@ -138,15 +142,14 @@ void MPEG2TransportStreamFromESSource
sourceRec = sourceRec->next()) {
sourceRec->askForNewData();
}
-
}
void MPEG2TransportStreamFromESSource
::addNewInputSource(FramedSource* inputSource,
- u_int8_t streamId, int mpegVersion) {
+ u_int8_t streamId, int mpegVersion, int16_t PID) {
if (inputSource == NULL) return;
fInputSources = new InputESSourceRecord(*this, inputSource, streamId,
- mpegVersion, fInputSources);
+ mpegVersion, fInputSources, PID);
}
@@ -156,9 +159,9 @@ InputESSourceRecord
::InputESSourceRecord(MPEG2TransportStreamFromESSource& parent,
FramedSource* inputSource,
u_int8_t streamId, int mpegVersion,
- InputESSourceRecord* next)
+ InputESSourceRecord* next, int16_t PID)
: fNext(next), fParent(parent), fInputSource(inputSource),
- fStreamId(streamId), fMPEGVersion(mpegVersion) {
+ fStreamId(streamId), fMPEGVersion(mpegVersion), fPID(PID) {
fInputBuffer = new unsigned char[INPUT_BUFFER_SIZE];
reset();
}
@@ -216,7 +219,7 @@ Boolean InputESSourceRecord::deliverBufferToClient() {
// Do the delivery:
fParent.handleNewBuffer(fInputBuffer, fInputBufferBytesAvailable,
- fMPEGVersion, fSCR);
+ fMPEGVersion, fSCR, fPID);
return True;
}
@@ -255,5 +258,8 @@ void InputESSourceRecord
fParent.fPresentationTime = presentationTime;
// Now that we have new input data, check if we can deliver to the client:
- fParent.awaitNewBuffer(NULL);
+ if (fParent.fAwaitingBackgroundDelivery) {
+ fParent.fAwaitingBackgroundDelivery = False;
+ fParent.awaitNewBuffer(NULL);
+ }
}
diff --git a/liveMedia/MPEG2TransportStreamFromPESSource.cpp b/liveMedia/MPEG2TransportStreamFromPESSource.cpp
index 7e08614..a936800 100644
--- a/liveMedia/MPEG2TransportStreamFromPESSource.cpp
+++ b/liveMedia/MPEG2TransportStreamFromPESSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter for converting a stream of MPEG PES packets to a MPEG-2 Transport Stream
// Implementation
diff --git a/liveMedia/MPEG2TransportStreamIndexFile.cpp b/liveMedia/MPEG2TransportStreamIndexFile.cpp
index 99187e9..dd6f754 100644
--- a/liveMedia/MPEG2TransportStreamIndexFile.cpp
+++ b/liveMedia/MPEG2TransportStreamIndexFile.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class that encapsulates MPEG-2 Transport Stream 'index files'/
// These index files are used to implement 'trick play' operations
// (seek-by-time, fast forward, reverse play) on Transport Stream files.
diff --git a/liveMedia/MPEG2TransportStreamMultiplexor.cpp b/liveMedia/MPEG2TransportStreamMultiplexor.cpp
index 81ffb90..0be578d 100644
--- a/liveMedia/MPEG2TransportStreamMultiplexor.cpp
+++ b/liveMedia/MPEG2TransportStreamMultiplexor.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class for generating MPEG-2 Transport Stream from one or more input
// Elementary Stream data sources
// Implementation
@@ -91,7 +91,7 @@ void MPEG2TransportStreamMultiplexor::doGetNextFrame() {
void MPEG2TransportStreamMultiplexor
::handleNewBuffer(unsigned char* buffer, unsigned bufferSize,
- int mpegVersion, MPEG1or2Demux::SCR scr) {
+ int mpegVersion, MPEG1or2Demux::SCR scr, int16_t PID) {
if (bufferSize < 4) return;
fInputBuffer = buffer;
fInputBufferSize = bufferSize;
@@ -106,7 +106,10 @@ void MPEG2TransportStreamMultiplexor
setProgramStreamMap(fInputBufferSize);
fInputBufferSize = 0; // then, ignore the buffer
} else {
- fCurrentPID = stream_id;
+ if (PID == -1)
+ fCurrentPID = stream_id;
+ else
+ fCurrentPID = (u_int8_t)PID;
// Set the stream's type:
u_int8_t& streamType = fPIDState[fCurrentPID].streamType; // alias
@@ -234,10 +237,10 @@ void MPEG2TransportStreamMultiplexor
}
}
-static u_int32_t calculateCRC(u_int8_t* data, unsigned dataLength); // forward
-
#define PAT_PID 0
+#ifndef OUR_PROGRAM_NUMBER
#define OUR_PROGRAM_NUMBER 1
+#endif
#define OUR_PROGRAM_MAP_PID 0x30
void MPEG2TransportStreamMultiplexor::deliverPATPacket() {
@@ -427,8 +430,8 @@ static u_int32_t const CRC32[256] = {
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
-static u_int32_t calculateCRC(u_int8_t* data, unsigned dataLength) {
- u_int32_t crc = 0xFFFFFFFF;
+u_int32_t calculateCRC(u_int8_t const* data, unsigned dataLength, u_int32_t initialValue) {
+ u_int32_t crc = initialValue;
while (dataLength-- > 0) {
crc = (crc<<8) ^ CRC32[(crc>>24) ^ (u_int32_t)(*data++)];
diff --git a/liveMedia/MPEG2TransportStreamTrickModeFilter.cpp b/liveMedia/MPEG2TransportStreamTrickModeFilter.cpp
index 88d897c..5c4125a 100644
--- a/liveMedia/MPEG2TransportStreamTrickModeFilter.cpp
+++ b/liveMedia/MPEG2TransportStreamTrickModeFilter.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that converts a MPEG Transport Stream file - with corresponding index file
// - to a corresponding Video Elementary Stream. It also uses a "scale" parameter
// to implement 'trick mode' (fast forward or reverse play, using I-frames) on
@@ -262,5 +262,5 @@ void MPEG2TransportStreamTrickModeFilter::onSourceClosure(void* clientData) {
void MPEG2TransportStreamTrickModeFilter::onSourceClosure1() {
fIndexFile->stopReading();
- FramedSource::handleClosure(this);
+ handleClosure();
}
diff --git a/liveMedia/MPEG2TransportUDPServerMediaSubsession.cpp b/liveMedia/MPEG2TransportUDPServerMediaSubsession.cpp
index 5c82b54..d320349 100644
--- a/liveMedia/MPEG2TransportUDPServerMediaSubsession.cpp
+++ b/liveMedia/MPEG2TransportUDPServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an incoming UDP (or RTP/UDP) MPEG-2 Transport Stream
// Implementation
diff --git a/liveMedia/MPEG4ESVideoRTPSink.cpp b/liveMedia/MPEG4ESVideoRTPSink.cpp
index 9f49863..bdc43e5 100644
--- a/liveMedia/MPEG4ESVideoRTPSink.cpp
+++ b/liveMedia/MPEG4ESVideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG-4 Elementary Stream video (RFC 3016)
// Implementation
@@ -65,8 +65,8 @@ void MPEG4ESVideoRTPSink
if (fragmentationOffset == 0) {
// Begin by inspecting the 4-byte code at the start of the frame:
if (numBytesInFrame < 4) return; // shouldn't happen
- unsigned startCode = (frameStart[0]<<24) | (frameStart[1]<<16)
- | (frameStart[2]<<8) | frameStart[3];
+ u_int32_t startCode
+ = (frameStart[0]<<24) | (frameStart[1]<<16) | (frameStart[2]<<8) | frameStart[3];
fVOPIsPresent = startCode == VOP_START_CODE;
}
diff --git a/liveMedia/MPEG4ESVideoRTPSource.cpp b/liveMedia/MPEG4ESVideoRTPSource.cpp
index 07d6915..12c2ee7 100644
--- a/liveMedia/MPEG4ESVideoRTPSource.cpp
+++ b/liveMedia/MPEG4ESVideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP4V-ES video RTP stream sources
// Implementation
diff --git a/liveMedia/MPEG4GenericRTPSink.cpp b/liveMedia/MPEG4GenericRTPSink.cpp
index f1de0f3..a723422 100644
--- a/liveMedia/MPEG4GenericRTPSink.cpp
+++ b/liveMedia/MPEG4GenericRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG4-GENERIC ("audio", "video", or "application") RTP stream sinks
// Implementation
diff --git a/liveMedia/MPEG4GenericRTPSource.cpp b/liveMedia/MPEG4GenericRTPSource.cpp
index 55e19aa..2064a60 100644
--- a/liveMedia/MPEG4GenericRTPSource.cpp
+++ b/liveMedia/MPEG4GenericRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG4-GENERIC ("audio", "video", or "application") RTP stream sources
// Implementation
diff --git a/liveMedia/MPEG4LATMAudioRTPSink.cpp b/liveMedia/MPEG4LATMAudioRTPSink.cpp
index efb8a24..2b9a50b 100644
--- a/liveMedia/MPEG4LATMAudioRTPSink.cpp
+++ b/liveMedia/MPEG4LATMAudioRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG-4 audio, using LATM multiplexing (RFC 3016)
// Implementation
diff --git a/liveMedia/MPEG4LATMAudioRTPSource.cpp b/liveMedia/MPEG4LATMAudioRTPSource.cpp
index af4fdc3..17954f0 100644
--- a/liveMedia/MPEG4LATMAudioRTPSource.cpp
+++ b/liveMedia/MPEG4LATMAudioRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG-4 audio, using LATM multiplexing
// Implementation
diff --git a/liveMedia/MPEG4VideoFileServerMediaSubsession.cpp b/liveMedia/MPEG4VideoFileServerMediaSubsession.cpp
index 0d6472a..6ef85c8 100644
--- a/liveMedia/MPEG4VideoFileServerMediaSubsession.cpp
+++ b/liveMedia/MPEG4VideoFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-4 video file.
// Implementation
@@ -73,7 +73,7 @@ void MPEG4VideoFileServerMediaSubsession::checkForAuxSDPLine1() {
// Signal the event loop that we're done:
setDoneFlag();
- } else {
+ } else if (!fDoneFlag) {
// try again after a brief delay:
int uSecsToDelay = 100000; // 100 ms
nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,
diff --git a/liveMedia/MPEG4VideoStreamDiscreteFramer.cpp b/liveMedia/MPEG4VideoStreamDiscreteFramer.cpp
index ebb89c3..7e9b97a 100644
--- a/liveMedia/MPEG4VideoStreamDiscreteFramer.cpp
+++ b/liveMedia/MPEG4VideoStreamDiscreteFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "MPEG4VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/MPEG4VideoStreamFramer.cpp b/liveMedia/MPEG4VideoStreamFramer.cpp
index 700f64c..e6c15ea 100644
--- a/liveMedia/MPEG4VideoStreamFramer.cpp
+++ b/liveMedia/MPEG4VideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG-4 video elementary stream into
// frames for:
// - Visual Object Sequence (VS) Header + Visual Object (VO) Header
diff --git a/liveMedia/MPEGVideoStreamFramer.cpp b/liveMedia/MPEGVideoStreamFramer.cpp
index 58f5d4d..46c15d3 100644
--- a/liveMedia/MPEGVideoStreamFramer.cpp
+++ b/liveMedia/MPEGVideoStreamFramer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG video elementary stream into
// headers and frames
// Implementation
@@ -142,6 +142,11 @@ void MPEGVideoStreamFramer::doGetNextFrame() {
continueReadProcessing();
}
+void MPEGVideoStreamFramer::doStopGettingFrames() {
+ flushInput();
+ FramedFilter::doStopGettingFrames();
+}
+
void MPEGVideoStreamFramer
::continueReadProcessing(void* clientData,
unsigned char* /*ptr*/, unsigned /*size*/,
diff --git a/liveMedia/MPEGVideoStreamParser.cpp b/liveMedia/MPEGVideoStreamParser.cpp
index e0eda9f..15c7d7d 100644
--- a/liveMedia/MPEGVideoStreamParser.cpp
+++ b/liveMedia/MPEGVideoStreamParser.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// An abstract parser for MPEG video streams
// Implementation
diff --git a/liveMedia/MPEGVideoStreamParser.hh b/liveMedia/MPEGVideoStreamParser.hh
index 7e09098..0eee206 100644
--- a/liveMedia/MPEGVideoStreamParser.hh
+++ b/liveMedia/MPEGVideoStreamParser.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// An abstract parser for MPEG video streams
// C++ header
diff --git a/liveMedia/Makefile.tail b/liveMedia/Makefile.tail
index b52eb9f..14f51f8 100644
--- a/liveMedia/Makefile.tail
+++ b/liveMedia/Makefile.tail
@@ -23,16 +23,17 @@ DV_SINK_OBJS = DVVideoRTPSink.$(OBJ)
AC3_SINK_OBJS = AC3AudioRTPSink.$(OBJ)
MISC_SOURCE_OBJS = MediaSource.$(OBJ) FramedSource.$(OBJ) FramedFileSource.$(OBJ) FramedFilter.$(OBJ) ByteStreamFileSource.$(OBJ) ByteStreamMultiFileSource.$(OBJ) ByteStreamMemoryBufferSource.$(OBJ) BasicUDPSource.$(OBJ) DeviceSource.$(OBJ) AudioInputDevice.$(OBJ) WAVAudioFileSource.$(OBJ) $(MPEG_SOURCE_OBJS) $(H263_SOURCE_OBJS) $(AC3_SOURCE_OBJS) $(DV_SOURCE_OBJS) JPEGVideoSource.$(OBJ) AMRAudioSource.$(OBJ) AMRAudioFileSource.$(OBJ) InputFile.$(OBJ) StreamReplicator.$(OBJ)
-MISC_SINK_OBJS = MediaSink.$(OBJ) FileSink.$(OBJ) BasicUDPSink.$(OBJ) AMRAudioFileSink.$(OBJ) H264VideoFileSink.$(OBJ) $(MPEG_SINK_OBJS) $(H263_SINK_OBJS) $(H264_OR_5_SINK_OBJS) $(DV_SINK_OBJS) $(AC3_SINK_OBJS) VorbisAudioRTPSink.$(OBJ) TheoraVideoRTPSink.$(OBJ) VP8VideoRTPSink.$(OBJ) GSMAudioRTPSink.$(OBJ) JPEGVideoRTPSink.$(OBJ) SimpleRTPSink.$(OBJ) AMRAudioRTPSink.$(OBJ) T140TextRTPSink.$(OBJ) TCPStreamSink.$(OBJ) OutputFile.$(OBJ)
+MISC_SINK_OBJS = MediaSink.$(OBJ) FileSink.$(OBJ) BasicUDPSink.$(OBJ) AMRAudioFileSink.$(OBJ) H264or5VideoFileSink.$(OBJ) H264VideoFileSink.$(OBJ) H265VideoFileSink.$(OBJ) OggFileSink.$(OBJ) $(MPEG_SINK_OBJS) $(H263_SINK_OBJS) $(H264_OR_5_SINK_OBJS) $(DV_SINK_OBJS) $(AC3_SINK_OBJS) VorbisAudioRTPSink.$(OBJ) TheoraVideoRTPSink.$(OBJ) VP8VideoRTPSink.$(OBJ) VP9VideoRTPSink.$(OBJ) GSMAudioRTPSink.$(OBJ) JPEGVideoRTPSink.$(OBJ) SimpleRTPSink.$(OBJ) AMRAudioRTPSink.$(OBJ) T140TextRTPSink.$(OBJ) TCPStreamSink.$(OBJ) OutputFile.$(OBJ)
MISC_FILTER_OBJS = uLawAudioFilter.$(OBJ)
TRANSPORT_STREAM_TRICK_PLAY_OBJS = MPEG2IndexFromTransportStream.$(OBJ) MPEG2TransportStreamIndexFile.$(OBJ) MPEG2TransportStreamTrickModeFilter.$(OBJ)
-RTP_SOURCE_OBJS = RTPSource.$(OBJ) MultiFramedRTPSource.$(OBJ) SimpleRTPSource.$(OBJ) H261VideoRTPSource.$(OBJ) H264VideoRTPSource.$(OBJ) QCELPAudioRTPSource.$(OBJ) AMRAudioRTPSource.$(OBJ) JPEGVideoRTPSource.$(OBJ) VorbisAudioRTPSource.$(OBJ) VP8VideoRTPSource.$(OBJ)
+RTP_SOURCE_OBJS = RTPSource.$(OBJ) MultiFramedRTPSource.$(OBJ) SimpleRTPSource.$(OBJ) H261VideoRTPSource.$(OBJ) H264VideoRTPSource.$(OBJ) H265VideoRTPSource.$(OBJ) QCELPAudioRTPSource.$(OBJ) AMRAudioRTPSource.$(OBJ) JPEGVideoRTPSource.$(OBJ) VorbisAudioRTPSource.$(OBJ) TheoraVideoRTPSource.$(OBJ) VP8VideoRTPSource.$(OBJ) VP9VideoRTPSource.$(OBJ)
RTP_SINK_OBJS = RTPSink.$(OBJ) MultiFramedRTPSink.$(OBJ) AudioRTPSink.$(OBJ) VideoRTPSink.$(OBJ) TextRTPSink.$(OBJ)
RTP_INTERFACE_OBJS = RTPInterface.$(OBJ)
RTP_OBJS = $(RTP_SOURCE_OBJS) $(RTP_SINK_OBJS) $(RTP_INTERFACE_OBJS)
RTCP_OBJS = RTCP.$(OBJ) rtcp_from_spec.$(OBJ)
+GENERIC_MEDIA_SERVER_OBJS = GenericMediaServer.$(OBJ)
RTSP_OBJS = RTSPServer.$(OBJ) RTSPClient.$(OBJ) RTSPCommon.$(OBJ) RTSPServerSupportingHTTPStreaming.$(OBJ) RTSPRegisterSender.$(OBJ)
SIP_OBJS = SIPClient.$(OBJ)
@@ -42,16 +43,18 @@ QUICKTIME_OBJS = QuickTimeFileSink.$(OBJ) QuickTimeGenericRTPSource.$(OBJ)
AVI_OBJS = AVIFileSink.$(OBJ)
MATROSKA_FILE_OBJS = MatroskaFile.$(OBJ) MatroskaFileParser.$(OBJ) EBMLNumber.$(OBJ) MatroskaDemuxedTrack.$(OBJ)
-MATROSKA_SERVER_MEDIA_SUBSESSION_VIDEO_OBJS = H264VideoMatroskaFileServerMediaSubsession.$(OBJ) H265VideoMatroskaFileServerMediaSubsession.$(OBJ) VP8VideoMatroskaFileServerMediaSubsession.$(OBJ)
-MATROSKA_SERVER_MEDIA_SUBSESSION_AUDIO_OBJS = AACAudioMatroskaFileServerMediaSubsession.$(OBJ) AC3AudioMatroskaFileServerMediaSubsession.$(OBJ) MP3AudioMatroskaFileServerMediaSubsession.$(OBJ) VorbisAudioMatroskaFileServerMediaSubsession.$(OBJ)
-MATROSKA_SERVER_MEDIA_SUBSESSION_TEXT_OBJS = T140TextMatroskaFileServerMediaSubsession.$(OBJ)
-MATROSKA_SERVER_MEDIA_SUBSESSION_OBJS = $(MATROSKA_SERVER_MEDIA_SUBSESSION_VIDEO_OBJS) $(MATROSKA_SERVER_MEDIA_SUBSESSION_AUDIO_OBJS) $(MATROSKA_SERVER_MEDIA_SUBSESSION_TEXT_OBJS)
+MATROSKA_SERVER_MEDIA_SUBSESSION_OBJS = MatroskaFileServerMediaSubsession.$(OBJ) MP3AudioMatroskaFileServerMediaSubsession.$(OBJ)
MATROSKA_RTSP_SERVER_OBJS = MatroskaFileServerDemux.$(OBJ) $(MATROSKA_SERVER_MEDIA_SUBSESSION_OBJS)
MATROSKA_OBJS = $(MATROSKA_FILE_OBJS) $(MATROSKA_RTSP_SERVER_OBJS)
-MISC_OBJS = DarwinInjector.$(OBJ) BitVector.$(OBJ) StreamParser.$(OBJ) DigestAuthentication.$(OBJ) ourMD5.$(OBJ) Base64.$(OBJ) Locale.$(OBJ)
+OGG_FILE_OBJS = OggFile.$(OBJ) OggFileParser.$(OBJ) OggDemuxedTrack.$(OBJ)
+OGG_SERVER_MEDIA_SUBSESSION_OBJS = OggFileServerMediaSubsession.$(OBJ)
+OGG_RTSP_SERVER_OBJS = OggFileServerDemux.$(OBJ) $(OGG_SERVER_MEDIA_SUBSESSION_OBJS)
+OGG_OBJS = $(OGG_FILE_OBJS) $(OGG_RTSP_SERVER_OBJS)
-LIVEMEDIA_LIB_OBJS = Media.$(OBJ) $(MISC_SOURCE_OBJS) $(MISC_SINK_OBJS) $(MISC_FILTER_OBJS) $(RTP_OBJS) $(RTCP_OBJS) $(RTSP_OBJS) $(SIP_OBJS) $(SESSION_OBJS) $(QUICKTIME_OBJS) $(AVI_OBJS) $(TRANSPORT_STREAM_TRICK_PLAY_OBJS) $(MATROSKA_OBJS) $(MISC_OBJS)
+MISC_OBJS = BitVector.$(OBJ) StreamParser.$(OBJ) DigestAuthentication.$(OBJ) ourMD5.$(OBJ) Base64.$(OBJ) Locale.$(OBJ)
+
+LIVEMEDIA_LIB_OBJS = Media.$(OBJ) $(MISC_SOURCE_OBJS) $(MISC_SINK_OBJS) $(MISC_FILTER_OBJS) $(RTP_OBJS) $(RTCP_OBJS) $(GENERIC_MEDIA_SERVER_OBJS) $(RTSP_OBJS) $(SIP_OBJS) $(SESSION_OBJS) $(QUICKTIME_OBJS) $(AVI_OBJS) $(TRANSPORT_STREAM_TRICK_PLAY_OBJS) $(MATROSKA_OBJS) $(OGG_OBJS) $(MISC_OBJS)
$(LIVEMEDIA_LIB): $(LIVEMEDIA_LIB_OBJS) \
$(PLATFORM_SPECIFIC_LIB_OBJS)
@@ -71,7 +74,7 @@ include/FramedFilter.hh: include/FramedSource.hh
RTPSource.$(CPP): include/RTPSource.hh
include/RTPSource.hh: include/FramedSource.hh include/RTPInterface.hh
include/RTPInterface.hh: include/Media.hh
-MultiFramedRTPSource.$(CPP): include/MultiFramedRTPSource.hh
+MultiFramedRTPSource.$(CPP): include/MultiFramedRTPSource.hh include/RTCP.hh
include/MultiFramedRTPSource.hh: include/RTPSource.hh
SimpleRTPSource.$(CPP): include/SimpleRTPSource.hh
include/SimpleRTPSource.hh: include/MultiFramedRTPSource.hh
@@ -79,16 +82,22 @@ H261VideoRTPSource.$(CPP): include/H261VideoRTPSource.hh
include/H261VideoRTPSource.hh: include/MultiFramedRTPSource.hh
H264VideoRTPSource.$(CPP): include/H264VideoRTPSource.hh include/Base64.hh
include/H264VideoRTPSource.hh: include/MultiFramedRTPSource.hh
+H265VideoRTPSource.$(CPP): include/H265VideoRTPSource.hh
+include/H265VideoRTPSource.hh: include/MultiFramedRTPSource.hh
QCELPAudioRTPSource.$(CPP): include/QCELPAudioRTPSource.hh include/MultiFramedRTPSource.hh include/FramedFilter.hh
include/QCELPAudioRTPSource.hh: include/RTPSource.hh
AMRAudioRTPSource.$(CPP): include/AMRAudioRTPSource.hh include/MultiFramedRTPSource.hh
include/AMRAudioRTPSource.hh: include/RTPSource.hh include/AMRAudioSource.hh
JPEGVideoRTPSource.$(CPP): include/JPEGVideoRTPSource.hh
include/JPEGVideoRTPSource.hh: include/MultiFramedRTPSource.hh
-VorbisAudioRTPSource.$(CPP): include/VorbisAudioRTPSource.hh
+VorbisAudioRTPSource.$(CPP): include/VorbisAudioRTPSource.hh include/Base64.hh
include/VorbisAudioRTPSource.hh: include/MultiFramedRTPSource.hh
+TheoraVideoRTPSource.$(CPP): include/TheoraVideoRTPSource.hh
+include/TheoraVideoRTPSource.hh: include/MultiFramedRTPSource.hh
VP8VideoRTPSource.$(CPP): include/VP8VideoRTPSource.hh
include/VP8VideoRTPSource.hh: include/MultiFramedRTPSource.hh
+VP9VideoRTPSource.$(CPP): include/VP9VideoRTPSource.hh
+include/VP9VideoRTPSource.hh: include/MultiFramedRTPSource.hh
ByteStreamFileSource.$(CPP): include/ByteStreamFileSource.hh include/InputFile.hh
include/ByteStreamFileSource.hh: include/FramedFileSource.hh
ByteStreamMultiFileSource.$(CPP): include/ByteStreamMultiFileSource.hh
@@ -203,9 +212,15 @@ BasicUDPSink.$(CPP): include/BasicUDPSink.hh
include/BasicUDPSink.hh: include/MediaSink.hh
AMRAudioFileSink.$(CPP): include/AMRAudioFileSink.hh include/AMRAudioSource.hh include/OutputFile.hh
include/AMRAudioFileSink.hh: include/FileSink.hh
-H264VideoFileSink.$(CPP): include/H264VideoFileSink.hh include/OutputFile.hh include/H264VideoRTPSource.hh
-include/H264VideoFileSink.hh: include/FileSink.hh
-RTPSink.$(CPP): include/RTPSink.hh
+H264or5VideoFileSink.$(CPP): include/H264or5VideoFileSink.hh include/H264VideoRTPSource.hh
+include/H264or5VideoFileSink.hh: include/FileSink.hh
+H264VideoFileSink.$(CPP): include/H264VideoFileSink.hh include/OutputFile.hh
+include/H264VideoFileSink.hh: include/H264or5VideoFileSink.hh
+H265VideoFileSink.$(CPP): include/H265VideoFileSink.hh include/OutputFile.hh
+include/H265VideoFileSink.hh: include/H264or5VideoFileSink.hh
+OggFileSink.$(CPP): include/OggFileSink.hh include/OutputFile.hh include/VorbisAudioRTPSource.hh include/MPEG2TransportStreamMultiplexor.hh include/FramedSource.hh
+include/OggFileSink.hh: include/FileSink.hh
+RTPSink.$(CPP): include/RTPSink.hh
include/RTPSink.hh: include/MediaSink.hh include/RTPInterface.hh
MultiFramedRTPSink.$(CPP): include/MultiFramedRTPSink.hh
include/MultiFramedRTPSink.hh: include/RTPSink.hh
@@ -241,12 +256,14 @@ include/DVVideoRTPSink.hh: include/VideoRTPSink.hh include/DVVideoStreamFramer.h
include/DVVideoStreamFramer.hh: include/FramedFilter.hh
AC3AudioRTPSink.$(CPP): include/AC3AudioRTPSink.hh
include/AC3AudioRTPSink.hh: include/AudioRTPSink.hh
-VorbisAudioRTPSink.$(CPP): include/VorbisAudioRTPSink.hh include/Base64.hh
+VorbisAudioRTPSink.$(CPP): include/VorbisAudioRTPSink.hh include/Base64.hh include/VorbisAudioRTPSource.hh
include/VorbisAudioRTPSink.hh: include/AudioRTPSink.hh
-TheoraVideoRTPSink.$(CPP): include/TheoraVideoRTPSink.hh include/Base64.hh
+TheoraVideoRTPSink.$(CPP): include/TheoraVideoRTPSink.hh include/Base64.hh include/VorbisAudioRTPSource.hh include/VorbisAudioRTPSink.hh
include/TheoraVideoRTPSink.hh: include/VideoRTPSink.hh
VP8VideoRTPSink.$(CPP): include/VP8VideoRTPSink.hh
include/VP8VideoRTPSink.hh: include/VideoRTPSink.hh
+VP9VideoRTPSink.$(CPP): include/VP9VideoRTPSink.hh
+include/VP9VideoRTPSink.hh: include/VideoRTPSink.hh
GSMAudioRTPSink.$(CPP): include/GSMAudioRTPSink.hh
include/GSMAudioRTPSink.hh: include/AudioRTPSink.hh
JPEGVideoRTPSink.$(CPP): include/JPEGVideoRTPSink.hh include/JPEGVideoSource.hh
@@ -271,10 +288,12 @@ include/MPEG2TransportStreamTrickModeFilter.hh: include/FramedFilter.hh include/
RTCP.$(CPP): include/RTCP.hh rtcp_from_spec.h
include/RTCP.hh: include/RTPSink.hh include/RTPSource.hh
rtcp_from_spec.$(C): rtcp_from_spec.h
+GenericMediaServer.$(CPP): include/GenericMediaServer.hh
+include/GenericMediaServer.hh: include/ServerMediaSession.hh
RTSPServer.$(CPP): include/RTSPServer.hh include/RTSPCommon.hh include/RTSPRegisterSender.hh include/ProxyServerMediaSession.hh include/Base64.hh
-include/RTSPServer.hh: include/ServerMediaSession.hh include/DigestAuthentication.hh include/RTSPCommon.hh
-include/ServerMediaSession.hh: include/Media.hh include/FramedSource.hh include/RTPInterface.hh
-RTSPClient.$(CPP): include/RTSPClient.hh include/RTSPCommon.hh include/Base64.hh include/Locale.hh ourMD5.hh
+include/RTSPServer.hh: include/GenericMediaServer.hh include/DigestAuthentication.hh
+include/ServerMediaSession.hh: include/RTCP.hh
+RTSPClient.$(CPP): include/RTSPClient.hh include/RTSPCommon.hh include/Base64.hh include/Locale.hh include/ourMD5.hh
include/RTSPClient.hh: include/MediaSession.hh include/DigestAuthentication.hh
RTSPCommon.$(CPP): include/RTSPCommon.hh include/Locale.hh
RTSPServerSupportingHTTPStreaming.$(CPP): include/RTSPServerSupportingHTTPStreaming.hh include/RTSPCommon.hh
@@ -323,52 +342,49 @@ include/AC3AudioFileServerMediaSubsession.hh: include/FileServerMediaSubsession.
MPEG2TransportUDPServerMediaSubsession.$(CPP): include/MPEG2TransportUDPServerMediaSubsession.hh include/BasicUDPSource.hh include/SimpleRTPSource.hh include/MPEG2TransportStreamFramer.hh include/SimpleRTPSink.hh
include/MPEG2TransportUDPServerMediaSubsession.hh: include/OnDemandServerMediaSubsession.hh
ProxyServerMediaSession.$(CPP): include/liveMedia.hh include/RTSPCommon.hh
-include/ProxyServerMediaSession.hh: include/ServerMediaSession.hh include/MediaSession.hh include/RTSPClient.hh
+include/ProxyServerMediaSession.hh: include/ServerMediaSession.hh include/MediaSession.hh include/RTSPClient.hh include/MediaTranscodingTable.hh
+include/MediaTranscodingTable.hh: include/FramedFilter.hh include/MediaSession.hh
QuickTimeFileSink.$(CPP): include/QuickTimeFileSink.hh include/InputFile.hh include/OutputFile.hh include/QuickTimeGenericRTPSource.hh include/H263plusVideoRTPSource.hh include/MPEG4GenericRTPSource.hh include/MPEG4LATMAudioRTPSource.hh
include/QuickTimeFileSink.hh: include/MediaSession.hh
QuickTimeGenericRTPSource.$(CPP): include/QuickTimeGenericRTPSource.hh
include/QuickTimeGenericRTPSource.hh: include/MultiFramedRTPSource.hh
AVIFileSink.$(CPP): include/AVIFileSink.hh include/InputFile.hh include/OutputFile.hh
include/AVIFileSink.hh: include/MediaSession.hh
-MatroskaFile.$(CPP): MatroskaFileParser.hh MatroskaDemuxedTrack.hh include/ByteStreamFileSource.hh
+MatroskaFile.$(CPP): MatroskaFileParser.hh MatroskaDemuxedTrack.hh include/ByteStreamFileSource.hh include/H264VideoStreamDiscreteFramer.hh include/H265VideoStreamDiscreteFramer.hh include/MPEG1or2AudioRTPSink.hh include/MPEG4GenericRTPSink.hh include/AC3AudioRTPSink.hh include/SimpleRTPSink.hh include/VorbisAudioRTPSink.hh include/H264VideoRTPSink.hh include/H265VideoRTPSink.hh include/VP8VideoRTPSink.hh include/VP9VideoRTPSink.hh include/T140TextRTPSink.hh
MatroskaFileParser.hh: StreamParser.hh include/MatroskaFile.hh EBMLNumber.hh
-include/MatroskaFile.hh: include/Media.hh
+include/MatroskaFile.hh: include/RTPSink.hh
MatroskaDemuxedTrack.hh: include/FramedSource.hh
MatroskaFileParser.$(CPP): MatroskaFileParser.hh MatroskaDemuxedTrack.hh include/ByteStreamFileSource.hh
EBMLNumber.$(CPP): EBMLNumber.hh
MatroskaDemuxedTrack.$(CPP): MatroskaDemuxedTrack.hh include/MatroskaFile.hh
-H264VideoMatroskaFileServerMediaSubsession.$(CPP): H264VideoMatroskaFileServerMediaSubsession.hh include/H264VideoStreamDiscreteFramer.hh
-H264VideoMatroskaFileServerMediaSubsession.hh: include/H264VideoFileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-H265VideoMatroskaFileServerMediaSubsession.$(CPP): H265VideoMatroskaFileServerMediaSubsession.hh include/H265VideoStreamDiscreteFramer.hh
-H265VideoMatroskaFileServerMediaSubsession.hh: include/H265VideoFileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-VP8VideoMatroskaFileServerMediaSubsession.$(CPP): VP8VideoMatroskaFileServerMediaSubsession.hh include/VP8VideoRTPSink.hh
-VP8VideoMatroskaFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-AACAudioMatroskaFileServerMediaSubsession.$(CPP): AACAudioMatroskaFileServerMediaSubsession.hh include/MPEG4GenericRTPSink.hh
-AACAudioMatroskaFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-AC3AudioMatroskaFileServerMediaSubsession.$(CPP): AC3AudioMatroskaFileServerMediaSubsession.hh include/AC3AudioRTPSink.hh
-AC3AudioMatroskaFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-VorbisAudioMatroskaFileServerMediaSubsession.$(CPP): VorbisAudioMatroskaFileServerMediaSubsession.hh include/VorbisAudioRTPSink.hh
-VorbisAudioMatroskaFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-MP3AudioMatroskaFileServerMediaSubsession.$(CPP): MP3AudioMatroskaFileServerMediaSubsession.hh
-MP3AudioMatroskaFileServerMediaSubsession.hh: include/MP3AudioFileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-T140TextMatroskaFileServerMediaSubsession.$(CPP): T140TextMatroskaFileServerMediaSubsession.hh include/T140TextRTPSink.hh
-T140TextMatroskaFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh MatroskaDemuxedTrack.hh
-MatroskaFileServerDemux.$(CPP): include/MatroskaFileServerDemux.hh H264VideoMatroskaFileServerMediaSubsession.hh H265VideoMatroskaFileServerMediaSubsession.hh VP8VideoMatroskaFileServerMediaSubsession.hh AACAudioMatroskaFileServerMediaSubsession.hh AC3AudioMatroskaFileServerMediaSubsession.hh VorbisAudioMatroskaFileServerMediaSubsession.hh MP3AudioMatroskaFileServerMediaSubsession.hh T140TextMatroskaFileServerMediaSubsession.hh
+MatroskaFileServerMediaSubsession.$(CPP): MatroskaFileServerMediaSubsession.hh MatroskaDemuxedTrack.hh include/FramedFilter.hh
+MatroskaFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh
+MP3AudioMatroskaFileServerMediaSubsession.$(CPP): MP3AudioMatroskaFileServerMediaSubsession.hh MatroskaDemuxedTrack.hh
+MP3AudioMatroskaFileServerMediaSubsession.hh: include/MP3AudioFileServerMediaSubsession.hh include/MatroskaFileServerDemux.hh
+MatroskaFileServerDemux.$(CPP): include/MatroskaFileServerDemux.hh MP3AudioMatroskaFileServerMediaSubsession.hh MatroskaFileServerMediaSubsession.hh
include/MatroskaFileServerDemux.hh: include/ServerMediaSession.hh include/MatroskaFile.hh
-DarwinInjector.$(CPP): include/DarwinInjector.hh
-include/DarwinInjector.hh: include/RTSPClient.hh include/RTCP.hh
+OggFile.$(CPP): OggFileParser.hh OggDemuxedTrack.hh include/ByteStreamFileSource.hh include/VorbisAudioRTPSink.hh include/SimpleRTPSink.hh include/TheoraVideoRTPSink.hh
+OggFileParser.hh: StreamParser.hh include/OggFile.hh
+include/OggFile.hh: include/RTPSink.hh
+OggDemuxedTrack.hh: include/FramedSource.hh
+OggFileParser.$(CPP): OggFileParser.hh OggDemuxedTrack.hh
+OggDemuxedTrack.$(CPP): OggDemuxedTrack.hh include/OggFile.hh
+OggFileServerMediaSubsession.$(CPP): OggFileServerMediaSubsession.hh OggDemuxedTrack.hh include/FramedFilter.hh
+OggFileServerMediaSubsession.hh: include/FileServerMediaSubsession.hh include/OggFileServerDemux.hh
+OggFileServerDemux.$(CPP): include/OggFileServerDemux.hh OggFileServerMediaSubsession.hh
+include/OggFileServerDemux.hh: include/ServerMediaSession.hh include/OggFile.hh
BitVector.$(CPP): include/BitVector.hh
StreamParser.$(CPP): StreamParser.hh
-DigestAuthentication.$(CPP): include/DigestAuthentication.hh ourMD5.hh
-ourMD5.$(CPP): ourMD5.hh
+DigestAuthentication.$(CPP): include/DigestAuthentication.hh include/ourMD5.hh
+ourMD5.$(CPP): include/ourMD5.hh
Base64.$(CPP): include/Base64.hh
Locale.$(CPP): include/Locale.hh
-include/liveMedia.hh:: include/MPEG1or2AudioRTPSink.hh include/MP3ADURTPSink.hh include/MPEG1or2VideoRTPSink.hh include/MPEG4ESVideoRTPSink.hh include/BasicUDPSink.hh include/AMRAudioFileSink.hh include/H264VideoFileSink.hh include/GSMAudioRTPSink.hh include/H263plusVideoRTPSink.hh include/H264VideoRTPSink.hh include/H265VideoRTPSink.hh include/DVVideoRTPSource.hh include/DVVideoRTPSink.hh include/DVVideoStreamFramer.hh include/H264VideoStreamFramer.hh include/H265VideoStreamFramer.hh include/H264VideoStreamDiscreteFramer.hh include/H265VideoStreamDiscreteFramer.hh include/JPEGVideoRTPSink.hh include/SimpleRTPSink.hh include/uLawAudioFilter.hh include/MPEG2IndexFromTransportStream.hh include/MPEG2TransportStreamTrickModeFilter.hh include/ByteStreamMultiFileSource.hh include/ByteStreamMemoryBufferSource.hh include/BasicUDPSource.hh include/SimpleRTPSource.hh include/MPEG1or2AudioRTPSource.hh include/MPEG4LATMAudioRTPSource.hh include/MPEG4LATMAudioRTPSink.hh include/MPEG4ESVideoRTPSource.hh include/MPEG4GenericRTPSource.hh include/MP3ADURTPSource.hh include/QCELPAudioRTPSource.hh include/AMRAudioRTPSource.hh include/JPEGVideoRTPSource.hh include/JPEGVideoSource.hh include/MPEG1or2VideoRTPSource.hh include/VorbisAudioRTPSource.hh include/VP8VideoRTPSource.hh
+include/liveMedia.hh:: include/MPEG1or2AudioRTPSink.hh include/MP3ADURTPSink.hh include/MPEG1or2VideoRTPSink.hh include/MPEG4ESVideoRTPSink.hh include/BasicUDPSink.hh include/AMRAudioFileSink.hh include/H264VideoFileSink.hh include/H265VideoFileSink.hh include/OggFileSink.hh include/GSMAudioRTPSink.hh include/H263plusVideoRTPSink.hh include/H264VideoRTPSink.hh include/H265VideoRTPSink.hh include/DVVideoRTPSource.hh include/DVVideoRTPSink.hh include/DVVideoStreamFramer.hh include/H264VideoStreamFramer.hh include/H265VideoStreamFramer.hh include/H264VideoStreamDiscreteFramer.hh include/H265VideoStreamDiscreteFramer.hh include/JPEGVideoRTPSink.hh include/SimpleRTPSink.hh include/uLawAudioFilter.hh include/MPEG2IndexFromTransportStream.hh include/MPEG2TransportStreamTrickModeFilter.hh include/ByteStreamMultiFileSource.hh include/ByteStreamMemoryBufferSource.hh include/BasicUDPSource.hh include/SimpleRTPSource.hh include/MPEG1or2AudioRTPSource.hh include/MPEG4LATMAudioRTPSource.hh include/MPEG4LATMAudioRTPSink.hh include/MPEG4ESVideoRTPSource.hh include/MPEG4GenericRTPSource.hh include/MP3ADURTPSource.hh include/QCELPAudioRTPSource.hh include/AMRAudioRTPSource.hh include/JPEGVideoRTPSource.hh include/JPEGVideoSource.hh include/MPEG1or2VideoRTPSource.hh include/VorbisAudioRTPSource.hh include/TheoraVideoRTPSource.hh include/VP8VideoRTPSource.hh include/VP9VideoRTPSource.hh
-include/liveMedia.hh:: include/MPEG2TransportStreamFromPESSource.hh include/MPEG2TransportStreamFromESSource.hh include/MPEG2TransportStreamFramer.hh include/ADTSAudioFileSource.hh include/H261VideoRTPSource.hh include/H263plusVideoRTPSource.hh include/H264VideoRTPSource.hh include/MP3FileSource.hh include/MP3ADU.hh include/MP3ADUinterleaving.hh include/MP3Transcoder.hh include/MPEG1or2DemuxedElementaryStream.hh include/MPEG1or2AudioStreamFramer.hh include/MPEG1or2VideoStreamDiscreteFramer.hh include/MPEG4VideoStreamDiscreteFramer.hh include/H263plusVideoStreamFramer.hh include/AC3AudioStreamFramer.hh include/AC3AudioRTPSource.hh include/AC3AudioRTPSink.hh include/VorbisAudioRTPSink.hh include/TheoraVideoRTPSink.hh include/VP8VideoRTPSink.hh include/MPEG4GenericRTPSink.hh include/DeviceSource.hh include/AudioInputDevice.hh include/WAVAudioFileSource.hh include/StreamReplicator.hh include/RTSPRegisterSender.hh
+include/liveMedia.hh:: include/MPEG2TransportStreamFromPESSource.hh include/MPEG2TransportStreamFromESSource.hh include/MPEG2TransportStreamFramer.hh include/ADTSAudioFileSource.hh include/H261VideoRTPSource.hh include/H263plusVideoRTPSource.hh include/H264VideoRTPSource.hh include/H265VideoRTPSource.hh include/MP3FileSource.hh include/MP3ADU.hh include/MP3ADUinterleaving.hh include/MP3Transcoder.hh include/MPEG1or2DemuxedElementaryStream.hh include/MPEG1or2AudioStreamFramer.hh include/MPEG1or2VideoStreamDiscreteFramer.hh include/MPEG4VideoStreamDiscreteFramer.hh include/H263plusVideoStreamFramer.hh include/AC3AudioStreamFramer.hh include/AC3AudioRTPSource.hh include/AC3AudioRTPSink.hh include/VorbisAudioRTPSink.hh include/TheoraVideoRTPSink.hh include/VP8VideoRTPSink.hh include/VP9VideoRTPSink.hh include/MPEG4GenericRTPSink.hh include/DeviceSource.hh include/AudioInputDevice.hh include/WAVAudioFileSource.hh include/StreamReplicator.hh include/RTSPRegisterSender.hh
-include/liveMedia.hh:: include/RTSPServerSupportingHTTPStreaming.hh include/RTSPClient.hh include/SIPClient.hh include/QuickTimeFileSink.hh include/QuickTimeGenericRTPSource.hh include/AVIFileSink.hh include/PassiveServerMediaSubsession.hh include/MPEG4VideoFileServerMediaSubsession.hh include/H264VideoFileServerMediaSubsession.hh include/H265VideoFileServerMediaSubsession.hh include/WAVAudioFileServerMediaSubsession.hh include/AMRAudioFileServerMediaSubsession.hh include/AMRAudioFileSource.hh include/AMRAudioRTPSink.hh include/T140TextRTPSink.hh include/TCPStreamSink.hh include/MP3AudioFileServerMediaSubsession.hh include/MPEG1or2VideoFileServerMediaSubsession.hh include/MPEG1or2FileServerDemux.hh include/MPEG2TransportFileServerMediaSubsession.hh include/H263plusVideoFileServerMediaSubsession.hh include/ADTSAudioFileServerMediaSubsession.hh include/DVVideoFileServerMediaSubsession.hh include/AC3AudioFileServerMediaSubsession.hh include/MPEG2TransportUDPServerMediaSubsession.hh include/MatroskaFileServerDemux.hh include/ProxyServerMediaSession.hh include/DarwinInjector.hh
+include/liveMedia.hh:: include/RTSPServerSupportingHTTPStreaming.hh include/RTSPClient.hh include/SIPClient.hh include/QuickTimeFileSink.hh include/QuickTimeGenericRTPSource.hh include/AVIFileSink.hh include/PassiveServerMediaSubsession.hh include/MPEG4VideoFileServerMediaSubsession.hh include/H264VideoFileServerMediaSubsession.hh include/H265VideoFileServerMediaSubsession.hh include/WAVAudioFileServerMediaSubsession.hh include/AMRAudioFileServerMediaSubsession.hh include/AMRAudioFileSource.hh include/AMRAudioRTPSink.hh include/T140TextRTPSink.hh include/TCPStreamSink.hh include/MP3AudioFileServerMediaSubsession.hh include/MPEG1or2VideoFileServerMediaSubsession.hh include/MPEG1or2FileServerDemux.hh include/MPEG2TransportFileServerMediaSubsession.hh include/H263plusVideoFileServerMediaSubsession.hh include/ADTSAudioFileServerMediaSubsession.hh include/DVVideoFileServerMediaSubsession.hh include/AC3AudioFileServerMediaSubsession.hh include/MPEG2TransportUDPServerMediaSubsession.hh include/MatroskaFileServerDemux.hh include/OggFileServerDemux.hh include/ProxyServerMediaSession.hh
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
@@ -379,7 +395,7 @@ install1: $(LIVEMEDIA_LIB)
install -m 644 include/*.hh $(DESTDIR)$(PREFIX)/include/liveMedia
install -m 644 $(LIVEMEDIA_LIB) $(DESTDIR)$(LIBDIR)
install_shared_libraries: $(LIVEMEDIA_LIB)
- ln -s $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
- ln -s $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
+ ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
+ ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
##### Any additional, platform-specific rules come here:
diff --git a/liveMedia/MatroskaDemuxedTrack.cpp b/liveMedia/MatroskaDemuxedTrack.cpp
index 193bcf4..608acd1 100644
--- a/liveMedia/MatroskaDemuxedTrack.cpp
+++ b/liveMedia/MatroskaDemuxedTrack.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A media track, demultiplexed from a Matroska file
// Implementation
@@ -27,7 +27,8 @@ void MatroskaDemuxedTrack::seekToTime(double& seekNPT) {
MatroskaDemuxedTrack::MatroskaDemuxedTrack(UsageEnvironment& env, unsigned trackNumber, MatroskaDemux& sourceDemux)
: FramedSource(env),
- fOurTrackNumber(trackNumber), fOurSourceDemux(sourceDemux), fDurationImbalance(0) {
+ fOurTrackNumber(trackNumber), fOurSourceDemux(sourceDemux), fDurationImbalance(0),
+ fOpusTrackNumber(0) {
fPrevPresentationTime.tv_sec = 0; fPrevPresentationTime.tv_usec = 0;
}
@@ -41,6 +42,6 @@ void MatroskaDemuxedTrack::doGetNextFrame() {
char const* MatroskaDemuxedTrack::MIMEtype() const {
MatroskaTrack* track = fOurSourceDemux.fOurFile.lookup(fOurTrackNumber);
- if (track == NULL) return NULL; // shouldn't happen
+ if (track == NULL) return "(unknown)"; // shouldn't happen
return track->mimeType;
}
diff --git a/liveMedia/MatroskaDemuxedTrack.hh b/liveMedia/MatroskaDemuxedTrack.hh
index 90fa3e9..649dfc1 100644
--- a/liveMedia/MatroskaDemuxedTrack.hh
+++ b/liveMedia/MatroskaDemuxedTrack.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A media track, demultiplexed from a Matroska file
// C++ header
@@ -58,6 +58,7 @@ private:
MatroskaDemux& fOurSourceDemux;
struct timeval fPrevPresentationTime;
int fDurationImbalance;
+ unsigned fOpusTrackNumber; // hack for Opus audio
};
#endif
diff --git a/liveMedia/MatroskaFile.cpp b/liveMedia/MatroskaFile.cpp
index c4ec667..5a4c912 100644
--- a/liveMedia/MatroskaFile.cpp
+++ b/liveMedia/MatroskaFile.cpp
@@ -14,13 +14,26 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class that encapsulates a Matroska file.
// Implementation
#include "MatroskaFileParser.hh"
#include "MatroskaDemuxedTrack.hh"
#include <ByteStreamFileSource.hh>
+#include <H264VideoStreamDiscreteFramer.hh>
+#include <H265VideoStreamDiscreteFramer.hh>
+#include <MPEG1or2AudioRTPSink.hh>
+#include <MPEG4GenericRTPSink.hh>
+#include <AC3AudioRTPSink.hh>
+#include <SimpleRTPSink.hh>
+#include <VorbisAudioRTPSink.hh>
+#include <H264VideoRTPSink.hh>
+#include <H265VideoRTPSink.hh>
+#include <VP8VideoRTPSink.hh>
+#include <VP9VideoRTPSink.hh>
+#include <TheoraVideoRTPSink.hh>
+#include <T140TextRTPSink.hh>
////////// CuePoint definition //////////
@@ -156,7 +169,7 @@ void MatroskaFile::handleEndOfTrackHeaderParsing() {
MatroskaTrackTable::Iterator iter(*fTrackTable);
MatroskaTrack* track;
while ((track = iter.next()) != NULL) {
- if (!track->isEnabled || track->trackType == 0 || track->codecID == NULL) continue; // track not enabled, or not fully-defined
+ if (!track->isEnabled || track->trackType == 0 || track->mimeType[0] == '\0') continue; // track not enabled, or not fully-defined
trackChoice[numEnabledTracks].trackNumber = track->trackNumber;
trackChoice[numEnabledTracks].trackType = track->trackType;
@@ -231,6 +244,355 @@ float MatroskaFile::fileDuration() {
return segmentDuration()*(timecodeScale()/1000000000.0f);
}
+FramedSource* MatroskaFile
+::createSourceForStreaming(FramedSource* baseSource, unsigned trackNumber,
+ unsigned& estBitrate, unsigned& numFiltersInFrontOfTrack) {
+ if (baseSource == NULL) return NULL;
+
+ FramedSource* result = baseSource; // by default
+ estBitrate = 100; // by default
+ numFiltersInFrontOfTrack = 0; // by default
+
+ // Look at the track's MIME type to set its estimated bitrate (for use by RTCP).
+ // (Later, try to be smarter about figuring out the bitrate.) #####
+ // Some MIME types also require adding a special 'framer' in front of the source.
+ MatroskaTrack* track = lookup(trackNumber);
+ if (track != NULL) { // should always be true
+ if (strcmp(track->mimeType, "audio/MPEG") == 0) {
+ estBitrate = 128;
+ } else if (strcmp(track->mimeType, "audio/AAC") == 0) {
+ estBitrate = 96;
+ } else if (strcmp(track->mimeType, "audio/AC3") == 0) {
+ estBitrate = 48;
+ } else if (strcmp(track->mimeType, "audio/VORBIS") == 0) {
+ estBitrate = 96;
+ } else if (strcmp(track->mimeType, "video/H264") == 0) {
+ estBitrate = 500;
+ // Allow for the possibility of very large NAL units being fed to the sink object:
+ OutPacketBuffer::increaseMaxSizeTo(300000); // bytes
+
+ // Add a framer in front of the source:
+ result = H264VideoStreamDiscreteFramer::createNew(envir(), result);
+ ++numFiltersInFrontOfTrack;
+ } else if (strcmp(track->mimeType, "video/H265") == 0) {
+ estBitrate = 500;
+ // Allow for the possibility of very large NAL units being fed to the sink object:
+ OutPacketBuffer::increaseMaxSizeTo(300000); // bytes
+
+ // Add a framer in front of the source:
+ result = H265VideoStreamDiscreteFramer::createNew(envir(), result);
+ ++numFiltersInFrontOfTrack;
+ } else if (strcmp(track->mimeType, "video/VP8") == 0) {
+ estBitrate = 500;
+ } else if (strcmp(track->mimeType, "video/VP9") == 0) {
+ estBitrate = 500;
+ } else if (strcmp(track->mimeType, "video/THEORA") == 0) {
+ estBitrate = 500;
+ } else if (strcmp(track->mimeType, "text/T140") == 0) {
+ estBitrate = 48;
+ }
+ }
+
+ return result;
+}
+
+#define getPrivByte(b) if (n == 0) break; else do {b = *p++; --n;} while (0) /* Vorbis/Theora configuration header parsing */
+#define CHECK_PTR if (ptr >= limit) break /* H.264/H.265 parsing */
+#define NUM_BYTES_REMAINING (unsigned)(limit - ptr) /* H.264/H.265 parsing */
+
+RTPSink* MatroskaFile
+::createRTPSinkForTrackNumber(unsigned trackNumber, Groupsock* rtpGroupsock,
+ unsigned char rtpPayloadTypeIfDynamic) {
+ RTPSink* result = NULL; // default value, if an error occurs
+
+ do {
+ MatroskaTrack* track = lookup(trackNumber);
+ if (track == NULL) break;
+
+ if (strcmp(track->mimeType, "audio/MPEG") == 0) {
+ result = MPEG1or2AudioRTPSink::createNew(envir(), rtpGroupsock);
+ } else if (strcmp(track->mimeType, "audio/AAC") == 0) {
+ // The Matroska file's 'Codec Private' data is assumed to be the AAC configuration
+ // information. Use this to generate a hexadecimal 'config' string for the new RTP sink:
+ char* configStr = new char[2*track->codecPrivateSize + 1]; if (configStr == NULL) break;
+ // 2 hex digits per byte, plus the trailing '\0'
+ for (unsigned i = 0; i < track->codecPrivateSize; ++i) {
+ sprintf(&configStr[2*i], "%02X", track->codecPrivate[i]);
+ }
+
+ result = MPEG4GenericRTPSink::createNew(envir(), rtpGroupsock,
+ rtpPayloadTypeIfDynamic,
+ track->samplingFrequency,
+ "audio", "AAC-hbr", configStr,
+ track->numChannels);
+ delete[] configStr;
+ } else if (strcmp(track->mimeType, "audio/AC3") == 0) {
+ result = AC3AudioRTPSink
+ ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, track->samplingFrequency);
+ } else if (strcmp(track->mimeType, "audio/OPUS") == 0) {
+ result = SimpleRTPSink
+ ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ 48000, "audio", "OPUS", 2, False/*only 1 Opus 'packet' in each RTP packet*/);
+ } else if (strcmp(track->mimeType, "audio/VORBIS") == 0 || strcmp(track->mimeType, "video/THEORA") == 0) {
+ // The Matroska file's 'Codec Private' data is assumed to be the codec configuration
+ // information, containing the "Identification", "Comment", and "Setup" headers.
+ // Extract these headers now:
+ u_int8_t* identificationHeader = NULL; unsigned identificationHeaderSize = 0;
+ u_int8_t* commentHeader = NULL; unsigned commentHeaderSize = 0;
+ u_int8_t* setupHeader = NULL; unsigned setupHeaderSize = 0;
+ Boolean isTheora = strcmp(track->mimeType, "video/THEORA") == 0; // otherwise, Vorbis
+
+ do {
+ u_int8_t* p = track->codecPrivate;
+ unsigned n = track->codecPrivateSize;
+ if (n == 0 || p == NULL) break; // we have no 'Codec Private' data
+
+ u_int8_t numHeaders;
+ getPrivByte(numHeaders);
+ unsigned headerSize[3]; // we don't handle any more than 2+1 headers
+
+ // Extract the sizes of each of these headers:
+ unsigned sizesSum = 0;
+ Boolean success = True;
+ unsigned i;
+ for (i = 0; i < numHeaders && i < 3; ++i) {
+ unsigned len = 0;
+ u_int8_t c;
+
+ do {
+ success = False;
+ getPrivByte(c);
+ success = True;
+
+ len += c;
+ } while (c == 255);
+ if (!success || len == 0) break;
+
+ headerSize[i] = len;
+ sizesSum += len;
+ }
+ if (!success) break;
+
+ // Compute the implicit size of the final header:
+ if (numHeaders < 3) {
+ int finalHeaderSize = n - sizesSum;
+ if (finalHeaderSize <= 0) break; // error in data; give up
+
+ headerSize[numHeaders] = (unsigned)finalHeaderSize;
+ ++numHeaders; // include the final header now
+ } else {
+ numHeaders = 3; // The maximum number of headers that we handle
+ }
+
+ // Then, extract and classify each header:
+ for (i = 0; i < numHeaders; ++i) {
+ success = False;
+ unsigned newHeaderSize = headerSize[i];
+ u_int8_t* newHeader = new u_int8_t[newHeaderSize];
+ if (newHeader == NULL) break;
+
+ u_int8_t* hdr = newHeader;
+ while (newHeaderSize-- > 0) {
+ success = False;
+ getPrivByte(*hdr++);
+ success = True;
+ }
+ if (!success) {
+ delete[] newHeader;
+ break;
+ }
+
+ u_int8_t headerType = newHeader[0];
+ if (headerType == 1 || (isTheora && headerType == 0x80)) { // "identification" header
+ delete[] identificationHeader; identificationHeader = newHeader;
+ identificationHeaderSize = headerSize[i];
+ } else if (headerType == 3 || (isTheora && headerType == 0x81)) { // "comment" header
+ delete[] commentHeader; commentHeader = newHeader;
+ commentHeaderSize = headerSize[i];
+ } else if (headerType == 5 || (isTheora && headerType == 0x82)) { // "setup" header
+ delete[] setupHeader; setupHeader = newHeader;
+ setupHeaderSize = headerSize[i];
+ } else {
+ delete[] newHeader; // because it was a header type that we don't understand
+ }
+ }
+ if (!success) break;
+
+ if (isTheora) {
+ result = TheoraVideoRTPSink
+ ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize);
+ } else { // Vorbis
+ result = VorbisAudioRTPSink
+ ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ track->samplingFrequency, track->numChannels,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize);
+ }
+ } while (0);
+
+ delete[] identificationHeader; delete[] commentHeader; delete[] setupHeader;
+ } else if (strcmp(track->mimeType, "video/H264") == 0) {
+ // Use our track's 'Codec Private' data: Bytes 5 and beyond contain SPS and PPSs:
+ u_int8_t* SPS = NULL; unsigned SPSSize = 0;
+ u_int8_t* PPS = NULL; unsigned PPSSize = 0;
+ u_int8_t* SPSandPPSBytes = NULL; unsigned numSPSandPPSBytes = 0;
+
+ do {
+ if (track->codecPrivateSize < 6) break;
+
+ numSPSandPPSBytes = track->codecPrivateSize - 5;
+ SPSandPPSBytes = &track->codecPrivate[5];
+
+ // Extract, from "SPSandPPSBytes", one SPS NAL unit, and one PPS NAL unit.
+ // (I hope one is all we need of each.)
+ unsigned i;
+ u_int8_t* ptr = SPSandPPSBytes;
+ u_int8_t* limit = &SPSandPPSBytes[numSPSandPPSBytes];
+
+ unsigned numSPSs = (*ptr++)&0x1F; CHECK_PTR;
+ for (i = 0; i < numSPSs; ++i) {
+ unsigned spsSize = (*ptr++)<<8; CHECK_PTR;
+ spsSize |= *ptr++; CHECK_PTR;
+
+ if (spsSize > NUM_BYTES_REMAINING) break;
+ u_int8_t nal_unit_type = ptr[0]&0x1F;
+ if (SPS == NULL && nal_unit_type == 7/*sanity check*/) { // save the first one
+ SPSSize = spsSize;
+ SPS = new u_int8_t[spsSize];
+ memmove(SPS, ptr, spsSize);
+ }
+ ptr += spsSize;
+ }
+
+ unsigned numPPSs = (*ptr++)&0x1F; CHECK_PTR;
+ for (i = 0; i < numPPSs; ++i) {
+ unsigned ppsSize = (*ptr++)<<8; CHECK_PTR;
+ ppsSize |= *ptr++; CHECK_PTR;
+
+ if (ppsSize > NUM_BYTES_REMAINING) break;
+ u_int8_t nal_unit_type = ptr[0]&0x1F;
+ if (PPS == NULL && nal_unit_type == 8/*sanity check*/) { // save the first one
+ PPSSize = ppsSize;
+ PPS = new u_int8_t[ppsSize];
+ memmove(PPS, ptr, ppsSize);
+ }
+ ptr += ppsSize;
+ }
+ } while (0);
+
+ result = H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ SPS, SPSSize, PPS, PPSSize);
+
+ delete[] SPS; delete[] PPS;
+ } else if (strcmp(track->mimeType, "video/H265") == 0) {
+ u_int8_t* VPS = NULL; unsigned VPSSize = 0;
+ u_int8_t* SPS = NULL; unsigned SPSSize = 0;
+ u_int8_t* PPS = NULL; unsigned PPSSize = 0;
+ u_int8_t* VPS_SPS_PPSBytes = NULL; unsigned numVPS_SPS_PPSBytes = 0;
+ unsigned i;
+
+ do {
+ if (track->codecPrivateUsesH264FormatForH265) {
+ // The data uses the H.264-style format (but including VPS NAL unit(s)).
+ // The VPS,SPS,PPS NAL unit information starts at byte #5:
+ if (track->codecPrivateSize >= 6) {
+ numVPS_SPS_PPSBytes = track->codecPrivateSize - 5;
+ VPS_SPS_PPSBytes = &track->codecPrivate[5];
+ }
+ } else {
+ // The data uses the proper H.265-style format.
+ // The VPS,SPS,PPS NAL unit information starts at byte #22:
+ if (track->codecPrivateSize >= 23) {
+ numVPS_SPS_PPSBytes = track->codecPrivateSize - 22;
+ VPS_SPS_PPSBytes = &track->codecPrivate[22];
+ }
+ }
+
+ // Extract, from "VPS_SPS_PPSBytes", one VPS NAL unit, one SPS NAL unit, and one PPS NAL unit.
+ // (I hope one is all we need of each.)
+ if (numVPS_SPS_PPSBytes == 0 || VPS_SPS_PPSBytes == NULL) break; // sanity check
+ u_int8_t* ptr = VPS_SPS_PPSBytes;
+ u_int8_t* limit = &VPS_SPS_PPSBytes[numVPS_SPS_PPSBytes];
+
+ if (track->codecPrivateUsesH264FormatForH265) {
+ // The data uses the H.264-style format (but including VPS NAL unit(s)).
+ while (NUM_BYTES_REMAINING > 0) {
+ unsigned numNALUnits = (*ptr++)&0x1F; CHECK_PTR;
+ for (i = 0; i < numNALUnits; ++i) {
+ unsigned nalUnitLength = (*ptr++)<<8; CHECK_PTR;
+ nalUnitLength |= *ptr++; CHECK_PTR;
+
+ if (nalUnitLength > NUM_BYTES_REMAINING) break;
+ u_int8_t nal_unit_type = (ptr[0]&0x7E)>>1;
+ if (nal_unit_type == 32) { // VPS
+ VPSSize = nalUnitLength;
+ delete[] VPS; VPS = new u_int8_t[nalUnitLength];
+ memmove(VPS, ptr, nalUnitLength);
+ } else if (nal_unit_type == 33) { // SPS
+ SPSSize = nalUnitLength;
+ delete[] SPS; SPS = new u_int8_t[nalUnitLength];
+ memmove(SPS, ptr, nalUnitLength);
+ } else if (nal_unit_type == 34) { // PPS
+ PPSSize = nalUnitLength;
+ delete[] PPS; PPS = new u_int8_t[nalUnitLength];
+ memmove(PPS, ptr, nalUnitLength);
+ }
+ ptr += nalUnitLength;
+ }
+ }
+ } else {
+ // The data uses the proper H.265-style format.
+ unsigned numOfArrays = *ptr++; CHECK_PTR;
+ for (unsigned j = 0; j < numOfArrays; ++j) {
+ ++ptr; CHECK_PTR; // skip the 'array_completeness'|'reserved'|'NAL_unit_type' byte
+
+ unsigned numNalus = (*ptr++)<<8; CHECK_PTR;
+ numNalus |= *ptr++; CHECK_PTR;
+
+ for (i = 0; i < numNalus; ++i) {
+ unsigned nalUnitLength = (*ptr++)<<8; CHECK_PTR;
+ nalUnitLength |= *ptr++; CHECK_PTR;
+
+ if (nalUnitLength > NUM_BYTES_REMAINING) break;
+ u_int8_t nal_unit_type = (ptr[0]&0x7E)>>1;
+ if (nal_unit_type == 32) { // VPS
+ VPSSize = nalUnitLength;
+ delete[] VPS; VPS = new u_int8_t[nalUnitLength];
+ memmove(VPS, ptr, nalUnitLength);
+ } else if (nal_unit_type == 33) { // SPS
+ SPSSize = nalUnitLength;
+ delete[] SPS; SPS = new u_int8_t[nalUnitLength];
+ memmove(SPS, ptr, nalUnitLength);
+ } else if (nal_unit_type == 34) { // PPS
+ PPSSize = nalUnitLength;
+ delete[] PPS; PPS = new u_int8_t[nalUnitLength];
+ memmove(PPS, ptr, nalUnitLength);
+ }
+ ptr += nalUnitLength;
+ }
+ }
+ }
+ } while (0);
+
+ result = H265VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ VPS, VPSSize, SPS, SPSSize, PPS, PPSSize);
+ delete[] VPS; delete[] SPS; delete[] PPS;
+ } else if (strcmp(track->mimeType, "video/VP8") == 0) {
+ result = VP8VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
+ } else if (strcmp(track->mimeType, "video/VP9") == 0) {
+ result = VP9VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
+ } else if (strcmp(track->mimeType, "text/T140") == 0) {
+ result = T140TextRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
+ }
+ } while (0);
+
+ return result;
+}
+
void MatroskaFile::addTrack(MatroskaTrack* newTrack, unsigned trackNumber) {
fTrackTable->add(newTrack, trackNumber);
}
@@ -301,7 +663,8 @@ MatroskaTrack::MatroskaTrack()
defaultDuration(0),
name(NULL), language(NULL), codecID(NULL),
samplingFrequency(0), numChannels(2), mimeType(""),
- codecPrivateSize(0), codecPrivate(NULL), codecPrivateUsesH264FormatForH265(False),
+ codecPrivateSize(0), codecPrivate(NULL),
+ codecPrivateUsesH264FormatForH265(False), codecIsOpus(False),
headerStrippedBytesSize(0), headerStrippedBytes(NULL),
subframeSizeSize(0) {
}
@@ -359,9 +722,9 @@ FramedSource* MatroskaDemux::newDemuxedTrack(unsigned& resultTrackNumber) {
FramedSource* MatroskaDemux::newDemuxedTrackByTrackNumber(unsigned trackNumber) {
if (trackNumber == 0) return NULL;
- FramedSource* track = new MatroskaDemuxedTrack(envir(), trackNumber, *this);
- fDemuxedTracksTable->Add((char const*)trackNumber, track);
- return track;
+ FramedSource* trackSource = new MatroskaDemuxedTrack(envir(), trackNumber, *this);
+ fDemuxedTracksTable->Add((char const*)trackNumber, trackSource);
+ return trackSource;
}
MatroskaDemuxedTrack* MatroskaDemux::lookupDemuxedTrack(unsigned trackNumber) {
@@ -372,7 +735,7 @@ void MatroskaDemux::removeTrack(unsigned trackNumber) {
fDemuxedTracksTable->Remove((char const*)trackNumber);
if (fDemuxedTracksTable->numEntries() == 0) {
// We no longer have any demuxed tracks, so delete ourselves now:
- delete this;
+ Medium::close(this);
}
}
@@ -407,7 +770,7 @@ void MatroskaDemux::handleEndOfFile() {
for (i = 0; i < numTracks; ++i) {
if (tracks[i] == NULL) continue; // sanity check; shouldn't happen
- FramedSource::handleClosure(tracks[i]);
+ tracks[i]->handleClosure();
}
delete[] tracks;
@@ -426,10 +789,6 @@ CuePoint::~CuePoint() {
delete fSubTree[0]; delete fSubTree[1];
}
-#ifndef ABS
-#define ABS(x) (x)<0 ? -(x) : (x)
-#endif
-
void CuePoint::addCuePoint(CuePoint*& root, double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster,
Boolean& needToReviseBalanceOfParent) {
needToReviseBalanceOfParent = False; // by default; may get changed below
diff --git a/liveMedia/MatroskaFileParser.cpp b/liveMedia/MatroskaFileParser.cpp
index 9e0f666..83b9151 100644
--- a/liveMedia/MatroskaFileParser.cpp
+++ b/liveMedia/MatroskaFileParser.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A parser for a Matroska file.
// Implementation
@@ -100,7 +100,7 @@ void MatroskaFileParser::continueParsing() {
}
}
- // We successfully parsed the file's 'Track' headers. Call our 'done' function now:
+ // We successfully parsed the file. Call our 'done' function now:
if (fOnEndFunc != NULL) (*fOnEndFunc)(fOnEndClientData);
}
@@ -433,6 +433,32 @@ Boolean MatroskaFileParser::parseTrack() {
#endif
if (track != NULL) {
delete[] track->codecID; track->codecID = codecID;
+
+ // Also set the track's "mimeType" field, if we can deduce it from the "codecID":
+ if (strncmp(codecID, "A_MPEG", 6) == 0) {
+ track->mimeType = "audio/MPEG";
+ } else if (strncmp(codecID, "A_AAC", 5) == 0) {
+ track->mimeType = "audio/AAC";
+ } else if (strncmp(codecID, "A_AC3", 5) == 0) {
+ track->mimeType = "audio/AC3";
+ } else if (strncmp(codecID, "A_VORBIS", 8) == 0) {
+ track->mimeType = "audio/VORBIS";
+ } else if (strcmp(codecID, "A_OPUS") == 0) {
+ track->mimeType = "audio/OPUS";
+ track->codecIsOpus = True;
+ } else if (strcmp(codecID, "V_MPEG4/ISO/AVC") == 0) {
+ track->mimeType = "video/H264";
+ } else if (strcmp(codecID, "V_MPEGH/ISO/HEVC") == 0) {
+ track->mimeType = "video/H265";
+ } else if (strncmp(codecID, "V_VP8", 5) == 0) {
+ track->mimeType = "video/VP8";
+ } else if (strncmp(codecID, "V_VP9", 5) == 0) {
+ track->mimeType = "video/VP9";
+ } else if (strncmp(codecID, "V_THEORA", 8) == 0) {
+ track->mimeType = "video/THEORA";
+ } else if (strncmp(codecID, "S_TEXT", 6) == 0) {
+ track->mimeType = "text/T140";
+ }
} else {
delete[] codecID;
}
@@ -458,7 +484,7 @@ Boolean MatroskaFileParser::parseTrack() {
if (track->codecID != NULL) {
if (strcmp(track->codecID, "V_MPEG4/ISO/AVC") == 0) { // H.264
// Byte 4 of the 'codec private' data contains 'lengthSizeMinusOne':
- if (codecPrivateSize >= 5) track->subframeSizeSize = (codecPrivate[4])&0x3 + 1;
+ if (codecPrivateSize >= 5) track->subframeSizeSize = (codecPrivate[4]&0x3) + 1;
} else if (strcmp(track->codecID, "V_MPEGH/ISO/HEVC") == 0) { // H.265
// H.265 'codec private' data is *supposed* to use the format that's described in
// http://lists.matroska.org/pipermail/matroska-devel/2013-September/004567.html
@@ -472,13 +498,13 @@ Boolean MatroskaFileParser::parseTrack() {
track->codecPrivateUsesH264FormatForH265 = True;
// Byte 4 of the 'codec private' data contains 'lengthSizeMinusOne':
- if (codecPrivateSize >= 5) track->subframeSizeSize = (codecPrivate[4])&0x3 + 1;
+ if (codecPrivateSize >= 5) track->subframeSizeSize = (codecPrivate[4]&0x3) + 1;
} else {
// This looks like the 'correct' format:
track->codecPrivateUsesH264FormatForH265 = False;
// Byte 21 of the 'codec private' data contains 'lengthSizeMinusOne':
- track->subframeSizeSize = (codecPrivate[21])&0x3 + 1;
+ track->subframeSizeSize = (codecPrivate[21]&0x3) + 1;
}
}
}
@@ -990,22 +1016,40 @@ Boolean MatroskaFileParser::deliverFrameWithinBlock() {
return False;
}
- unsigned frameSize = fFrameSizesWithinBlock[fNextFrameNumberToDeliver];
- if (track->haveSubframes()) {
- // The next "track->subframeSizeSize" bytes contain the length of a 'subframe':
- if (fCurOffsetWithinFrame + track->subframeSizeSize > frameSize) break; // sanity check
- unsigned subframeSize = 0;
- for (unsigned i = 0; i < track->subframeSizeSize; ++i) {
- u_int8_t c;
- getCommonFrameBytes(track, &c, 1, 0);
- if (fCurFrameNumBytesToGet > 0) { // it'll be 1
- c = get1Byte();
- ++fCurOffsetWithinFrame;
+ unsigned frameSize;
+ u_int8_t const* specialFrameSource = NULL;
+ u_int8_t const opusCommentHeader[16]
+ = {'O','p','u','s','T','a','g','s', 0, 0, 0, 0, 0, 0, 0, 0};
+ if (track->codecIsOpus && demuxedTrack->fOpusTrackNumber < 2) {
+ // Special case for Opus audio. The first frame (the 'configuration' header) comes from
+ // the 'private data'. The second frame (the 'comment' header) comes is synthesized by
+ // us here:
+ if (demuxedTrack->fOpusTrackNumber == 0) {
+ specialFrameSource = track->codecPrivate;
+ frameSize = track->codecPrivateSize;
+ } else { // demuxedTrack->fOpusTrackNumber == 1
+ specialFrameSource = opusCommentHeader;
+ frameSize = sizeof opusCommentHeader;
+ }
+ ++demuxedTrack->fOpusTrackNumber;
+ } else {
+ frameSize = fFrameSizesWithinBlock[fNextFrameNumberToDeliver];
+ if (track->haveSubframes()) {
+ // The next "track->subframeSizeSize" bytes contain the length of a 'subframe':
+ if (fCurOffsetWithinFrame + track->subframeSizeSize > frameSize) break; // sanity check
+ unsigned subframeSize = 0;
+ for (unsigned i = 0; i < track->subframeSizeSize; ++i) {
+ u_int8_t c;
+ getCommonFrameBytes(track, &c, 1, 0);
+ if (fCurFrameNumBytesToGet > 0) { // it'll be 1
+ c = get1Byte();
+ ++fCurOffsetWithinFrame;
+ }
+ subframeSize = subframeSize*256 + c;
}
- subframeSize = subframeSize*256 + c;
+ if (subframeSize == 0 || fCurOffsetWithinFrame + subframeSize > frameSize) break; // sanity check
+ frameSize = subframeSize;
}
- if (subframeSize == 0 || fCurOffsetWithinFrame + subframeSize > frameSize) break; // sanity check
- frameSize = subframeSize;
}
// Compute the presentation time of this frame (from the cluster timecode, the block timecode, and the default duration):
@@ -1023,12 +1067,17 @@ Boolean MatroskaFileParser::deliverFrameWithinBlock() {
struct timeval presentationTime;
presentationTime.tv_sec = (unsigned)pt;
presentationTime.tv_usec = (unsigned)((pt - presentationTime.tv_sec)*1000000);
- unsigned durationInMicroseconds = track->defaultDuration/1000;
- if (track->haveSubframes()) {
- // If this is a 'subframe', use a duration of 0 instead (unless it's the last 'subframe'):
- if (fCurOffsetWithinFrame + frameSize + track->subframeSizeSize < fFrameSizesWithinBlock[fNextFrameNumberToDeliver]) {
- // There's room for at least one more subframe after this, so give this subframe a duration of 0
- durationInMicroseconds = 0;
+ unsigned durationInMicroseconds;
+ if (specialFrameSource != NULL) {
+ durationInMicroseconds = 0;
+ } else { // normal case
+ durationInMicroseconds = track->defaultDuration/1000;
+ if (track->haveSubframes()) {
+ // If this is a 'subframe', use a duration of 0 instead (unless it's the last 'subframe'):
+ if (fCurOffsetWithinFrame + frameSize + track->subframeSizeSize < fFrameSizesWithinBlock[fNextFrameNumberToDeliver]) {
+ // There's room for at least one more subframe after this, so give this subframe a duration of 0
+ durationInMicroseconds = 0;
+ }
}
}
@@ -1069,8 +1118,19 @@ Boolean MatroskaFileParser::deliverFrameWithinBlock() {
getCommonFrameBytes(track, demuxedTrack->to(), demuxedTrack->frameSize(), demuxedTrack->numTruncatedBytes());
// Next, deliver (and/or skip) bytes from the input file:
- fCurrentParseState = DELIVERING_FRAME_BYTES;
- setParseState();
+ if (specialFrameSource != NULL) {
+ memmove(demuxedTrack->to(), specialFrameSource, demuxedTrack->frameSize());
+#ifdef DEBUG
+ fprintf(stderr, "\tdelivered special frame: %d bytes", demuxedTrack->frameSize());
+ if (demuxedTrack->numTruncatedBytes() > 0) fprintf(stderr, " (%d bytes truncated)", demuxedTrack->numTruncatedBytes());
+ fprintf(stderr, " @%u.%06u (%.06f from start); duration %u us\n", demuxedTrack->presentationTime().tv_sec, demuxedTrack->presentationTime().tv_usec, demuxedTrack->presentationTime().tv_sec+demuxedTrack->presentationTime().tv_usec/1000000.0-fPresentationTimeOffset, demuxedTrack->durationInMicroseconds());
+#endif
+ setParseState();
+ FramedSource::afterGetting(demuxedTrack); // completes delivery
+ } else { // normal case
+ fCurrentParseState = DELIVERING_FRAME_BYTES;
+ setParseState();
+ }
return True;
} while (0);
diff --git a/liveMedia/MatroskaFileParser.hh b/liveMedia/MatroskaFileParser.hh
index cab91c5..7668b88 100644
--- a/liveMedia/MatroskaFileParser.hh
+++ b/liveMedia/MatroskaFileParser.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A parser for a Matroska file.
// C++ header
diff --git a/liveMedia/MatroskaFileServerDemux.cpp b/liveMedia/MatroskaFileServerDemux.cpp
index 340aadc..e94fdc2 100644
--- a/liveMedia/MatroskaFileServerDemux.cpp
+++ b/liveMedia/MatroskaFileServerDemux.cpp
@@ -14,19 +14,13 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A server demultiplexor for a Matroska file
// Implementation
#include "MatroskaFileServerDemux.hh"
#include "MP3AudioMatroskaFileServerMediaSubsession.hh"
-#include "AACAudioMatroskaFileServerMediaSubsession.hh"
-#include "AC3AudioMatroskaFileServerMediaSubsession.hh"
-#include "VorbisAudioMatroskaFileServerMediaSubsession.hh"
-#include "H264VideoMatroskaFileServerMediaSubsession.hh"
-#include "H265VideoMatroskaFileServerMediaSubsession.hh"
-#include "VP8VideoMatroskaFileServerMediaSubsession.hh"
-#include "T140TextMatroskaFileServerMediaSubsession.hh"
+#include "MatroskaFileServerMediaSubsession.hh"
void MatroskaFileServerDemux
::createNew(UsageEnvironment& env, char const* fileName,
@@ -65,30 +59,10 @@ ServerMediaSubsession* MatroskaFileServerDemux
// Use the track's "codecID" string to figure out which "ServerMediaSubsession" subclass to use:
ServerMediaSubsession* result = NULL;
- if (strncmp(track->codecID, "A_MPEG", 6) == 0) {
- track->mimeType = "audio/MPEG";
- result = MP3AudioMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber, False, NULL);
- } else if (strncmp(track->codecID, "A_AAC", 5) == 0) {
- track->mimeType = "audio/AAC";
- result = AACAudioMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
- } else if (strncmp(track->codecID, "A_AC3", 5) == 0) {
- track->mimeType = "audio/AC3";
- result = AC3AudioMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
- } else if (strncmp(track->codecID, "A_VORBIS", 8) == 0) {
- track->mimeType = "audio/VORBIS";
- result = VorbisAudioMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
- } else if (strcmp(track->codecID, "V_MPEG4/ISO/AVC") == 0) {
- track->mimeType = "video/H264";
- result = H264VideoMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
- } else if (strcmp(track->codecID, "V_MPEGH/ISO/HEVC") == 0) {
- track->mimeType = "video/H265";
- result = H265VideoMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
- } else if (strncmp(track->codecID, "V_VP8", 5) == 0) {
- track->mimeType = "video/VP8";
- result = VP8VideoMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
- } else if (strncmp(track->codecID, "S_TEXT", 6) == 0) {
- track->mimeType = "text/T140";
- result = T140TextMatroskaFileServerMediaSubsession::createNew(*this, track->trackNumber);
+ if (strcmp(track->mimeType, "audio/MPEG") == 0) {
+ result = MP3AudioMatroskaFileServerMediaSubsession::createNew(*this, track);
+ } else {
+ result = MatroskaFileServerMediaSubsession::createNew(*this, track);
}
if (result != NULL) {
diff --git a/liveMedia/MatroskaFileServerMediaSubsession.cpp b/liveMedia/MatroskaFileServerMediaSubsession.cpp
new file mode 100644
index 0000000..c4bc1de
--- /dev/null
+++ b/liveMedia/MatroskaFileServerMediaSubsession.cpp
@@ -0,0 +1,65 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
+// on demand, from a track within a Matroska file.
+// Implementation
+
+#include "MatroskaFileServerMediaSubsession.hh"
+#include "MatroskaDemuxedTrack.hh"
+#include "FramedFilter.hh"
+
+MatroskaFileServerMediaSubsession* MatroskaFileServerMediaSubsession
+::createNew(MatroskaFileServerDemux& demux, MatroskaTrack* track) {
+ return new MatroskaFileServerMediaSubsession(demux, track);
+}
+
+MatroskaFileServerMediaSubsession
+::MatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, MatroskaTrack* track)
+ : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
+ fOurDemux(demux), fTrack(track), fNumFiltersInFrontOfTrack(0) {
+}
+
+MatroskaFileServerMediaSubsession::~MatroskaFileServerMediaSubsession() {
+}
+
+float MatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
+
+void MatroskaFileServerMediaSubsession
+::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
+ for (unsigned i = 0; i < fNumFiltersInFrontOfTrack; ++i) {
+ // "inputSource" is a filter. Go back to *its* source:
+ inputSource = ((FramedFilter*)inputSource)->inputSource();
+ }
+ ((MatroskaDemuxedTrack*)inputSource)->seekToTime(seekNPT);
+}
+
+FramedSource* MatroskaFileServerMediaSubsession
+::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
+ FramedSource* baseSource = fOurDemux.newDemuxedTrack(clientSessionId, fTrack->trackNumber);
+ if (baseSource == NULL) return NULL;
+
+ return fOurDemux.ourMatroskaFile()
+ ->createSourceForStreaming(baseSource, fTrack->trackNumber,
+ estBitrate, fNumFiltersInFrontOfTrack);
+}
+
+RTPSink* MatroskaFileServerMediaSubsession
+::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
+ return fOurDemux.ourMatroskaFile()
+ ->createRTPSinkForTrackNumber(fTrack->trackNumber, rtpGroupsock, rtpPayloadTypeIfDynamic);
+}
diff --git a/liveMedia/AC3AudioMatroskaFileServerMediaSubsession.hh b/liveMedia/MatroskaFileServerMediaSubsession.hh
similarity index 67%
rename from liveMedia/AC3AudioMatroskaFileServerMediaSubsession.hh
rename to liveMedia/MatroskaFileServerMediaSubsession.hh
index be9835b..43aaada 100644
--- a/liveMedia/AC3AudioMatroskaFileServerMediaSubsession.hh
+++ b/liveMedia/MatroskaFileServerMediaSubsession.hh
@@ -14,13 +14,13 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an AC3 audio track within a Matroska file.
+// on demand, from a track within a Matroska file.
// C++ header
-#ifndef _AC3_AUDIO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _AC3_AUDIO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
+#ifndef _MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
+#define _MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
#ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
#include "FileServerMediaSubsession.hh"
@@ -29,26 +29,27 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "MatroskaFileServerDemux.hh"
#endif
-class AC3AudioMatroskaFileServerMediaSubsession: public FileServerMediaSubsession {
+class MatroskaFileServerMediaSubsession: public FileServerMediaSubsession {
public:
- static AC3AudioMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
+ static MatroskaFileServerMediaSubsession*
+ createNew(MatroskaFileServerDemux& demux, MatroskaTrack* track);
-private:
- AC3AudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~AC3AudioMatroskaFileServerMediaSubsession();
+protected:
+ MatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, MatroskaTrack* track);
+ // called only by createNew(), or by subclass constructors
+ virtual ~MatroskaFileServerMediaSubsession();
-private: // redefined virtual functions
+protected: // redefined virtual functions
virtual float duration() const;
virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
-private:
+protected:
MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
+ MatroskaTrack* fTrack;
+ unsigned fNumFiltersInFrontOfTrack;
};
#endif
diff --git a/liveMedia/Media.cpp b/liveMedia/Media.cpp
index 058e746..c0f90f0 100644
--- a/liveMedia/Media.cpp
+++ b/liveMedia/Media.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Media
// Implementation
@@ -87,10 +87,6 @@ Boolean Medium::isServerMediaSession() const {
return False; // default implementation
}
-Boolean Medium::isDarwinInjector() const {
- return False; // default implementation
-}
-
////////// _Tables implementation //////////
diff --git a/liveMedia/MediaSession.cpp b/liveMedia/MediaSession.cpp
index 8019233..88829a7 100644
--- a/liveMedia/MediaSession.cpp
+++ b/liveMedia/MediaSession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A data structure that represents a session that consists of
// potentially multiple (audio and/or video) sub-sessions
// Implementation
@@ -61,8 +61,8 @@ MediaSession::MediaSession(UsageEnvironment& env)
fSubsessionsHead(NULL), fSubsessionsTail(NULL),
fConnectionEndpointName(NULL),
fMaxPlayStartTime(0.0f), fMaxPlayEndTime(0.0f), fAbsStartTime(NULL), fAbsEndTime(NULL),
- fScale(1.0f), fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL),
- fControlPath(NULL) {
+ fScale(1.0f), fSpeed(1.0f),
+ fMediaSessionType(NULL), fSessionName(NULL), fSessionDescription(NULL), fControlPath(NULL) {
fSourceFilterAddr.s_addr = 0;
// Get our host name, and use this for the RTCP CNAME:
@@ -206,6 +206,7 @@ Boolean MediaSession::initializeWithSDP(char const* sdpDescription) {
if (subsession->parseSDPLine_c(sdpLine)) continue;
if (subsession->parseSDPLine_b(sdpLine)) continue;
if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue;
+ if (subsession->parseSDPAttribute_rtcpmux(sdpLine)) continue;
if (subsession->parseSDPAttribute_control(sdpLine)) continue;
if (subsession->parseSDPAttribute_range(sdpLine)) continue;
if (subsession->parseSDPAttribute_fmtp(sdpLine)) continue;
@@ -569,6 +570,27 @@ void MediaSubsessionIterator::reset() {
fNextPtr = fOurSession.fSubsessionsHead;
}
+
+////////// SDPAttribute definition //////////
+
+class SDPAttribute {
+public:
+ SDPAttribute(char const* strValue, Boolean valueIsHexadecimal);
+ virtual ~SDPAttribute();
+
+ char const* strValue() const { return fStrValue; }
+ char const* strValueToLower() const { return fStrValueToLower; }
+ int intValue() const { return fIntValue; }
+ Boolean valueIsHexadecimal() const { return fValueIsHexadecimal; }
+
+private:
+ char* fStrValue;
+ char* fStrValueToLower;
+ int fIntValue;
+ Boolean fValueIsHexadecimal;
+};
+
+
////////// MediaSubsession //////////
MediaSubsession::MediaSubsession(MediaSession& parent)
@@ -577,22 +599,24 @@ MediaSubsession::MediaSubsession(MediaSession& parent)
fConnectionEndpointName(NULL),
fClientPortNum(0), fRTPPayloadFormat(0xFF),
fSavedSDPLines(NULL), fMediumName(NULL), fCodecName(NULL), fProtocolName(NULL),
- fRTPTimestampFrequency(0), fControlPath(NULL),
+ fRTPTimestampFrequency(0), fMultiplexRTCPWithRTP(False), fControlPath(NULL),
fSourceFilterAddr(parent.sourceFilterAddr()), fBandwidth(0),
- fAuxiliarydatasizelength(0), fConstantduration(0), fConstantsize(0),
- fCRC(0), fCtsdeltalength(0), fDe_interleavebuffersize(0), fDtsdeltalength(0),
- fIndexdeltalength(0), fIndexlength(0), fInterleaving(0), fMaxdisplacement(0),
- fObjecttype(0), fOctetalign(0), fProfile_level_id(0), fRobustsorting(0),
- fSizelength(0), fStreamstateindication(0), fStreamtype(0),
- fCpresent(False), fRandomaccessindication(False),
- fConfig(NULL), fMode(NULL), fSpropParameterSets(NULL), fEmphasis(NULL), fChannelOrder(NULL),
fPlayStartTime(0.0), fPlayEndTime(0.0), fAbsStartTime(NULL), fAbsEndTime(NULL),
fVideoWidth(0), fVideoHeight(0), fVideoFPS(0), fNumChannels(1), fScale(1.0f), fNPT_PTS_Offset(0.0f),
+ fAttributeTable(HashTable::create(STRING_HASH_KEYS)),
fRTPSocket(NULL), fRTCPSocket(NULL),
fRTPSource(NULL), fRTCPInstance(NULL), fReadSource(NULL),
fReceiveRawMP3ADUs(False), fReceiveRawJPEGFrames(False),
fSessionId(NULL) {
rtpInfo.seqNum = 0; rtpInfo.timestamp = 0; rtpInfo.infoIsNew = False;
+
+ // A few attributes have unusual default values. Set these now:
+ setAttribute("profile-level-id", "0", True/*value is hexadecimal*/); // used with "video/H264"
+ // This won't work for MPEG-4 (unless the value is <10), because for MPEG-4, the value
+ // is assumed to be a decimal string, not a hexadecimal string. NEED TO FIX #####
+ setAttribute("profile-id", "1"); // used with "video/H265"
+ setAttribute("level-id", "93"); // used with "video/H265"
+ setAttribute("interop-constraints", "B00000000000"); // used with "video/H265"
}
MediaSubsession::~MediaSubsession() {
@@ -601,15 +625,20 @@ MediaSubsession::~MediaSubsession() {
delete[] fConnectionEndpointName; delete[] fSavedSDPLines;
delete[] fMediumName; delete[] fCodecName; delete[] fProtocolName;
delete[] fControlPath;
- delete[] fConfig; delete[] fMode; delete[] fSpropParameterSets; delete[] fEmphasis; delete[] fChannelOrder;
delete[] fAbsStartTime; delete[] fAbsEndTime;
delete[] fSessionId;
+ // Empty and delete our 'attributes table':
+ SDPAttribute* attr;
+ while ((attr = (SDPAttribute*)fAttributeTable->RemoveNext()) != NULL) {
+ delete attr;
+ }
+ delete fAttributeTable;
+
delete fNext;
}
void MediaSubsession::addFilter(FramedFilter* filter){
- if (filter == NULL || filter->inputSource() != fReadSource) return; // sanity check
fReadSource = filter;
}
@@ -662,7 +691,7 @@ Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
if (fClientPortNum != 0 && (honorSDPPortChoice || IsMulticastAddress(tempAddr.s_addr))) {
// The sockets' port numbers were specified for us. Use these:
Boolean const protocolIsRTP = strcmp(fProtocolName, "RTP") == 0;
- if (protocolIsRTP) {
+ if (protocolIsRTP && !fMultiplexRTCPWithRTP) {
fClientPortNum = fClientPortNum&~1;
// use an even-numbered port for RTP, and the next (odd-numbered) port for RTCP
}
@@ -677,17 +706,24 @@ Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
}
if (protocolIsRTP) {
- // Set our RTCP port to be the RTP port +1
- portNumBits const rtcpPortNum = fClientPortNum|1;
- if (isSSM()) {
- fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
+ if (fMultiplexRTCPWithRTP) {
+ // Use the RTP 'groupsock' object for RTCP as well:
+ fRTCPSocket = fRTPSocket;
} else {
- fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
+ // Set our RTCP port to be the RTP port + 1:
+ portNumBits const rtcpPortNum = fClientPortNum|1;
+ if (isSSM()) {
+ fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);
+ } else {
+ fRTCPSocket = new Groupsock(env(), tempAddr, rtcpPortNum, 255);
+ }
}
}
} else {
// Port numbers were not specified in advance, so we use ephemeral port numbers.
// Create sockets until we get a port-number pair (even: RTP; even+1: RTCP).
+ // (However, if we're multiplexing RTCP with RTP, then we create only one socket,
+ // and the port number can be even or odd.)
// We need to make sure that we don't keep trying to use the same bad port numbers over
// and over again, so we store bad sockets in a table, and delete them all when we're done.
HashTable* socketHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
@@ -708,12 +744,21 @@ Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
break;
}
- // Get the client port number, and check whether it's even (for RTP):
+ // Get the client port number:
Port clientPort(0);
if (!getSourcePort(env(), fRTPSocket->socketNum(), clientPort)) {
break;
}
fClientPortNum = ntohs(clientPort.num());
+
+ if (fMultiplexRTCPWithRTP) {
+ // Use this RTP 'groupsock' object for RTCP as well:
+ fRTCPSocket = fRTPSocket;
+ success = True;
+ break;
+ }
+
+ // To be usable for RTP, the client port number must be even:
if ((fClientPortNum&1) != 0) { // it's odd
// Record this socket in our table, and keep trying:
unsigned key = (unsigned)fClientPortNum;
@@ -807,8 +852,9 @@ void MediaSubsession::deInitiate() {
Medium::close(fReadSource); // this is assumed to also close fRTPSource
fReadSource = NULL; fRTPSource = NULL;
- delete fRTPSocket; fRTPSocket = NULL;
- delete fRTCPSocket; fRTCPSocket = NULL;
+ delete fRTPSocket;
+ if (fRTCPSocket != fRTPSocket) delete fRTCPSocket;
+ fRTPSocket = NULL; fRTCPSocket = NULL;
}
Boolean MediaSubsession::setClientPortNum(unsigned short portNum) {
@@ -821,6 +867,34 @@ Boolean MediaSubsession::setClientPortNum(unsigned short portNum) {
return True;
}
+char const* MediaSubsession::attrVal_str(char const* attrName) const {
+ SDPAttribute* attr = (SDPAttribute*)(fAttributeTable->Lookup(attrName));
+ if (attr == NULL) return "";
+
+ return attr->strValue();
+}
+
+char const* MediaSubsession::attrVal_strToLower(char const* attrName) const {
+ SDPAttribute* attr = (SDPAttribute*)(fAttributeTable->Lookup(attrName));
+ if (attr == NULL) return "";
+
+ return attr->strValueToLower();
+}
+
+unsigned MediaSubsession::attrVal_int(char const* attrName) const {
+ SDPAttribute* attr = (SDPAttribute*)(fAttributeTable->Lookup(attrName));
+ if (attr == NULL) return 0;
+
+ return attr->intValue();
+}
+
+char const* MediaSubsession::fmtp_config() const {
+ char const* result = attrVal_str("config");
+ if (result[0] == '\0') result = attrVal_str("configuration");
+
+ return result;
+}
+
netAddressBits MediaSubsession::connectionEndpointAddress() const {
do {
// Get the endpoint name from with us, or our parent session:
@@ -855,7 +929,7 @@ void MediaSubsession::setDestinations(netAddressBits defaultDestAddress) {
Port destPort(serverPortNum);
fRTPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
}
- if (fRTCPSocket != NULL && !isSSM()) {
+ if (fRTCPSocket != NULL && !isSSM() && !fMultiplexRTCPWithRTP) {
// Note: For SSM sessions, the dest address for RTCP was already set.
Port destPort(serverPortNum+1);
fRTCPSocket->changeDestinationParameters(destAddr, destPort, destTTL);
@@ -903,6 +977,21 @@ double MediaSubsession::getNormalPlayTime(struct timeval const& presentationTime
}
}
+void MediaSubsession
+::setAttribute(char const* name, char const* value, Boolean valueIsHexadecimal) {
+ // Replace any existing attribute record with this name (except that the 'valueIsHexadecimal'
+ // property will be inherited from it, if it exists).
+ SDPAttribute* oldAttr = (SDPAttribute*)fAttributeTable->Lookup(name);
+ if (oldAttr != NULL) {
+ valueIsHexadecimal = oldAttr->valueIsHexadecimal();
+ fAttributeTable->Remove(name);
+ delete oldAttr;
+ }
+
+ SDPAttribute* newAttr = new SDPAttribute(value, valueIsHexadecimal);
+ (void)fAttributeTable->Add(name, newAttr);
+}
+
Boolean MediaSubsession::parseSDPLine_c(char const* sdpLine) {
// Check for "c=IN IP4 <connection-endpoint>"
// or "c=IN IP4 <connection-endpoint>/<ttl+numAddresses>"
@@ -959,6 +1048,15 @@ Boolean MediaSubsession::parseSDPAttribute_rtpmap(char const* sdpLine) {
return parseSuccess;
}
+Boolean MediaSubsession::parseSDPAttribute_rtcpmux(char const* sdpLine) {
+ if (strncmp(sdpLine, "a=rtcp-mux", 10) == 0) {
+ fMultiplexRTCPWithRTP = True;
+ return True;
+ }
+
+ return False;
+}
+
Boolean MediaSubsession::parseSDPAttribute_control(char const* sdpLine) {
// Check for a "a=control:<control-path>" line:
Boolean parseSuccess = False;
@@ -1003,116 +1101,42 @@ Boolean MediaSubsession::parseSDPAttribute_range(char const* sdpLine) {
Boolean MediaSubsession::parseSDPAttribute_fmtp(char const* sdpLine) {
// Check for a "a=fmtp:" line:
- // TEMP: We check only for a handful of expected parameter names #####
- // Later: (i) check that payload format number matches; #####
- // (ii) look for other parameters also (generalize?) #####
+ // Later: Check that payload format number matches; #####
do {
if (strncmp(sdpLine, "a=fmtp:", 7) != 0) break; sdpLine += 7;
while (isdigit(*sdpLine)) ++sdpLine;
// The remaining "sdpLine" should be a sequence of
// <name>=<value>;
+ // or
+ // <name>;
// parameter assignments. Look at each of these.
- // First, convert the line to lower-case, to ease comparison:
- char* const lineCopy = strDup(sdpLine); char* line = lineCopy;
- {
- Locale l("POSIX");
- for (char* c = line; *c != '\0'; ++c) *c = tolower(*c);
- }
- while (*line != '\0' && *line != '\r' && *line != '\n') {
- unsigned u;
- char* valueStr = strDupSize(line);
- if (sscanf(line, " auxiliarydatasizelength = %u", &u) == 1) {
- fAuxiliarydatasizelength = u;
- } else if (sscanf(line, " constantduration = %u", &u) == 1) {
- fConstantduration = u;
- } else if (sscanf(line, " constantsize; = %u", &u) == 1) {
- fConstantsize = u;
- } else if (sscanf(line, " crc = %u", &u) == 1) {
- fCRC = u;
- } else if (sscanf(line, " ctsdeltalength = %u", &u) == 1) {
- fCtsdeltalength = u;
- } else if (sscanf(line, " de-interleavebuffersize = %u", &u) == 1) {
- fDe_interleavebuffersize = u;
- } else if (sscanf(line, " dtsdeltalength = %u", &u) == 1) {
- fDtsdeltalength = u;
- } else if (sscanf(line, " indexdeltalength = %u", &u) == 1) {
- fIndexdeltalength = u;
- } else if (sscanf(line, " indexlength = %u", &u) == 1) {
- fIndexlength = u;
- } else if (sscanf(line, " interleaving = %u", &u) == 1) {
- fInterleaving = u;
- } else if (sscanf(line, " maxdisplacement = %u", &u) == 1) {
- fMaxdisplacement = u;
- } else if (sscanf(line, " objecttype = %u", &u) == 1) {
- fObjecttype = u;
- } else if (sscanf(line, " octet-align = %u", &u) == 1) {
- fOctetalign = u;
- } else if (sscanf(line, " profile-level-id = %x", &u) == 1) {
- // Note that the "profile-level-id" parameter is assumed to be hexadecimal
- fProfile_level_id = u;
- } else if (sscanf(line, " robust-sorting = %u", &u) == 1) {
- fRobustsorting = u;
- } else if (sscanf(line, " sizelength = %u", &u) == 1) {
- fSizelength = u;
- } else if (sscanf(line, " streamstateindication = %u", &u) == 1) {
- fStreamstateindication = u;
- } else if (sscanf(line, " streamtype = %u", &u) == 1) {
- fStreamtype = u;
- } else if (sscanf(line, " cpresent = %u", &u) == 1) {
- fCpresent = u != 0;
- } else if (sscanf(line, " randomaccessindication = %u", &u) == 1) {
- fRandomaccessindication = u != 0;
- } else if (sscanf(sdpLine, " config = %[^; \t\r\n]", valueStr) == 1 ||
- sscanf(sdpLine, " configuration = %[^; \t\r\n]", valueStr) == 1) {
- // Note: We used "sdpLine" here, because the value may be case-sensitive (if it's Base-64).
- delete[] fConfig; fConfig = strDup(valueStr);
- } else if (sscanf(line, " mode = %[^; \t\r\n]", valueStr) == 1) {
- delete[] fMode; fMode = strDup(valueStr);
- } else if (sscanf(sdpLine, " sprop-parameter-sets = %[^; \t\r\n]", valueStr) == 1) {
- // Note: We used "sdpLine" here, because the value is case-sensitive.
- delete[] fSpropParameterSets; fSpropParameterSets = strDup(valueStr);
- } else if (sscanf(line, " emphasis = %[^; \t\r\n]", valueStr) == 1) {
- delete[] fEmphasis; fEmphasis = strDup(valueStr);
- } else if (sscanf(sdpLine, " channel-order = %[^; \t\r\n]", valueStr) == 1) {
- // Note: We used "sdpLine" here, because the value is case-sensitive.
- delete[] fChannelOrder; fChannelOrder = strDup(valueStr);
- } else if (sscanf(line, " width = %u", &u) == 1) {
- // A non-standard parameter, but one that's often used:
- fVideoWidth = u;
- } else if (sscanf(line, " height = %u", &u) == 1) {
- // A non-standard parameter, but one that's often used:
- fVideoHeight = u;
- } else {
- // Some of the above parameters are Boolean. Check whether the parameter
- // names appear alone, without a "= 1" at the end:
- if (sscanf(line, " %[^; \t\r\n]", valueStr) == 1) {
- if (strcmp(valueStr, "octet-align") == 0) {
- fOctetalign = 1;
- } else if (strcmp(valueStr, "cpresent") == 0) {
- fCpresent = True;
- } else if (strcmp(valueStr, "crc") == 0) {
- fCRC = 1;
- } else if (strcmp(valueStr, "robust-sorting") == 0) {
- fRobustsorting = 1;
- } else if (strcmp(valueStr, "randomaccessindication") == 0) {
- fRandomaccessindication = True;
- }
+ unsigned const sdpLineLen = strlen(sdpLine);
+ char* nameStr = new char[sdpLineLen+1];
+ char* valueStr = new char[sdpLineLen+1];
+
+ while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n') {
+ int sscanfResult = sscanf(sdpLine, " %[^=; \t\r\n] = %[^; \t\r\n]", nameStr, valueStr);
+ if (sscanfResult >= 1) {
+ // <name> or <name>=<value>
+ // Convert <name> to lower-case, to ease comparison:
+ Locale l("POSIX");
+ for (char* c = nameStr; *c != '\0'; ++c) *c = tolower(*c);
+
+ if (sscanfResult == 1) {
+ // <name>
+ setAttribute(nameStr);
+ } else {
+ // <name>=<value>
+ setAttribute(nameStr, valueStr);
}
}
- delete[] valueStr;
// Move to the next parameter assignment string:
- while (*line != '\0' && *line != '\r' && *line != '\n'
- && *line != ';') ++line;
- while (*line == ';') ++line;
-
- // Do the same with sdpLine; needed for finding case sensitive values:
- while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n'
- && *sdpLine != ';') ++sdpLine;
+ while (*sdpLine != '\0' && *sdpLine != '\r' && *sdpLine != '\n' && *sdpLine != ';') ++sdpLine;
while (*sdpLine == ';') ++sdpLine;
}
- delete[] lineCopy;
+ delete[] nameStr; delete[] valueStr;
return True;
} while (0);
@@ -1184,15 +1208,19 @@ Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
fReadSource =
AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
fRTPPayloadFormat, False /*isWideband*/,
- fNumChannels, fOctetalign != 0, fInterleaving,
- fRobustsorting != 0, fCRC != 0);
+ fNumChannels, attrVal_bool("octet-align"),
+ attrVal_unsigned("interleaving"),
+ attrVal_bool("robust-sorting"),
+ attrVal_bool("crc"));
// Note that fReadSource will differ from fRTPSource in this case
} else if (strcmp(fCodecName, "AMR-WB") == 0) { // AMR audio (wideband)
fReadSource =
AMRAudioRTPSource::createNew(env(), fRTPSocket, fRTPSource,
fRTPPayloadFormat, True /*isWideband*/,
- fNumChannels, fOctetalign != 0, fInterleaving,
- fRobustsorting != 0, fCRC != 0);
+ fNumChannels, attrVal_bool("octet-align"),
+ attrVal_unsigned("interleaving"),
+ attrVal_bool("robust-sorting"),
+ attrVal_bool("crc"));
// Note that fReadSource will differ from fRTPSource in this case
} else if (strcmp(fCodecName, "MPA") == 0) { // MPEG-1 or 2 audio
fReadSource = fRTPSource
@@ -1236,11 +1264,19 @@ Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
= VorbisAudioRTPSource::createNew(env(), fRTPSocket,
fRTPPayloadFormat,
fRTPTimestampFrequency);
+ } else if (strcmp(fCodecName, "THEORA") == 0) { // Theora video
+ fReadSource = fRTPSource
+ = TheoraVideoRTPSource::createNew(env(), fRTPSocket, fRTPPayloadFormat);
} else if (strcmp(fCodecName, "VP8") == 0) { // VP8 video
fReadSource = fRTPSource
= VP8VideoRTPSource::createNew(env(), fRTPSocket,
fRTPPayloadFormat,
fRTPTimestampFrequency);
+ } else if (strcmp(fCodecName, "VP9") == 0) { // VP9 video
+ fReadSource = fRTPSource
+ = VP9VideoRTPSource::createNew(env(), fRTPSocket,
+ fRTPPayloadFormat,
+ fRTPTimestampFrequency);
} else if (strcmp(fCodecName, "AC3") == 0 || strcmp(fCodecName, "EAC3") == 0) { // AC3 audio
fReadSource = fRTPSource
= AC3AudioRTPSource::createNew(env(), fRTPSocket,
@@ -1256,9 +1292,10 @@ Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
= MPEG4GenericRTPSource::createNew(env(), fRTPSocket,
fRTPPayloadFormat,
fRTPTimestampFrequency,
- fMediumName, fMode,
- fSizelength, fIndexlength,
- fIndexdeltalength);
+ fMediumName, attrVal_strToLower("mode"),
+ attrVal_unsigned("sizelength"),
+ attrVal_unsigned("indexlength"),
+ attrVal_unsigned("indexdeltalength"));
} else if (strcmp(fCodecName, "MPV") == 0) { // MPEG-1 or 2 video
fReadSource = fRTPSource
= MPEG1or2VideoRTPSource::createNew(env(), fRTPSocket,
@@ -1286,6 +1323,13 @@ Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
= H264VideoRTPSource::createNew(env(), fRTPSocket,
fRTPPayloadFormat,
fRTPTimestampFrequency);
+ } else if (strcmp(fCodecName, "H265") == 0) {
+ Boolean expectDONFields = attrVal_unsigned("sprop-depack-buf-nalus") > 0;
+ fReadSource = fRTPSource
+ = H265VideoRTPSource::createNew(env(), fRTPSocket,
+ fRTPPayloadFormat,
+ expectDONFields,
+ fRTPTimestampFrequency);
} else if (strcmp(fCodecName, "DV") == 0) {
fReadSource = fRTPSource
= DVVideoRTPSource::createNew(env(), fRTPSocket,
@@ -1330,6 +1374,7 @@ Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
|| strcmp(fCodecName, "L16") == 0 // 16-bit linear audio
|| strcmp(fCodecName, "L20") == 0 // 20-bit linear audio (RFC 3190)
|| strcmp(fCodecName, "L24") == 0 // 24-bit linear audio (RFC 3190)
+ || strcmp(fCodecName, "G722") == 0 // G.722 audio (RFC 3551)
|| strcmp(fCodecName, "G726-16") == 0 // G.726, 16 kbps
|| strcmp(fCodecName, "G726-24") == 0 // G.726, 24 kbps
|| strcmp(fCodecName, "G726-32") == 0 // G.726, 32 kbps
@@ -1374,3 +1419,32 @@ Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {
return False; // an error occurred
}
+
+
+////////// SDPAttribute implementation //////////
+
+SDPAttribute::SDPAttribute(char const* strValue, Boolean valueIsHexadecimal)
+ : fStrValue(strDup(strValue)), fStrValueToLower(NULL), fValueIsHexadecimal(valueIsHexadecimal) {
+ if (fStrValue == NULL) {
+ // No value was given for this attribute, so consider it to be a Boolean, with value True:
+ fIntValue = 1;
+ } else {
+ // Create a 'tolower' version of "fStrValue", in case it's needed:
+ Locale l("POSIX");
+ size_t strSize;
+
+ fStrValueToLower = strDupSize(fStrValue, strSize);
+ for (unsigned i = 0; i < strSize-1; ++i) fStrValueToLower[i] = tolower(fStrValue[i]);
+ fStrValueToLower[strSize-1] = '\0';
+
+ // Try to parse "fStrValueToLower" as an integer. If we can't, assume an integer value of 0:
+ if (sscanf(fStrValueToLower, valueIsHexadecimal ? "%x" : "%d", &fIntValue) != 1) {
+ fIntValue = 0;
+ }
+ }
+}
+
+SDPAttribute::~SDPAttribute() {
+ delete[] fStrValue;
+ delete[] fStrValueToLower;
+}
diff --git a/liveMedia/MediaSink.cpp b/liveMedia/MediaSink.cpp
index 46694bd..1402ba4 100644
--- a/liveMedia/MediaSink.cpp
+++ b/liveMedia/MediaSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Media Sinks
// Implementation
@@ -112,11 +112,12 @@ Boolean MediaSink::isRTPSink() const {
unsigned OutPacketBuffer::maxSize = 60000; // by default
-OutPacketBuffer::OutPacketBuffer(unsigned preferredPacketSize,
- unsigned maxPacketSize)
+OutPacketBuffer
+::OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize, unsigned maxBufferSize)
: fPreferred(preferredPacketSize), fMax(maxPacketSize),
fOverflowDataSize(0) {
- unsigned maxNumPackets = (maxSize + (maxPacketSize-1))/maxPacketSize;
+ if (maxBufferSize == 0) maxBufferSize = maxSize;
+ unsigned maxNumPackets = (maxBufferSize + (maxPacketSize-1))/maxPacketSize;
fLimit = maxNumPackets*maxPacketSize;
fBuf = new unsigned char[fLimit];
resetPacketStart();
diff --git a/liveMedia/MediaSource.cpp b/liveMedia/MediaSource.cpp
index d5b5947..5358ba1 100644
--- a/liveMedia/MediaSource.cpp
+++ b/liveMedia/MediaSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Media Sources
// Implementation
diff --git a/liveMedia/MultiFramedRTPSink.cpp b/liveMedia/MultiFramedRTPSink.cpp
index e58fe24..378eaa1 100644
--- a/liveMedia/MultiFramedRTPSink.cpp
+++ b/liveMedia/MultiFramedRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for a common kind of payload format: Those which pack multiple,
// complete codec frames (as many as possible) into each RTP packet.
// Implementation
@@ -44,7 +44,7 @@ MultiFramedRTPSink::MultiFramedRTPSink(UsageEnvironment& env,
rtpPayloadFormatName, numChannels),
fOutBuf(NULL), fCurFragmentationOffset(0), fPreviousFrameEndedFragmentation(False),
fOnSendErrorFunc(NULL), fOnSendErrorData(NULL) {
- setPacketSizes(1000, 1448);
+ setPacketSizes(1000, 1456);
// Default max packet size (1500, minus allowance for IP, UDP, UMTP headers)
// (Also, make it a multiple of 4 bytes, just in case that matters.)
}
diff --git a/liveMedia/MultiFramedRTPSource.cpp b/liveMedia/MultiFramedRTPSource.cpp
index cda2d71..fa16e69 100644
--- a/liveMedia/MultiFramedRTPSource.cpp
+++ b/liveMedia/MultiFramedRTPSource.cpp
@@ -14,12 +14,13 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP source for a common kind of payload format: Those that pack multiple,
// complete codec frames (as many as possible) into each RTP packet.
// Implementation
#include "MultiFramedRTPSource.hh"
+#include "RTCP.hh"
#include "GroupsockHelper.hh"
#include <string.h>
@@ -104,6 +105,10 @@ Boolean MultiFramedRTPSource
}
void MultiFramedRTPSource::doStopGettingFrames() {
+ if (fPacketReadInProgress != NULL) {
+ fReorderingBuffer->freePacket(fPacketReadInProgress);
+ fPacketReadInProgress = NULL;
+ }
envir().taskScheduler().unscheduleDelayedTask(nextTask());
fRTPInterface.stopNetworkReading();
fReorderingBuffer->reset();
@@ -144,7 +149,7 @@ void MultiFramedRTPSource::doGetNextFrame1() {
// Something's wrong with the header; reject the packet:
fReorderingBuffer->releaseUsedPacket(nextPacket);
fNeedDelivery = True;
- break;
+ continue;
}
nextPacket->skip(specialHeaderSize);
}
@@ -167,7 +172,7 @@ void MultiFramedRTPSource::doGetNextFrame1() {
// This packet is unusable; reject it:
fReorderingBuffer->releaseUsedPacket(nextPacket);
fNeedDelivery = True;
- break;
+ continue;
}
// The packet is usable. Deliver all or part of it to our caller:
@@ -183,7 +188,7 @@ void MultiFramedRTPSource::doGetNextFrame1() {
fReorderingBuffer->releaseUsedPacket(nextPacket);
}
- if (fCurrentPacketCompletesFrame) {
+ if (fCurrentPacketCompletesFrame && fFrameSize > 0) {
// We have all the data that the client wants.
if (fNumTruncatedBytes > 0) {
envir() << "MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size ("
@@ -231,10 +236,11 @@ void MultiFramedRTPSource::networkReadHandler1() {
// Read the network packet, and perform sanity checks on the RTP header:
Boolean readSuccess = False;
do {
+ struct sockaddr_in fromAddress;
Boolean packetReadWasIncomplete = fPacketReadInProgress != NULL;
- if (!bPacket->fillInData(fRTPInterface, packetReadWasIncomplete)) {
- if (bPacket->bytesAvailable() == 0) {
- envir() << "MultiFramedRTPSource error: Hit limit when reading incoming packet over TCP. Increase \"MAX_PACKET_SIZE\"\n";
+ if (!bPacket->fillInData(fRTPInterface, fromAddress, packetReadWasIncomplete)) {
+ if (bPacket->bytesAvailable() == 0) { // should not happen??
+ envir() << "MultiFramedRTPSource internal error: Hit limit when reading incoming packet over TCP\n";
}
fPacketReadInProgress = NULL;
break;
@@ -262,9 +268,22 @@ void MultiFramedRTPSource::networkReadHandler1() {
// Check the RTP version number (it should be 2):
if ((rtpHdr&0xC0000000) != 0x80000000) break;
+ // Check the Payload Type.
+ unsigned char rtpPayloadType = (unsigned char)((rtpHdr&0x007F0000)>>16);
+ if (rtpPayloadType != rtpPayloadFormat()) {
+ if (fRTCPInstanceForMultiplexedRTCPPackets != NULL
+ && rtpPayloadType >= 64 && rtpPayloadType <= 95) {
+ // This is a multiplexed RTCP packet, and we've been asked to deliver such packets.
+ // Do so now:
+ fRTCPInstanceForMultiplexedRTCPPackets
+ ->injectReport(bPacket->data()-12, bPacket->dataSize()+12, fromAddress);
+ }
+ break;
+ }
+
// Skip over any CSRC identifiers in the header:
- unsigned cc = (rtpHdr>>24)&0xF;
- if (bPacket->dataSize() < cc) break;
+ unsigned cc = (rtpHdr>>24)&0x0F;
+ if (bPacket->dataSize() < cc*4) break;
ADVANCE(cc*4);
// Check for (& ignore) any RTP header extension
@@ -284,11 +303,6 @@ void MultiFramedRTPSource::networkReadHandler1() {
if (bPacket->dataSize() < numPaddingBytes) break;
bPacket->removePadding(numPaddingBytes);
}
- // Check the Payload Type.
- if ((unsigned char)((rtpHdr&0x007F0000)>>16)
- != rtpPayloadFormat()) {
- break;
- }
// The rest of the packet is the usable data. Record and save it:
if (rtpSSRC != fLastReceivedSSRC) {
@@ -328,7 +342,7 @@ void MultiFramedRTPSource::networkReadHandler1() {
////////// BufferedPacket and BufferedPacketFactory implementation /////
-#define MAX_PACKET_SIZE 20000
+#define MAX_PACKET_SIZE 65536
BufferedPacket::BufferedPacket()
: fPacketSize(MAX_PACKET_SIZE),
@@ -371,14 +385,20 @@ void BufferedPacket
frameDurationInMicroseconds = 0; // by default. Subclasses should correct this.
}
-Boolean BufferedPacket::fillInData(RTPInterface& rtpInterface, Boolean& packetReadWasIncomplete) {
+Boolean BufferedPacket::fillInData(RTPInterface& rtpInterface, struct sockaddr_in& fromAddress,
+ Boolean& packetReadWasIncomplete) {
if (!packetReadWasIncomplete) reset();
- unsigned numBytesRead;
- struct sockaddr_in fromAddress;
unsigned const maxBytesToRead = bytesAvailable();
if (maxBytesToRead == 0) return False; // exceeded buffer size when reading over TCP
- if (!rtpInterface.handleRead(&fBuf[fTail], maxBytesToRead, numBytesRead, fromAddress, packetReadWasIncomplete)) {
+
+ unsigned numBytesRead;
+ int tcpSocketNum; // not used
+ unsigned char tcpStreamChannelId; // not used
+ if (!rtpInterface.handleRead(&fBuf[fTail], maxBytesToRead,
+ numBytesRead, fromAddress,
+ tcpSocketNum, tcpStreamChannelId,
+ packetReadWasIncomplete)) {
return False;
}
fTail += numBytesRead;
diff --git a/liveMedia/MatroskaDemuxedTrack.cpp b/liveMedia/OggDemuxedTrack.cpp
similarity index 54%
copy from liveMedia/MatroskaDemuxedTrack.cpp
copy to liveMedia/OggDemuxedTrack.cpp
index 193bcf4..03a65e9 100644
--- a/liveMedia/MatroskaDemuxedTrack.cpp
+++ b/liveMedia/OggDemuxedTrack.cpp
@@ -14,33 +14,30 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A media track, demultiplexed from a Matroska file
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A media track, demultiplexed from an Ogg file
// Implementation
-#include "MatroskaDemuxedTrack.hh"
-#include "MatroskaFile.hh"
+#include "OggDemuxedTrack.hh"
+#include "OggFile.hh"
-void MatroskaDemuxedTrack::seekToTime(double& seekNPT) {
- fOurSourceDemux.seekToTime(seekNPT);
-}
-
-MatroskaDemuxedTrack::MatroskaDemuxedTrack(UsageEnvironment& env, unsigned trackNumber, MatroskaDemux& sourceDemux)
+OggDemuxedTrack::OggDemuxedTrack(UsageEnvironment& env, unsigned trackNumber, OggDemux& sourceDemux)
: FramedSource(env),
- fOurTrackNumber(trackNumber), fOurSourceDemux(sourceDemux), fDurationImbalance(0) {
- fPrevPresentationTime.tv_sec = 0; fPrevPresentationTime.tv_usec = 0;
+ fOurTrackNumber(trackNumber), fOurSourceDemux(sourceDemux),
+ fCurrentPageIsContinuation(False) {
+ fNextPresentationTime.tv_sec = 0; fNextPresentationTime.tv_usec = 0;
}
-MatroskaDemuxedTrack::~MatroskaDemuxedTrack() {
+OggDemuxedTrack::~OggDemuxedTrack() {
fOurSourceDemux.removeTrack(fOurTrackNumber);
}
-void MatroskaDemuxedTrack::doGetNextFrame() {
+void OggDemuxedTrack::doGetNextFrame() {
fOurSourceDemux.continueReading();
}
-char const* MatroskaDemuxedTrack::MIMEtype() const {
- MatroskaTrack* track = fOurSourceDemux.fOurFile.lookup(fOurTrackNumber);
- if (track == NULL) return NULL; // shouldn't happen
+char const* OggDemuxedTrack::MIMEtype() const {
+ OggTrack* track = fOurSourceDemux.fOurFile.lookup(fOurTrackNumber);
+ if (track == NULL) return "(unknown)"; // shouldn't happen
return track->mimeType;
}
diff --git a/liveMedia/MatroskaDemuxedTrack.hh b/liveMedia/OggDemuxedTrack.hh
similarity index 57%
copy from liveMedia/MatroskaDemuxedTrack.hh
copy to liveMedia/OggDemuxedTrack.hh
index 90fa3e9..5e5cbff 100644
--- a/liveMedia/MatroskaDemuxedTrack.hh
+++ b/liveMedia/OggDemuxedTrack.hh
@@ -14,50 +14,45 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A media track, demultiplexed from a Matroska file
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A media track, demultiplexed from an Ogg file
// C++ header
-#ifndef _MATROSKA_DEMUXED_TRACK_HH
-#define _MATROSKA_DEMUXED_TRACK_HH
+#ifndef _OGG_DEMUXED_TRACK_HH
+#define _OGG_DEMUXED_TRACK_HH
#ifndef _FRAMED_SOURCE_HH
#include "FramedSource.hh"
#endif
-class MatroskaDemux; // forward
+class OggDemux; // forward
-class MatroskaDemuxedTrack: public FramedSource {
-public:
- void seekToTime(double& seekNPT);
-
-private: // We are created only by a MatroskaDemux (a friend)
- friend class MatroskaDemux;
- MatroskaDemuxedTrack(UsageEnvironment& env, unsigned trackNumber, MatroskaDemux& sourceDemux);
- virtual ~MatroskaDemuxedTrack();
+class OggDemuxedTrack: public FramedSource {
+private: // We are created only by a OggDemux (a friend)
+ friend class OggDemux;
+ OggDemuxedTrack(UsageEnvironment& env, unsigned trackNumber, OggDemux& sourceDemux);
+ virtual ~OggDemuxedTrack();
private:
// redefined virtual functions:
virtual void doGetNextFrame();
virtual char const* MIMEtype() const;
-private: // We are accessed only by MatroskaDemux and by MatroskaFileParser (a friend)
- friend class MatroskaFileParser;
- unsigned char* to() { return fTo; }
- unsigned maxSize() { return fMaxSize; }
+private: // We are accessed only by OggDemux and by OggFileParser (a friend)
+ friend class OggFileParser;
+ unsigned char*& to() { return fTo; }
+ unsigned& maxSize() { return fMaxSize; }
unsigned& frameSize() { return fFrameSize; }
unsigned& numTruncatedBytes() { return fNumTruncatedBytes; }
struct timeval& presentationTime() { return fPresentationTime; }
unsigned& durationInMicroseconds() { return fDurationInMicroseconds; }
-
- struct timeval& prevPresentationTime() { return fPrevPresentationTime; }
- int& durationImbalance() { return fDurationImbalance; }
+ struct timeval& nextPresentationTime() { return fNextPresentationTime; }
private:
unsigned fOurTrackNumber;
- MatroskaDemux& fOurSourceDemux;
- struct timeval fPrevPresentationTime;
- int fDurationImbalance;
+ OggDemux& fOurSourceDemux;
+ Boolean fCurrentPageIsContinuation;
+ struct timeval fNextPresentationTime;
};
#endif
diff --git a/liveMedia/OggFile.cpp b/liveMedia/OggFile.cpp
new file mode 100644
index 0000000..3083470
--- /dev/null
+++ b/liveMedia/OggFile.cpp
@@ -0,0 +1,328 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A class that encapsulates an Ogg file.
+// Implementation
+
+#include "OggFileParser.hh"
+#include "OggDemuxedTrack.hh"
+#include "ByteStreamFileSource.hh"
+#include "VorbisAudioRTPSink.hh"
+#include "SimpleRTPSink.hh"
+#include "TheoraVideoRTPSink.hh"
+
+////////// OggTrackTable definition /////////
+
+// For looking up and iterating over the file's tracks:
+
+class OggTrackTable {
+public:
+ OggTrackTable();
+ virtual ~OggTrackTable();
+
+ void add(OggTrack* newTrack);
+ OggTrack* lookup(u_int32_t trackNumber);
+
+ unsigned numTracks() const;
+
+private:
+ friend class OggTrackTableIterator;
+ HashTable* fTable;
+};
+
+
+////////// OggFile implementation //////////
+
+void OggFile::createNew(UsageEnvironment& env, char const* fileName,
+ onCreationFunc* onCreation, void* onCreationClientData) {
+ new OggFile(env, fileName, onCreation, onCreationClientData);
+}
+
+OggTrack* OggFile::lookup(u_int32_t trackNumber) {
+ return fTrackTable->lookup(trackNumber);
+}
+
+OggDemux* OggFile::newDemux() {
+ OggDemux* demux = new OggDemux(*this);
+ fDemuxesTable->Add((char const*)demux, demux);
+
+ return demux;
+}
+
+unsigned OggFile::numTracks() const {
+ return fTrackTable->numTracks();
+}
+
+FramedSource* OggFile
+::createSourceForStreaming(FramedSource* baseSource, u_int32_t trackNumber,
+ unsigned& estBitrate, unsigned& numFiltersInFrontOfTrack) {
+ if (baseSource == NULL) return NULL;
+
+ FramedSource* result = baseSource; // by default
+ numFiltersInFrontOfTrack = 0; // by default
+
+ // Look at the track's MIME type to set its estimated bitrate (for use by RTCP).
+ // (Later, try to be smarter about figuring out the bitrate.) #####
+ // Some MIME types also require adding a special 'framer' in front of the source.
+ OggTrack* track = lookup(trackNumber);
+ if (track != NULL) { // should always be true
+ estBitrate = track->estBitrate;
+ }
+
+ return result;
+}
+
+RTPSink* OggFile
+::createRTPSinkForTrackNumber(u_int32_t trackNumber, Groupsock* rtpGroupsock,
+ unsigned char rtpPayloadTypeIfDynamic) {
+ OggTrack* track = lookup(trackNumber);
+ if (track == NULL || track->mimeType == NULL) return NULL;
+
+ RTPSink* result = NULL; // default value for unknown media types
+
+ if (strcmp(track->mimeType, "audio/VORBIS") == 0) {
+ // For Vorbis audio, we use the special "identification", "comment", and "setup" headers
+ // that we read when we initially read the headers at the start of the file:
+ result = VorbisAudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ track->samplingFrequency, track->numChannels,
+ track->vtoHdrs.header[0], track->vtoHdrs.headerSize[0],
+ track->vtoHdrs.header[1], track->vtoHdrs.headerSize[1],
+ track->vtoHdrs.header[2], track->vtoHdrs.headerSize[2]);
+ } else if (strcmp(track->mimeType, "audio/OPUS") == 0) {
+ result = SimpleRTPSink
+ ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ 48000, "audio", "OPUS", 2, False/*only 1 Opus 'packet' in each RTP packet*/);
+ } else if (strcmp(track->mimeType, "video/THEORA") == 0) {
+ // For Theora video, we use the special "identification", "comment", and "setup" headers
+ // that we read when we initially read the headers at the start of the file:
+ result = TheoraVideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ track->vtoHdrs.header[0], track->vtoHdrs.headerSize[0],
+ track->vtoHdrs.header[1], track->vtoHdrs.headerSize[1],
+ track->vtoHdrs.header[2], track->vtoHdrs.headerSize[2]);
+ }
+
+ return result;
+}
+
+
+OggFile::OggFile(UsageEnvironment& env, char const* fileName,
+ onCreationFunc* onCreation, void* onCreationClientData)
+ : Medium(env),
+ fFileName(strDup(fileName)),
+ fOnCreation(onCreation), fOnCreationClientData(onCreationClientData) {
+ fTrackTable = new OggTrackTable;
+ fDemuxesTable = HashTable::create(ONE_WORD_HASH_KEYS);
+
+ FramedSource* inputSource = ByteStreamFileSource::createNew(envir(), fileName);
+ if (inputSource == NULL) {
+ // The specified input file does not exist!
+ fParserForInitialization = NULL;
+ handleEndOfBosPageParsing(); // we have no file, and thus no tracks, but we still need to signal this
+ } else {
+ // Initialize ourselves by parsing the file's headers:
+ fParserForInitialization
+ = new OggFileParser(*this, inputSource, handleEndOfBosPageParsing, this);
+ }
+}
+
+OggFile::~OggFile() {
+ delete fParserForInitialization;
+
+ // Delete any outstanding "OggDemux"s, and the table for them:
+ OggDemux* demux;
+ while ((demux = (OggDemux*)fDemuxesTable->RemoveNext()) != NULL) {
+ delete demux;
+ }
+ delete fDemuxesTable;
+ delete fTrackTable;
+
+ delete[] (char*)fFileName;
+}
+
+void OggFile::handleEndOfBosPageParsing(void* clientData) {
+ ((OggFile*)clientData)->handleEndOfBosPageParsing();
+}
+
+void OggFile::handleEndOfBosPageParsing() {
+ // Delete our parser, because it's done its job now:
+ delete fParserForInitialization; fParserForInitialization = NULL;
+
+ // Finally, signal our caller that we've been created and initialized:
+ if (fOnCreation != NULL) (*fOnCreation)(this, fOnCreationClientData);
+}
+
+void OggFile::addTrack(OggTrack* newTrack) {
+ fTrackTable->add(newTrack);
+}
+
+void OggFile::removeDemux(OggDemux* demux) {
+ fDemuxesTable->Remove((char const*)demux);
+}
+
+
+////////// OggTrackTable implementation /////////
+
+OggTrackTable::OggTrackTable()
+ : fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
+}
+
+OggTrackTable::~OggTrackTable() {
+ // Remove and delete all of our "OggTrack" descriptors, and the hash table itself:
+ OggTrack* track;
+ while ((track = (OggTrack*)fTable->RemoveNext()) != NULL) {
+ delete track;
+ }
+ delete fTable;
+}
+
+void OggTrackTable::add(OggTrack* newTrack) {
+ OggTrack* existingTrack
+ = (OggTrack*)fTable->Add((char const*)newTrack->trackNumber, newTrack);
+ delete existingTrack; // if any
+}
+
+OggTrack* OggTrackTable::lookup(u_int32_t trackNumber) {
+ return (OggTrack*)fTable->Lookup((char const*)trackNumber);
+}
+
+unsigned OggTrackTable::numTracks() const { return fTable->numEntries(); }
+
+OggTrackTableIterator::OggTrackTableIterator(OggTrackTable& ourTable) {
+ fIter = HashTable::Iterator::create(*(ourTable.fTable));
+}
+
+OggTrackTableIterator::~OggTrackTableIterator() {
+ delete fIter;
+}
+
+OggTrack* OggTrackTableIterator::next() {
+ char const* key;
+ return (OggTrack*)fIter->next(key);
+}
+
+
+////////// OggTrack implementation //////////
+
+OggTrack::OggTrack()
+ : trackNumber(0), mimeType(NULL),
+ samplingFrequency(48000), numChannels(2), estBitrate(100) { // default settings
+ vtoHdrs.header[0] = vtoHdrs.header[1] = vtoHdrs.header[2] = NULL;
+ vtoHdrs.headerSize[0] = vtoHdrs.headerSize[1] = vtoHdrs.headerSize[2] = 0;
+
+ vtoHdrs.vorbis_mode_count = 0;
+ vtoHdrs.vorbis_mode_blockflag = NULL;
+}
+
+OggTrack::~OggTrack() {
+ delete[] vtoHdrs.header[0]; delete[] vtoHdrs.header[1]; delete[] vtoHdrs.header[2];
+ delete[] vtoHdrs.vorbis_mode_blockflag;
+}
+
+
+///////// OggDemux implementation /////////
+
+FramedSource* OggDemux::newDemuxedTrack(u_int32_t& resultTrackNumber) {
+ OggTrack* nextTrack;
+ do {
+ nextTrack = fIter->next();
+ } while (nextTrack != NULL && nextTrack->mimeType == NULL);
+
+ if (nextTrack == NULL) { // no more tracks
+ resultTrackNumber = 0;
+ return NULL;
+ }
+
+ resultTrackNumber = nextTrack->trackNumber;
+ FramedSource* trackSource = new OggDemuxedTrack(envir(), resultTrackNumber, *this);
+ fDemuxedTracksTable->Add((char const*)resultTrackNumber, trackSource);
+ return trackSource;
+}
+
+FramedSource* OggDemux::newDemuxedTrackByTrackNumber(unsigned trackNumber) {
+ if (trackNumber == 0) return NULL;
+
+ FramedSource* trackSource = new OggDemuxedTrack(envir(), trackNumber, *this);
+ fDemuxedTracksTable->Add((char const*)trackNumber, trackSource);
+ return trackSource;
+}
+
+OggDemuxedTrack* OggDemux::lookupDemuxedTrack(u_int32_t trackNumber) {
+ return (OggDemuxedTrack*)fDemuxedTracksTable->Lookup((char const*)trackNumber);
+}
+
+OggDemux::OggDemux(OggFile& ourFile)
+ : Medium(ourFile.envir()),
+ fOurFile(ourFile), fDemuxedTracksTable(HashTable::create(ONE_WORD_HASH_KEYS)),
+ fIter(new OggTrackTableIterator(*fOurFile.fTrackTable)) {
+ FramedSource* fileSource = ByteStreamFileSource::createNew(envir(), ourFile.fileName());
+ fOurParser = new OggFileParser(ourFile, fileSource, handleEndOfFile, this, this);
+}
+
+OggDemux::~OggDemux() {
+ // Begin by acting as if we've reached the end of the source file.
+ // This should cause all of our demuxed tracks to get closed.
+ handleEndOfFile();
+
+ // Then delete our table of "OggDemuxedTrack"s
+ // - but not the "OggDemuxedTrack"s themselves; that should have already happened:
+ delete fDemuxedTracksTable;
+
+ delete fIter;
+ delete fOurParser;
+ fOurFile.removeDemux(this);
+}
+
+void OggDemux::removeTrack(u_int32_t trackNumber) {
+ fDemuxedTracksTable->Remove((char const*)trackNumber);
+ if (fDemuxedTracksTable->numEntries() == 0) {
+ // We no longer have any demuxed tracks, so delete ourselves now:
+ delete this;
+ }
+}
+
+void OggDemux::continueReading() {
+ fOurParser->continueParsing();
+}
+
+void OggDemux::handleEndOfFile(void* clientData) {
+ ((OggDemux*)clientData)->handleEndOfFile();
+}
+
+void OggDemux::handleEndOfFile() {
+ // Iterate through all of our 'demuxed tracks', handling 'end of input' on each one.
+ // Hack: Because this can cause the hash table to get modified underneath us,
+ // we don't call the handlers until after we've first iterated through all of the tracks.
+ unsigned numTracks = fDemuxedTracksTable->numEntries();
+ if (numTracks == 0) return;
+ OggDemuxedTrack** tracks = new OggDemuxedTrack*[numTracks];
+
+ HashTable::Iterator* iter = HashTable::Iterator::create(*fDemuxedTracksTable);
+ unsigned i;
+ char const* trackNumber;
+
+ for (i = 0; i < numTracks; ++i) {
+ tracks[i] = (OggDemuxedTrack*)iter->next(trackNumber);
+ }
+ delete iter;
+
+ for (i = 0; i < numTracks; ++i) {
+ if (tracks[i] == NULL) continue; // sanity check; shouldn't happen
+ tracks[i]->handleClosure();
+ }
+
+ delete[] tracks;
+}
diff --git a/liveMedia/OggFileParser.cpp b/liveMedia/OggFileParser.cpp
new file mode 100644
index 0000000..473bc17
--- /dev/null
+++ b/liveMedia/OggFileParser.cpp
@@ -0,0 +1,1032 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A parser for an Ogg file.
+// Implementation
+
+#include "OggFileParser.hh"
+#include "OggDemuxedTrack.hh"
+#include <GroupsockHelper.hh> // for "gettimeofday()
+
+PacketSizeTable::PacketSizeTable(unsigned number_page_segments)
+ : numCompletedPackets(0), totSizes(0), nextPacketNumToDeliver(0),
+ lastPacketIsIncomplete(False) {
+ size = new unsigned[number_page_segments];
+ for (unsigned i = 0; i < number_page_segments; ++i) size[i] = 0;
+}
+
+PacketSizeTable::~PacketSizeTable() {
+ delete[] size;
+}
+
+OggFileParser::OggFileParser(OggFile& ourFile, FramedSource* inputSource,
+ FramedSource::onCloseFunc* onEndFunc, void* onEndClientData,
+ OggDemux* ourDemux)
+ : StreamParser(inputSource, onEndFunc, onEndClientData, continueParsing, this),
+ fOurFile(ourFile), fInputSource(inputSource),
+ fOnEndFunc(onEndFunc), fOnEndClientData(onEndClientData),
+ fOurDemux(ourDemux), fNumUnfulfilledTracks(0),
+ fPacketSizeTable(NULL), fCurrentTrackNumber(0), fSavedPacket(NULL) {
+ if (ourDemux == NULL) {
+ // Initialization
+ fCurrentParseState = PARSING_START_OF_FILE;
+ continueParsing();
+ } else {
+ fCurrentParseState = PARSING_AND_DELIVERING_PAGES;
+ // In this case, parsing (of page data) doesn't start until a client starts reading from a track.
+ }
+}
+
+OggFileParser::~OggFileParser() {
+ delete[] fSavedPacket;
+ delete fPacketSizeTable;
+ Medium::close(fInputSource);
+}
+
+void OggFileParser::continueParsing(void* clientData, unsigned char* ptr, unsigned size, struct timeval presentationTime) {
+ ((OggFileParser*)clientData)->continueParsing();
+}
+
+void OggFileParser::continueParsing() {
+ if (fInputSource != NULL) {
+ if (fInputSource->isCurrentlyAwaitingData()) return;
+ // Our input source is currently being read. Wait until that read completes
+
+ if (!parse()) {
+ // We didn't complete the parsing, because we had to read more data from the source,
+ // or because we're waiting for another read from downstream.
+ // Once that happens, we'll get called again.
+ return;
+ }
+ }
+
+ // We successfully parsed the file. Call our 'done' function now:
+ if (fOnEndFunc != NULL) (*fOnEndFunc)(fOnEndClientData);
+}
+
+Boolean OggFileParser::parse() {
+ try {
+ while (1) {
+ switch (fCurrentParseState) {
+ case PARSING_START_OF_FILE: {
+ if (parseStartOfFile()) return True;
+ }
+ case PARSING_AND_DELIVERING_PAGES: {
+ parseAndDeliverPages();
+ }
+ case DELIVERING_PACKET_WITHIN_PAGE: {
+ if (deliverPacketWithinPage()) return False;
+ }
+ }
+ }
+ } catch (int /*e*/) {
+#ifdef DEBUG
+ fprintf(stderr, "OggFileParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");
+#endif
+ return False; // the parsing got interrupted
+ }
+}
+
+Boolean OggFileParser::parseStartOfFile() {
+#ifdef DEBUG
+ fprintf(stderr, "parsing start of file\n");
+#endif
+ // Read and parse each 'page', until we see the first non-BOS page, or until we have
+ // collected all required headers for Vorbis, Theora, or Opus track(s) (if any).
+ u_int8_t header_type_flag;
+ do {
+ header_type_flag = parseInitialPage();
+ } while ((header_type_flag&0x02) != 0 || needHeaders());
+
+#ifdef DEBUG
+ fprintf(stderr, "Finished parsing start of file\n");
+#endif
+ return True;
+}
+
+static u_int32_t byteSwap(u_int32_t x) {
+ return (x<<24)|((x<<8)&0x00FF0000)|((x>>8)&0x0000FF00)|(x>>24);
+}
+
+u_int8_t OggFileParser::parseInitialPage() {
+ u_int8_t header_type_flag;
+ u_int32_t bitstream_serial_number;
+ parseStartOfPage(header_type_flag, bitstream_serial_number);
+
+ // If this is a BOS page, examine the first 8 bytes of the first 'packet', to see whether
+ // the track data type is one that we know how to stream:
+ OggTrack* track;
+ if ((header_type_flag&0x02) != 0) { // BOS
+ char const* mimeType = NULL; // if unknown
+ if (fPacketSizeTable != NULL && fPacketSizeTable->size[0] >= 8) { // sanity check
+ char buf[8];
+ testBytes((u_int8_t*)buf, 8);
+
+ if (strncmp(&buf[1], "vorbis", 6) == 0) {
+ mimeType = "audio/VORBIS";
+ ++fNumUnfulfilledTracks;
+ } else if (strncmp(buf, "OpusHead", 8) == 0) {
+ mimeType = "audio/OPUS";
+ ++fNumUnfulfilledTracks;
+ } else if (strncmp(&buf[1], "theora", 6) == 0) {
+ mimeType = "video/THEORA";
+ ++fNumUnfulfilledTracks;
+ }
+ }
+
+ // Add a new track descriptor for this track:
+ track = new OggTrack;
+ track->trackNumber = bitstream_serial_number;
+ track->mimeType = mimeType;
+ fOurFile.addTrack(track);
+ } else { // not a BOS page
+ // Because this is not a BOS page, the specified track should already have been seen:
+ track = fOurFile.lookup(bitstream_serial_number);
+ }
+
+ if (track != NULL) { // sanity check
+#ifdef DEBUG
+ fprintf(stderr, "This track's MIME type: %s\n",
+ track->mimeType == NULL ? "(unknown)" : track->mimeType);
+#endif
+ if (track->mimeType != NULL &&
+ (strcmp(track->mimeType, "audio/VORBIS") == 0 ||
+ strcmp(track->mimeType, "video/THEORA") == 0 ||
+ strcmp(track->mimeType, "audio/OPUS") == 0)) {
+ // Special-case handling of Vorbis, Theora, or Opus tracks:
+ // Make a copy of each packet, until we get the three special headers that we need:
+ Boolean isVorbis = strcmp(track->mimeType, "audio/VORBIS") == 0;
+ Boolean isTheora = strcmp(track->mimeType, "video/THEORA") == 0;
+
+ for (unsigned j = 0; j < fPacketSizeTable->numCompletedPackets && track->weNeedHeaders(); ++j) {
+ unsigned const packetSize = fPacketSizeTable->size[j];
+ if (packetSize == 0) continue; // sanity check
+
+ delete[] fSavedPacket/*if any*/; fSavedPacket = new u_int8_t[packetSize];
+ getBytes(fSavedPacket, packetSize);
+ fPacketSizeTable->totSizes -= packetSize;
+
+ // The start of the packet tells us whether its a header that we know about:
+ Boolean headerIsKnown = False;
+ unsigned index = 0;
+ if (isVorbis) {
+ u_int8_t const firstByte = fSavedPacket[0];
+
+ headerIsKnown = firstByte == 1 || firstByte == 3 || firstByte == 5;
+ index = (firstByte-1)/2; // 1, 3, or 5 => 0, 1, or 2
+ } else if (isTheora) {
+ u_int8_t const firstByte = fSavedPacket[0];
+
+ headerIsKnown = firstByte == 0x80 || firstByte == 0x81 || firstByte == 0x82;
+ index = firstByte &~0x80; // 0x80, 0x81, or 0x82 => 0, 1, or 2
+ } else { // Opus
+ if (strncmp((char const*)fSavedPacket, "OpusHead", 8) == 0) {
+ headerIsKnown = True;
+ index = 0; // "identification" header
+ } else if (strncmp((char const*)fSavedPacket, "OpusTags", 8) == 0) {
+ headerIsKnown = True;
+ index = 1; // "comment" header
+ }
+ }
+ if (headerIsKnown) {
+#ifdef DEBUG
+ char const* headerName[3] = { "identification", "comment", "setup" };
+ fprintf(stderr, "Saved %d-byte %s \"%s\" header\n", packetSize, track->mimeType,
+ headerName[index]);
+#endif
+ // This is a header, but first check it for validity:
+ if (!validateHeader(track, fSavedPacket, packetSize)) continue;
+
+ // Save this header (deleting any old header of the same type that we'd saved before)
+ delete[] track->vtoHdrs.header[index];
+ track->vtoHdrs.header[index] = fSavedPacket;
+ fSavedPacket = NULL;
+ track->vtoHdrs.headerSize[index] = packetSize;
+
+ if (!track->weNeedHeaders()) {
+ // We now have all of the needed Vorbis, Theora, or Opus headers for this track:
+ --fNumUnfulfilledTracks;
+ }
+ // Note: The above code won't work if a required header is fragmented over
+ // more than one 'page'. We assume that that won't ever happen...
+ }
+ }
+ }
+ }
+
+ // Skip over any remaining packet data bytes:
+ if (fPacketSizeTable->totSizes > 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Skipping %d remaining packet data bytes\n", fPacketSizeTable->totSizes);
+#endif
+ skipBytes(fPacketSizeTable->totSizes);
+ }
+
+ return header_type_flag;
+}
+
+// A simple bit vector class for reading bits in little-endian order.
+// (We can't use our usual "BitVector" class, because that's big-endian.)
+class LEBitVector {
+public:
+ LEBitVector(u_int8_t const* p, unsigned numBytes)
+ : fPtr(p), fEnd(&p[numBytes]), fNumBitsRemainingInCurrentByte(8) {
+ }
+
+ u_int32_t getBits(unsigned numBits/*<=32*/) {
+ if (noMoreBits()) {
+ return 0;
+ } else if (numBits == fNumBitsRemainingInCurrentByte) {
+ u_int32_t result = (*fPtr++)>>(8-fNumBitsRemainingInCurrentByte);
+ fNumBitsRemainingInCurrentByte = 8;
+
+ return result;
+ } else if (numBits < fNumBitsRemainingInCurrentByte) {
+ u_int8_t mask = 0xFF>>(8-numBits);
+ u_int32_t result = ((*fPtr)>>(8-fNumBitsRemainingInCurrentByte)) & mask;
+ fNumBitsRemainingInCurrentByte -= numBits;
+
+ return result;
+ } else { // numBits > fNumBitsRemainingInCurrentByte
+ // Do two recursive calls to get the result:
+ unsigned nbr = fNumBitsRemainingInCurrentByte;
+ u_int32_t firstBits = getBits(nbr);
+ u_int32_t nextBits = getBits(numBits - nbr);
+
+ return (nextBits<<nbr) | firstBits;
+ }
+ }
+
+ void skipBits(unsigned numBits) {
+ while (numBits > 32) {
+ (void)getBits(32);
+ numBits -= 32;
+ }
+ (void)getBits(numBits);
+ }
+
+ unsigned numBitsRemaining() { return (fEnd-fPtr-1)*8 + fNumBitsRemainingInCurrentByte; }
+ Boolean noMoreBits() const { return fPtr >= fEnd; }
+
+private:
+ u_int8_t const* fPtr;
+ u_int8_t const* fEnd;
+ unsigned fNumBitsRemainingInCurrentByte; // 1..8
+};
+
+static unsigned ilog(int n) {
+ if (n < 0) return 0;
+
+ unsigned x = (unsigned)n;
+ unsigned result = 0;
+
+ while (x > 0) {
+ ++result;
+ x >>= 1;
+ }
+
+ return result;
+}
+
+static unsigned lookup1_values(unsigned codebook_entries, unsigned codebook_dimensions) {
+ // "the greatest integer value for which [return_value] to the power of [codebook_dimensions]
+ // is less than or equal to [codebook_entries]"
+ unsigned return_value = 0;
+ unsigned powerValue;
+
+ do {
+ ++return_value;
+ // Compute powerValue = return_value ** codebook_dimensions
+ if (return_value == 1) powerValue = 1; // optimization
+ else {
+ powerValue = 1;
+ for (unsigned i = 0; i < codebook_dimensions; ++i) {
+ powerValue *= return_value;
+ }
+ }
+ } while (powerValue <= codebook_entries);
+ return_value -= 1;
+
+ return return_value;
+}
+
+static Boolean parseVorbisSetup_codebook(LEBitVector& bv) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned sync = bv.getBits(24);
+ if (sync != 0x564342) return False;
+ unsigned codebook_dimensions = bv.getBits(16);
+ unsigned codebook_entries = bv.getBits(24);
+ unsigned ordered = bv.getBits(1);
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\t\tcodebook_dimensions: %d; codebook_entries: %d, ordered: %d\n",
+ codebook_dimensions, codebook_entries, ordered);
+#endif
+ if (!ordered) {
+ unsigned sparse = bv.getBits(1);
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\t\t!ordered: sparse %d\n", sparse);
+#endif
+ for (unsigned i = 0; i < codebook_entries; ++i) {
+ unsigned codewordLength;
+
+ if (sparse) {
+ unsigned flag = bv.getBits(1);
+ if (flag) {
+ codewordLength = bv.getBits(5) + 1;
+ } else {
+ codewordLength = 0;
+ }
+ } else {
+ codewordLength = bv.getBits(5) + 1;
+ }
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\t\t\tcodeword length[%d]:\t%d\n", i, codewordLength);
+#else
+ codewordLength = codewordLength; // to prevent compiler warning
+#endif
+ }
+ } else { // ordered
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\t\tordered:\n");
+#endif
+ unsigned current_entry = 0;
+ unsigned current_length = bv.getBits(5) + 1;
+ do {
+ unsigned number = bv.getBits(ilog(codebook_entries - current_entry));
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\t\t\tcodeword length[%d..%d]:\t%d\n",
+ current_entry, current_entry + number - 1, current_length);
+#endif
+ current_entry += number;
+ if (current_entry > codebook_entries) {
+ fprintf(stderr, "Vorbis codebook parsing error: current_entry %d > codebook_entries %d!\n", current_entry, codebook_entries);
+ return False;
+ }
+ ++current_length;
+ } while (current_entry < codebook_entries);
+ }
+
+ unsigned codebook_lookup_type = bv.getBits(4);
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\t\tcodebook_lookup_type: %d\n", codebook_lookup_type);
+#endif
+ if (codebook_lookup_type > 2) {
+ fprintf(stderr, "Vorbis codebook parsing error: codebook_lookup_type %d!\n", codebook_lookup_type);
+ return False;
+ } else if (codebook_lookup_type > 0) { // 1 or 2
+ bv.skipBits(32+32); // "codebook_minimum_value" and "codebook_delta_value"
+ unsigned codebook_value_bits = bv.getBits(4) + 1;
+ bv.skipBits(1); // "codebook_lookup_p"
+ unsigned codebook_lookup_values;
+ if (codebook_lookup_type == 1) {
+ codebook_lookup_values = lookup1_values(codebook_entries, codebook_dimensions);
+ } else { // 2
+ codebook_lookup_values = codebook_entries*codebook_dimensions;
+ }
+
+ bv.skipBits(codebook_lookup_values*codebook_value_bits); // "codebook_multiplicands"
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetup_codebooks(LEBitVector& bv) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned vorbis_codebook_count = bv.getBits(8) + 1;
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\tCodebooks: vorbis_codebook_count: %d\n", vorbis_codebook_count);
+#endif
+ for (unsigned i = 0; i < vorbis_codebook_count; ++i) {
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\tCodebook %d:\n", i);
+#endif
+ if (!parseVorbisSetup_codebook(bv)) return False;
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetup_timeDomainTransforms(LEBitVector& bv) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned vorbis_time_count = bv.getBits(6) + 1;
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\tTime domain transforms: vorbis_time_count: %d\n", vorbis_time_count);
+#endif
+ for (unsigned i = 0; i < vorbis_time_count; ++i) {
+ unsigned val = bv.getBits(16);
+ if (val != 0) {
+ fprintf(stderr, "Vorbis Time domain transforms, read non-zero value %d\n", val);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetup_floors(LEBitVector& bv) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned vorbis_floor_count = bv.getBits(6) + 1;
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\tFloors: vorbis_floor_count: %d\n", vorbis_floor_count);
+#endif
+ for (unsigned i = 0; i < vorbis_floor_count; ++i) {
+ unsigned floorType = bv.getBits(16);
+ if (floorType == 0) {
+ bv.skipBits(8+16+16+6+8);
+ unsigned floor0_number_of_books = bv.getBits(4) + 1;
+ bv.skipBits(floor0_number_of_books*8);
+ } else if (floorType == 1) {
+ unsigned floor1_partitions = bv.getBits(5);
+
+ unsigned* floor1_partition_class_list = new unsigned[floor1_partitions];
+ unsigned maximum_class = 0, j;
+ for (j = 0; j < floor1_partitions; ++j) {
+ floor1_partition_class_list[j] = bv.getBits(4);
+ if (floor1_partition_class_list[j] > maximum_class) maximum_class = floor1_partition_class_list[j];
+ }
+
+ unsigned* floor1_class_dimensions = new unsigned[maximum_class + 1];
+ for (j = 0; j <= maximum_class; ++j) {
+ floor1_class_dimensions[j] = bv.getBits(3) + 1;
+ unsigned floor1_class_subclasses = bv.getBits(2);
+ if (floor1_class_subclasses != 0) {
+ bv.skipBits(8); // "floor1_class_masterbooks[j]"
+ }
+
+ unsigned twoExp_floor1_class_subclasses = 1 << floor1_class_subclasses;
+ bv.skipBits(twoExp_floor1_class_subclasses*8); // "floor1_subclass_books[j][*]"
+ }
+
+ bv.skipBits(2); // "floor1_multiplier"
+ unsigned rangebits = bv.getBits(4);
+ for (j = 0; j < floor1_partitions; ++j) {
+ unsigned current_class_number = floor1_partition_class_list[j];
+ bv.skipBits(floor1_class_dimensions[current_class_number] * rangebits);
+ }
+
+ delete[] floor1_partition_class_list;
+ delete[] floor1_class_dimensions;
+ } else { // floorType > 1
+ fprintf(stderr, "Vorbis Floors, read bad floor type %d\n", floorType);
+ return False;
+ }
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetup_residues(LEBitVector& bv) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned vorbis_residue_count = bv.getBits(6) + 1;
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\tResidues: vorbis_residue_count: %d\n", vorbis_residue_count);
+#endif
+ for (unsigned i = 0; i < vorbis_residue_count; ++i) {
+ unsigned vorbis_residue_type = bv.getBits(16);
+ if (vorbis_residue_type > 2) {
+ fprintf(stderr, "Vorbis Residues, read bad vorbis_residue_type: %d\n", vorbis_residue_type);
+ return False;
+ } else {
+ bv.skipBits(24+24+24); // "residue_begin", "residue_end", "residue_partition_size"
+ unsigned residue_classifications = bv.getBits(6) + 1;
+ bv.skipBits(8); // "residue_classbook"
+
+ u_int8_t* residue_cascade = new u_int8_t[residue_classifications];
+ unsigned j;
+ for (j = 0; j < residue_classifications; ++j) {
+ u_int8_t high_bits = 0;
+ u_int8_t low_bits = bv.getBits(3);
+ unsigned bitflag = bv.getBits(1);
+ if (bitflag) {
+ high_bits = bv.getBits(5);
+ }
+
+ residue_cascade[j] = (high_bits<<3) | low_bits;
+ }
+
+ for (j = 0; j < residue_classifications; ++j) {
+ u_int8_t const cascade = residue_cascade[j];
+ u_int8_t mask = 0x80;
+ while (mask != 0) {
+ if ((cascade&mask) != 0) bv.skipBits(8); // "residue_books[j][*]"
+ mask >>= 1;
+ }
+ }
+
+ delete[] residue_cascade;
+ }
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetup_mappings(LEBitVector& bv, unsigned audio_channels) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned vorbis_mapping_count = bv.getBits(6) + 1;
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\tMappings: vorbis_mapping_count: %d\n", vorbis_mapping_count);
+#endif
+ for (unsigned i = 0; i < vorbis_mapping_count; ++i) {
+ unsigned vorbis_mapping_type = bv.getBits(16);
+ if (vorbis_mapping_type != 0) {
+ fprintf(stderr, "Vorbis Mappings, read bad vorbis_mapping_type: %d\n", vorbis_mapping_type);
+ return False;
+ }
+
+ unsigned vorbis_mapping_submaps = 1;
+ if (bv.getBits(1)) vorbis_mapping_submaps = bv.getBits(4) + 1;
+
+ if (bv.getBits(1)) { // "square polar channel mapping is in use"
+ unsigned vorbis_mapping_coupling_steps = bv.getBits(8) + 1;
+
+ for (unsigned j = 0; j < vorbis_mapping_coupling_steps; ++j) {
+ unsigned ilog_audio_channels_minus_1 = ilog(audio_channels - 1);
+ bv.skipBits(2*ilog_audio_channels_minus_1); // "vorbis_mapping_magnitude", "vorbis_mapping_angle"
+ }
+ }
+
+ unsigned reserved = bv.getBits(2);
+ if (reserved != 0) {
+ fprintf(stderr, "Vorbis Mappings, read bad 'reserved' field\n");
+ return False;
+ }
+
+ if (vorbis_mapping_submaps > 1) {
+ for (unsigned j = 0; j < audio_channels; ++j) {
+ unsigned vorbis_mapping_mux = bv.getBits(4);
+
+ fprintf(stderr, "\t\t\t\tvorbis_mapping_mux[%d]: %d\n", j, vorbis_mapping_mux);
+ if (vorbis_mapping_mux >= vorbis_mapping_submaps) {
+ fprintf(stderr, "Vorbis Mappings, read bad \"vorbis_mapping_mux\" %d (>= \"vorbis_mapping_submaps\" %d)\n", vorbis_mapping_mux, vorbis_mapping_submaps);
+ return False;
+ }
+ }
+ }
+
+ bv.skipBits(vorbis_mapping_submaps*(8+8+8)); // "the floor and residue numbers"
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetup_modes(LEBitVector& bv, OggTrack* track) {
+ if (bv.noMoreBits()) return False;
+
+ unsigned vorbis_mode_count = bv.getBits(6) + 1;
+ unsigned ilog_vorbis_mode_count_minus_1 = ilog(vorbis_mode_count - 1);
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\tModes: vorbis_mode_count: %d (ilog(%d-1):%d)\n",
+ vorbis_mode_count, vorbis_mode_count, ilog_vorbis_mode_count_minus_1);
+#endif
+ track->vtoHdrs.vorbis_mode_count = vorbis_mode_count;
+ track->vtoHdrs.ilog_vorbis_mode_count_minus_1 = ilog_vorbis_mode_count_minus_1;
+ track->vtoHdrs.vorbis_mode_blockflag = new u_int8_t[vorbis_mode_count];
+
+ for (unsigned i = 0; i < vorbis_mode_count; ++i) {
+ track->vtoHdrs.vorbis_mode_blockflag[i] = (u_int8_t)bv.getBits(1);
+#ifdef DEBUG_SETUP_HEADER
+ fprintf(stderr, "\t\tMode %d: vorbis_mode_blockflag: %d\n", i, track->vtoHdrs.vorbis_mode_blockflag[i]);
+#endif
+ bv.skipBits(16+16+8); // "vorbis_mode_windowtype", "vorbis_mode_transformtype", "vorbis_mode_mapping"
+ }
+
+ return True;
+}
+
+static Boolean parseVorbisSetupHeader(OggTrack* track, u_int8_t const* p, unsigned headerSize) {
+ LEBitVector bv(p, headerSize);
+ do {
+ if (!parseVorbisSetup_codebooks(bv)) break;
+ if (!parseVorbisSetup_timeDomainTransforms(bv)) break;
+ if (!parseVorbisSetup_floors(bv)) break;
+ if (!parseVorbisSetup_residues(bv)) break;
+ if (!parseVorbisSetup_mappings(bv, track->numChannels)) break;
+ if (!parseVorbisSetup_modes(bv, track)) break;
+ unsigned framingFlag = bv.getBits(1);
+ if (framingFlag == 0) {
+ fprintf(stderr, "Vorbis \"setup\" header did not end with a 'framing flag'!\n");
+ break;
+ }
+
+ return True;
+ } while (0);
+
+ // An error occurred:
+ return False;
+}
+
+#ifdef DEBUG
+#define CHECK_PTR if (p >= pEnd) return False
+#define printComment(p, len) do { for (unsigned k = 0; k < len; ++k) { CHECK_PTR; fprintf(stderr, "%c", *p++); } } while (0)
+#endif
+
+static Boolean validateCommentHeader(u_int8_t const *p, unsigned headerSize,
+ unsigned isOpus = 0) {
+ if (headerSize < 15+isOpus) { // need 7+isOpus + 4(vendor_length) + 4(user_comment_list_length)
+ fprintf(stderr, "\"comment\" header is too short (%d bytes)\n", headerSize);
+ return False;
+ }
+
+#ifdef DEBUG
+ u_int8_t const* pEnd = &p[headerSize];
+ p += 7+isOpus;
+
+ u_int32_t vendor_length = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4;
+ fprintf(stderr, "\tvendor_string:");
+ printComment(p, vendor_length);
+ fprintf(stderr, "\n");
+
+ u_int32_t user_comment_list_length = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4;
+ for (unsigned i = 0; i < user_comment_list_length; ++i) {
+ CHECK_PTR; u_int32_t length = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4;
+ fprintf(stderr, "\tuser_comment[%d]:", i);
+ printComment(p, length);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ return True;
+}
+
+static unsigned blocksizeFromExponent(unsigned exponent) {
+ unsigned result = 1;
+ for (unsigned i = 0; i < exponent; ++i) result = 2*result;
+ return result;
+}
+
+Boolean OggFileParser::validateHeader(OggTrack* track, u_int8_t const* p, unsigned headerSize) {
+ // Assert: headerSize >= 7 (because we've already checked "<packet_type>XXXXXX" or "OpusXXXX")
+ if (strcmp(track->mimeType, "audio/VORBIS") == 0) {
+ u_int8_t const firstByte = p[0];
+
+ if (firstByte == 1) { // "identification" header
+ if (headerSize < 30) {
+ fprintf(stderr, "Vorbis \"identification\" header is too short (%d bytes)\n", headerSize);
+ return False;
+ } else if ((p[29]&0x1) != 1) {
+ fprintf(stderr, "Vorbis \"identification\" header: 'framing_flag' is not set\n");
+ return False;
+ }
+
+ p += 7;
+ u_int32_t vorbis_version = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4;
+ if (vorbis_version != 0) {
+ fprintf(stderr, "Vorbis \"identification\" header has a bad 'vorbis_version': 0x%08x\n", vorbis_version);
+ return False;
+ }
+
+ u_int8_t audio_channels = *p++;
+ if (audio_channels == 0) {
+ fprintf(stderr, "Vorbis \"identification\" header: 'audio_channels' is 0!\n");
+ return False;
+ }
+ track->numChannels = audio_channels;
+
+ u_int32_t audio_sample_rate = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4;
+ if (audio_sample_rate == 0) {
+ fprintf(stderr, "Vorbis \"identification\" header: 'audio_sample_rate' is 0!\n");
+ return False;
+ }
+ track->samplingFrequency = audio_sample_rate;
+
+ p += 4; // skip over 'bitrate_maximum'
+ u_int32_t bitrate_nominal = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4;
+ if (bitrate_nominal > 0) track->estBitrate = (bitrate_nominal+500)/1000; // round
+
+ p += 4; // skip over 'bitrate_maximum'
+
+ // Note the two 'block sizes' (samples per packet), and their durations in microseconds:
+ u_int8_t blocksizeBits = *p++;
+ unsigned& blocksize_0 = track->vtoHdrs.blocksize[0]; // alias
+ unsigned& blocksize_1 = track->vtoHdrs.blocksize[1]; // alias
+ blocksize_0 = blocksizeFromExponent(blocksizeBits&0x0F);
+ blocksize_1 = blocksizeFromExponent(blocksizeBits>>4);
+
+ double uSecsPerSample = 1000000.0/(track->samplingFrequency*2);
+ // Why the "2"? I don't know, but it seems to be necessary
+ track->vtoHdrs.uSecsPerPacket[0] = (unsigned)(uSecsPerSample*blocksize_0);
+ track->vtoHdrs.uSecsPerPacket[1] = (unsigned)(uSecsPerSample*blocksize_1);
+#ifdef DEBUG
+ fprintf(stderr, "\t%u Hz, %u-channel, %u kbps (est), block sizes: %u,%u (%u,%u us)\n",
+ track->samplingFrequency, track->numChannels, track->estBitrate,
+ blocksize_0, blocksize_1,
+ track->vtoHdrs.uSecsPerPacket[0], track->vtoHdrs.uSecsPerPacket[1]);
+#endif
+ // To be valid, "blocksize_0" must be <= "blocksize_1", and both must be in [64,8192]:
+ if (!(blocksize_0 <= blocksize_1 && blocksize_0 >= 64 && blocksize_1 <= 8192)) {
+ fprintf(stderr, "Invalid Vorbis \"blocksize_0\" (%d) and/or \"blocksize_1\" (%d)!\n",
+ blocksize_0, blocksize_1);
+ return False;
+ }
+ } else if (firstByte == 3) { // "comment" header
+ if (!validateCommentHeader(p, headerSize)) return False;
+ } else if (firstByte == 5) { // "setup" header
+ // Parse the "setup" header to get the values that we want:
+ // "vorbis_mode_count", and "vorbis_mode_blockflag" for each mode. Unfortunately these come
+ // near the end of the header, so we have to parse lots of other crap first.
+ p += 7;
+ if (!parseVorbisSetupHeader(track, p, headerSize)) {
+ fprintf(stderr, "Failed to parse Vorbis \"setup\" header!\n");
+ return False;
+ }
+ }
+ } else if (strcmp(track->mimeType, "video/THEORA") == 0) {
+ u_int8_t const firstByte = p[0];
+
+ if (firstByte == 0x80) { // "identification" header
+ if (headerSize < 42) {
+ fprintf(stderr, "Theora \"identification\" header is too short (%d bytes)\n", headerSize);
+ return False;
+ } else if ((p[41]&0x7) != 0) {
+ fprintf(stderr, "Theora \"identification\" header: 'res' bits are non-zero\n");
+ return False;
+ }
+
+ track->vtoHdrs.KFGSHIFT = ((p[40]&3)<<3) | (p[41]>>5);
+ u_int32_t FRN = (p[22]<<24) | (p[23]<<16) | (p[24]<<8) | p[25]; // Frame rate numerator
+ u_int32_t FRD = (p[26]<<24) | (p[27]<<16) | (p[28]<<8) | p[29]; // Frame rate numerator
+#ifdef DEBUG
+ fprintf(stderr, "\tKFGSHIFT %d, Frame rate numerator %d, Frame rate denominator %d\n", track->vtoHdrs.KFGSHIFT, FRN, FRD);
+#endif
+ if (FRN == 0 || FRD == 0) {
+ fprintf(stderr, "Theora \"identification\" header: Bad FRN and/or FRD values: %d, %d\n", FRN, FRD);
+ return False;
+ }
+ track->vtoHdrs.uSecsPerFrame = (unsigned)((1000000.0*FRD)/FRN);
+#ifdef DEBUG
+ fprintf(stderr, "\t\t=> %u microseconds per frame\n", track->vtoHdrs.uSecsPerFrame);
+#endif
+ } else if (firstByte == 0x81) { // "comment" header
+ if (!validateCommentHeader(p, headerSize)) return False;
+ } else if (firstByte == 0x82) { // "setup" header
+ // We don't care about the contents of the Theora "setup" header; just assume it's valid
+ }
+ } else { // Opus audio
+ if (strncmp((char const*)p, "OpusHead", 8) == 0) { // "identification" header
+ // Just check the size, and the 'major' number of the version byte:
+ if (headerSize < 19 || (p[8]&0xF0) != 0) return False;
+ } else { // comment header
+ if (!validateCommentHeader(p, headerSize, 1/*isOpus*/)) return False;
+ }
+ }
+
+ return True;
+}
+
+void OggFileParser::parseAndDeliverPages() {
+#ifdef DEBUG
+ fprintf(stderr, "parsing and delivering data\n");
+#endif
+ while (parseAndDeliverPage()) {}
+}
+
+Boolean OggFileParser::parseAndDeliverPage() {
+ u_int8_t header_type_flag;
+ u_int32_t bitstream_serial_number;
+ parseStartOfPage(header_type_flag, bitstream_serial_number);
+
+ OggDemuxedTrack* demuxedTrack = fOurDemux->lookupDemuxedTrack(bitstream_serial_number);
+ if (demuxedTrack == NULL) { // this track is not being read
+#ifdef DEBUG
+ fprintf(stderr, "\tIgnoring page from unread track; skipping %d remaining packet data bytes\n",
+ fPacketSizeTable->totSizes);
+#endif
+ skipBytes(fPacketSizeTable->totSizes);
+ return True;
+ } else if (fPacketSizeTable->totSizes == 0) {
+ // This page is empty (has no packets). Skip it and continue
+#ifdef DEBUG
+ fprintf(stderr, "\t[track: %s] Skipping empty page\n", demuxedTrack->MIMEtype());
+#endif
+ return True;
+ }
+
+ // Start delivering packets next:
+ demuxedTrack->fCurrentPageIsContinuation = (header_type_flag&0x01) != 0;
+ fCurrentTrackNumber = bitstream_serial_number;
+ fCurrentParseState = DELIVERING_PACKET_WITHIN_PAGE;
+ saveParserState();
+ return False;
+}
+
+Boolean OggFileParser::deliverPacketWithinPage() {
+ OggDemuxedTrack* demuxedTrack = fOurDemux->lookupDemuxedTrack(fCurrentTrackNumber);
+ if (demuxedTrack == NULL) return False; // should not happen
+
+ unsigned packetNum = fPacketSizeTable->nextPacketNumToDeliver;
+ unsigned packetSize = fPacketSizeTable->size[packetNum];
+
+ if (!demuxedTrack->isCurrentlyAwaitingData()) {
+ // Someone has been reading this stream, but isn't right now.
+ // We can't deliver this frame until he asks for it, so punt for now.
+ // The next time he asks for a frame, he'll get it.
+#ifdef DEBUG
+ fprintf(stderr, "\t[track: %s] Deferring delivery of packet %d (%d bytes%s)\n",
+ demuxedTrack->MIMEtype(), packetNum, packetSize,
+ packetNum == fPacketSizeTable->numCompletedPackets ? " (incomplete)" : "");
+#endif
+ return True;
+ }
+
+ // Deliver the next packet:
+#ifdef DEBUG
+ fprintf(stderr, "\t[track: %s] Delivering packet %d (%d bytes%s)\n", demuxedTrack->MIMEtype(),
+ packetNum, packetSize,
+ packetNum == fPacketSizeTable->numCompletedPackets ? " (incomplete)" : "");
+#endif
+ unsigned numBytesDelivered
+ = packetSize < demuxedTrack->maxSize() ? packetSize : demuxedTrack->maxSize();
+ getBytes(demuxedTrack->to(), numBytesDelivered);
+ u_int8_t firstByte = numBytesDelivered > 0 ? demuxedTrack->to()[0] : 0x00;
+ u_int8_t secondByte = numBytesDelivered > 1 ? demuxedTrack->to()[1] : 0x00;
+ demuxedTrack->to() += numBytesDelivered;
+
+ if (demuxedTrack->fCurrentPageIsContinuation) { // the previous page's read was incomplete
+ demuxedTrack->frameSize() += numBytesDelivered;
+ } else {
+ // This is the first delivery for this "doGetNextFrame()" call.
+ demuxedTrack->frameSize() = numBytesDelivered;
+ }
+ if (packetSize > demuxedTrack->maxSize()) {
+ demuxedTrack->numTruncatedBytes() += packetSize - demuxedTrack->maxSize();
+ }
+ demuxedTrack->maxSize() -= numBytesDelivered;
+
+ // Figure out the duration and presentation time of this frame.
+ unsigned durationInMicroseconds;
+ OggTrack* track = fOurFile.lookup(demuxedTrack->fOurTrackNumber);
+
+ if (strcmp(track->mimeType, "audio/VORBIS") == 0) {
+ if ((firstByte&0x01) != 0) { // This is a header packet
+ durationInMicroseconds = 0;
+ } else { // This is a data packet.
+ // Parse the first byte to figure out its duration.
+ // Extract the next "track->vtoHdrs.ilog_vorbis_mode_count_minus_1" bits of the first byte:
+ u_int8_t const mask = 0xFE<<(track->vtoHdrs.ilog_vorbis_mode_count_minus_1);
+ u_int8_t const modeNumber = (firstByte&~mask)>>1;
+ if (modeNumber >= track->vtoHdrs.vorbis_mode_count) {
+ fprintf(stderr, "Error: Bad mode number %d (>= vorbis_mode_count %d) in Vorbis packet!\n",
+ modeNumber, track->vtoHdrs.vorbis_mode_count);
+ durationInMicroseconds = 0;
+ } else {
+ unsigned blockNumber = track->vtoHdrs.vorbis_mode_blockflag[modeNumber];
+ durationInMicroseconds = track->vtoHdrs.uSecsPerPacket[blockNumber];
+ }
+ }
+ } else if (strcmp(track->mimeType, "video/THEORA") == 0) {
+ if ((firstByte&0x80) != 0) { // This is a header packet
+ durationInMicroseconds = 0;
+ } else { // This is a data packet.
+ durationInMicroseconds = track->vtoHdrs.uSecsPerFrame;
+ }
+ } else { // "audio/OPUS"
+ if (firstByte == 0x4F/*'O'*/ && secondByte == 0x70/*'p*/) { // This is a header packet
+ durationInMicroseconds = 0;
+ } else { // This is a data packet.
+ // Parse the first byte to figure out the duration of each frame, and then (if necessary)
+ // parse the second byte to figure out how many frames are in this packet:
+ u_int8_t config = firstByte >> 3;
+ u_int8_t c = firstByte & 0x03;
+ unsigned const configDuration[32] = { // in microseconds
+ 10000, 20000, 40000, 60000, // config 0..3
+ 10000, 20000, 40000, 60000, // config 4..7
+ 10000, 20000, 40000, 60000, // config 8..11
+ 10000, 20000, // config 12..13
+ 10000, 20000, // config 14..15
+ 2500, 5000, 10000, 20000, // config 16..19
+ 2500, 5000, 10000, 20000, // config 20..23
+ 2500, 5000, 10000, 20000, // config 24..27
+ 2500, 5000, 10000, 20000 // config 28..31
+ };
+ unsigned const numFramesInPacket = c == 0 ? 1 : c == 3 ? (secondByte&0x3F) : 2;
+ durationInMicroseconds = numFramesInPacket*configDuration[config];
+ }
+ }
+
+ if (demuxedTrack->nextPresentationTime().tv_sec == 0 && demuxedTrack->nextPresentationTime().tv_usec == 0) {
+ // This is the first delivery. Initialize "demuxedTrack->nextPresentationTime()":
+ gettimeofday(&demuxedTrack->nextPresentationTime(), NULL);
+ }
+ demuxedTrack->presentationTime() = demuxedTrack->nextPresentationTime();
+ demuxedTrack->durationInMicroseconds() = durationInMicroseconds;
+
+ demuxedTrack->nextPresentationTime().tv_usec += durationInMicroseconds;
+ while (demuxedTrack->nextPresentationTime().tv_usec >= 1000000) {
+ ++demuxedTrack->nextPresentationTime().tv_sec;
+ demuxedTrack->nextPresentationTime().tv_usec -= 1000000;
+ }
+ saveParserState();
+
+ // And check whether there's a next packet in this page:
+ if (packetNum == fPacketSizeTable->numCompletedPackets) {
+ // This delivery was for an incomplete packet, at the end of the page.
+ // Return without completing delivery:
+ fCurrentParseState = PARSING_AND_DELIVERING_PAGES;
+ return False;
+ }
+
+ if (packetNum < fPacketSizeTable->numCompletedPackets-1
+ || fPacketSizeTable->lastPacketIsIncomplete) {
+ // There is at least one more packet (possibly incomplete) left in this packet.
+ // Deliver it next:
+ ++fPacketSizeTable->nextPacketNumToDeliver;
+ } else {
+ // Start parsing a new page next:
+ fCurrentParseState = PARSING_AND_DELIVERING_PAGES;
+ }
+
+ FramedSource::afterGetting(demuxedTrack); // completes delivery
+ return True;
+}
+
+void OggFileParser::parseStartOfPage(u_int8_t& header_type_flag,
+ u_int32_t& bitstream_serial_number) {
+ saveParserState();
+ // First, make sure we start with the 'capture_pattern': 0x4F676753 ('OggS'):
+ while (test4Bytes() != 0x4F676753) {
+ skipBytes(1);
+ saveParserState(); // ensures forward progress through the file
+ }
+ skipBytes(4);
+#ifdef DEBUG
+ fprintf(stderr, "\nSaw Ogg page header:\n");
+#endif
+
+ u_int8_t stream_structure_version = get1Byte();
+ if (stream_structure_version != 0) {
+ fprintf(stderr, "Saw page with unknown Ogg file version number: 0x%02x\n", stream_structure_version);
+ }
+
+ header_type_flag = get1Byte();
+#ifdef DEBUG
+ fprintf(stderr, "\theader_type_flag: 0x%02x (", header_type_flag);
+ if (header_type_flag&0x01) fprintf(stderr, "continuation ");
+ if (header_type_flag&0x02) fprintf(stderr, "bos ");
+ if (header_type_flag&0x04) fprintf(stderr, "eos ");
+ fprintf(stderr, ")\n");
+#endif
+
+ u_int32_t granule_position1 = byteSwap(get4Bytes());
+ u_int32_t granule_position2 = byteSwap(get4Bytes());
+ bitstream_serial_number = byteSwap(get4Bytes());
+ u_int32_t page_sequence_number = byteSwap(get4Bytes());
+ u_int32_t CRC_checksum = byteSwap(get4Bytes());
+ u_int8_t number_page_segments = get1Byte();
+#ifdef DEBUG
+ fprintf(stderr, "\tgranule_position 0x%08x%08x, bitstream_serial_number 0x%08x, page_sequence_number 0x%08x, CRC_checksum 0x%08x, number_page_segments %d\n", granule_position2, granule_position1, bitstream_serial_number, page_sequence_number, CRC_checksum, number_page_segments);
+#else
+ // Dummy statements to prevent 'unused variable' compiler warnings:
+#define DUMMY_STATEMENT(x) do {x = x;} while (0)
+ DUMMY_STATEMENT(granule_position1);
+ DUMMY_STATEMENT(granule_position2);
+ DUMMY_STATEMENT(page_sequence_number);
+ DUMMY_STATEMENT(CRC_checksum);
+#endif
+
+ // Look at the "segment_table" to count the sizes of the packets in this page:
+ delete fPacketSizeTable/*if any*/; fPacketSizeTable = new PacketSizeTable(number_page_segments);
+ u_int8_t lacing_value = 0;
+#ifdef DEBUG
+ fprintf(stderr, "\tsegment_table\n");
+#endif
+ for (unsigned i = 0; i < number_page_segments; ++i) {
+ lacing_value = get1Byte();
+#ifdef DEBUG
+ fprintf(stderr, "\t\t%d:\t%d", i, lacing_value);
+#endif
+ fPacketSizeTable->totSizes += lacing_value;
+ fPacketSizeTable->size[fPacketSizeTable->numCompletedPackets] += lacing_value;
+ if (lacing_value < 255) {
+ // This completes a packet:
+#ifdef DEBUG
+ fprintf(stderr, " (->%d)", fPacketSizeTable->size[fPacketSizeTable->numCompletedPackets]);
+#endif
+ ++fPacketSizeTable->numCompletedPackets;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "\n");
+#endif
+ }
+
+ fPacketSizeTable->lastPacketIsIncomplete = lacing_value == 255;
+}
diff --git a/liveMedia/OggFileParser.hh b/liveMedia/OggFileParser.hh
new file mode 100644
index 0000000..e0592fc
--- /dev/null
+++ b/liveMedia/OggFileParser.hh
@@ -0,0 +1,91 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A parser for an Ogg file
+// C++ header
+
+#ifndef _OGG_FILE_PARSER_HH
+
+#ifndef _STREAM_PARSER_HH
+#include "StreamParser.hh"
+#endif
+#ifndef _OGG_FILE_HH
+#include "OggFile.hh"
+#endif
+
+// An enum representing the current state of the parser:
+enum OggParseState {
+ PARSING_START_OF_FILE,
+ PARSING_AND_DELIVERING_PAGES,
+ DELIVERING_PACKET_WITHIN_PAGE
+};
+
+// A structure that counts the sizes of 'packets' given by each page's "segment_table":
+class PacketSizeTable {
+public:
+ PacketSizeTable(unsigned number_page_segments);
+ ~PacketSizeTable();
+
+ unsigned numCompletedPackets; // will be <= "number_page_segments"
+ unsigned* size; // an array of sizes of each of the packets
+ unsigned totSizes;
+ unsigned nextPacketNumToDeliver;
+ Boolean lastPacketIsIncomplete; // iff the last segment's 'lacing' was 255
+};
+
+class OggFileParser: public StreamParser {
+public:
+ OggFileParser(OggFile& ourFile, FramedSource* inputSource,
+ FramedSource::onCloseFunc* onEndFunc, void* onEndClientData,
+ OggDemux* ourDemux = NULL);
+ virtual ~OggFileParser();
+
+ // StreamParser 'client continue' function:
+ static void continueParsing(void* clientData, unsigned char* ptr, unsigned size, struct timeval presentationTime);
+ void continueParsing();
+
+private:
+ Boolean needHeaders() { return fNumUnfulfilledTracks > 0; }
+
+ // Parsing functions:
+ Boolean parse(); // returns True iff we have finished parsing all BOS pages (on initialization)
+
+ Boolean parseStartOfFile();
+ u_int8_t parseInitialPage(); // returns the 'header_type_flag' byte
+ void parseAndDeliverPages();
+ Boolean parseAndDeliverPage();
+ Boolean deliverPacketWithinPage();
+ void parseStartOfPage(u_int8_t& header_type_flag, u_int32_t& bitstream_serial_number);
+
+ Boolean validateHeader(OggTrack* track, u_int8_t const* p, unsigned headerSize);
+
+private:
+ // General state for parsing:
+ OggFile& fOurFile;
+ FramedSource* fInputSource;
+ FramedSource::onCloseFunc* fOnEndFunc;
+ void* fOnEndClientData;
+ OggDemux* fOurDemux;
+ OggParseState fCurrentParseState;
+
+ unsigned fNumUnfulfilledTracks;
+ PacketSizeTable* fPacketSizeTable;
+ u_int32_t fCurrentTrackNumber;
+ u_int8_t* fSavedPacket; // used to temporarily save a copy of a 'packet' from a page
+};
+
+#endif
diff --git a/liveMedia/OggFileServerDemux.cpp b/liveMedia/OggFileServerDemux.cpp
new file mode 100644
index 0000000..31f8980
--- /dev/null
+++ b/liveMedia/OggFileServerDemux.cpp
@@ -0,0 +1,109 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A server demultiplexor for a Ogg file
+// Implementation
+
+#include "OggFileServerDemux.hh"
+#include "OggFileServerMediaSubsession.hh"
+
+void OggFileServerDemux
+::createNew(UsageEnvironment& env, char const* fileName,
+ onCreationFunc* onCreation, void* onCreationClientData) {
+ (void)new OggFileServerDemux(env, fileName,
+ onCreation, onCreationClientData);
+}
+
+ServerMediaSubsession* OggFileServerDemux::newServerMediaSubsession() {
+ u_int32_t dummyResultTrackNumber;
+ return newServerMediaSubsession(dummyResultTrackNumber);
+}
+
+ServerMediaSubsession* OggFileServerDemux
+::newServerMediaSubsession(u_int32_t& resultTrackNumber) {
+ resultTrackNumber = 0;
+
+ OggTrack* nextTrack = fIter->next();
+ if (nextTrack == NULL) return NULL;
+
+ return newServerMediaSubsessionByTrackNumber(nextTrack->trackNumber);
+}
+
+ServerMediaSubsession* OggFileServerDemux
+::newServerMediaSubsessionByTrackNumber(u_int32_t trackNumber) {
+ OggTrack* track = fOurOggFile->lookup(trackNumber);
+ if (track == NULL) return NULL;
+
+ ServerMediaSubsession* result = OggFileServerMediaSubsession::createNew(*this, track);
+ if (result != NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Created 'ServerMediaSubsession' object for track #%d: (%s)\n", track->trackNumber, track->mimeType);
+#endif
+ }
+
+ return result;
+}
+
+FramedSource* OggFileServerDemux::newDemuxedTrack(unsigned clientSessionId, u_int32_t trackNumber) {
+ OggDemux* demuxToUse = NULL;
+
+ if (clientSessionId != 0 && clientSessionId == fLastClientSessionId) {
+ demuxToUse = fLastCreatedDemux; // use the same demultiplexor as before
+ // Note: This code relies upon the fact that the creation of streams for different
+ // client sessions do not overlap - so all demuxed tracks are created for one "OggDemux" at a time.
+ // Also, the "clientSessionId != 0" test is a hack, because 'session 0' is special; its audio and video streams
+ // are created and destroyed one-at-a-time, rather than both streams being
+ // created, and then (later) both streams being destroyed (as is the case
+ // for other ('real') session ids). Because of this, a separate demultiplexor is used for each 'session 0' track.
+ }
+
+ if (demuxToUse == NULL) demuxToUse = fOurOggFile->newDemux();
+
+ fLastClientSessionId = clientSessionId;
+ fLastCreatedDemux = demuxToUse;
+
+ return demuxToUse->newDemuxedTrackByTrackNumber(trackNumber);
+}
+
+OggFileServerDemux
+::OggFileServerDemux(UsageEnvironment& env, char const* fileName,
+ onCreationFunc* onCreation, void* onCreationClientData)
+ : Medium(env),
+ fFileName(fileName), fOnCreation(onCreation), fOnCreationClientData(onCreationClientData),
+ fIter(NULL/*until the OggFile is created*/),
+ fLastClientSessionId(0), fLastCreatedDemux(NULL) {
+ OggFile::createNew(env, fileName, onOggFileCreation, this);
+}
+
+OggFileServerDemux::~OggFileServerDemux() {
+ Medium::close(fOurOggFile);
+
+ delete fIter;
+}
+
+void OggFileServerDemux::onOggFileCreation(OggFile* newFile, void* clientData) {
+ ((OggFileServerDemux*)clientData)->onOggFileCreation(newFile);
+}
+
+void OggFileServerDemux::onOggFileCreation(OggFile* newFile) {
+ fOurOggFile = newFile;
+
+ fIter = new OggTrackTableIterator(fOurOggFile->trackTable());
+
+ // Now, call our own creation notification function:
+ if (fOnCreation != NULL) (*fOnCreation)(this, fOnCreationClientData);
+}
diff --git a/liveMedia/OggFileServerMediaSubsession.cpp b/liveMedia/OggFileServerMediaSubsession.cpp
new file mode 100644
index 0000000..acda9b0
--- /dev/null
+++ b/liveMedia/OggFileServerMediaSubsession.cpp
@@ -0,0 +1,54 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
+// on demand, from a track within an Ogg file.
+// Implementation
+
+#include "OggFileServerMediaSubsession.hh"
+#include "OggDemuxedTrack.hh"
+#include "FramedFilter.hh"
+
+OggFileServerMediaSubsession* OggFileServerMediaSubsession
+::createNew(OggFileServerDemux& demux, OggTrack* track) {
+ return new OggFileServerMediaSubsession(demux, track);
+}
+
+OggFileServerMediaSubsession
+::OggFileServerMediaSubsession(OggFileServerDemux& demux, OggTrack* track)
+ : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
+ fOurDemux(demux), fTrack(track), fNumFiltersInFrontOfTrack(0) {
+}
+
+OggFileServerMediaSubsession::~OggFileServerMediaSubsession() {
+}
+
+FramedSource* OggFileServerMediaSubsession
+::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
+ FramedSource* baseSource = fOurDemux.newDemuxedTrack(clientSessionId, fTrack->trackNumber);
+ if (baseSource == NULL) return NULL;
+
+ return fOurDemux.ourOggFile()
+ ->createSourceForStreaming(baseSource, fTrack->trackNumber,
+ estBitrate, fNumFiltersInFrontOfTrack);
+}
+
+RTPSink* OggFileServerMediaSubsession
+::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
+ return fOurDemux.ourOggFile()
+ ->createRTPSinkForTrackNumber(fTrack->trackNumber, rtpGroupsock, rtpPayloadTypeIfDynamic);
+}
diff --git a/liveMedia/VP8VideoMatroskaFileServerMediaSubsession.hh b/liveMedia/OggFileServerMediaSubsession.hh
similarity index 55%
rename from liveMedia/VP8VideoMatroskaFileServerMediaSubsession.hh
rename to liveMedia/OggFileServerMediaSubsession.hh
index 5542fb6..fd5516a 100644
--- a/liveMedia/VP8VideoMatroskaFileServerMediaSubsession.hh
+++ b/liveMedia/OggFileServerMediaSubsession.hh
@@ -14,41 +14,40 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from a VP8 Video track within a Matroska file.
+// on demand, from a track within an Ogg file.
// C++ header
-#ifndef _VP8_VIDEO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _VP8_VIDEO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
+#ifndef _OGG_FILE_SERVER_MEDIA_SUBSESSION_HH
+#define _OGG_FILE_SERVER_MEDIA_SUBSESSION_HH
#ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
#include "FileServerMediaSubsession.hh"
#endif
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#include "MatroskaFileServerDemux.hh"
+#ifndef _OGG_FILE_SERVER_DEMUX_HH
+#include "OggFileServerDemux.hh"
#endif
-class VP8VideoMatroskaFileServerMediaSubsession: public FileServerMediaSubsession {
+class OggFileServerMediaSubsession: public FileServerMediaSubsession {
public:
- static VP8VideoMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
+ static OggFileServerMediaSubsession*
+ createNew(OggFileServerDemux& demux, OggTrack* track);
-private:
- VP8VideoMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~VP8VideoMatroskaFileServerMediaSubsession();
+protected:
+ OggFileServerMediaSubsession(OggFileServerDemux& demux, OggTrack* track);
+ // called only by createNew(), or by subclass constructors
+ virtual ~OggFileServerMediaSubsession();
-private: // redefined virtual functions
- virtual float duration() const;
- virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
+protected: // redefined virtual functions
virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
-private:
- MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
+protected:
+ OggFileServerDemux& fOurDemux;
+ OggTrack* fTrack;
+ unsigned fNumFiltersInFrontOfTrack;
};
#endif
diff --git a/liveMedia/OggFileSink.cpp b/liveMedia/OggFileSink.cpp
new file mode 100644
index 0000000..61e29e5
--- /dev/null
+++ b/liveMedia/OggFileSink.cpp
@@ -0,0 +1,273 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// 'Ogg' File Sink (recording a single media track only)
+// Implementation
+
+#include "OggFileSink.hh"
+#include "OutputFile.hh"
+#include "VorbisAudioRTPSource.hh" // for "parseVorbisOrTheoraConfigStr()"
+#include "MPEG2TransportStreamMultiplexor.hh" // for calculateCRC()
+#include "FramedSource.hh"
+
+OggFileSink* OggFileSink
+::createNew(UsageEnvironment& env, char const* fileName,
+ unsigned samplingFrequency, char const* configStr,
+ unsigned bufferSize, Boolean oneFilePerFrame) {
+ do {
+ FILE* fid;
+ char const* perFrameFileNamePrefix;
+ if (oneFilePerFrame) {
+ // Create the fid for each frame
+ fid = NULL;
+ perFrameFileNamePrefix = fileName;
+ } else {
+ // Normal case: create the fid once
+ fid = OpenOutputFile(env, fileName);
+ if (fid == NULL) break;
+ perFrameFileNamePrefix = NULL;
+ }
+
+ return new OggFileSink(env, fid, samplingFrequency, configStr, bufferSize, perFrameFileNamePrefix);
+ } while (0);
+
+ return NULL;
+}
+
+OggFileSink::OggFileSink(UsageEnvironment& env, FILE* fid,
+ unsigned samplingFrequency, char const* configStr,
+ unsigned bufferSize, char const* perFrameFileNamePrefix)
+ : FileSink(env, fid, bufferSize, perFrameFileNamePrefix),
+ fSamplingFrequency(samplingFrequency), fConfigStr(configStr),
+ fHaveWrittenFirstFrame(False), fHaveSeenEOF(False),
+ fGranulePosition(0), fGranulePositionAdjustment(0), fPageSequenceNumber(0),
+ fIsTheora(False), fGranuleIncrementPerFrame(1),
+ fAltFrameSize(0), fAltNumTruncatedBytes(0) {
+ fAltBuffer = new unsigned char[bufferSize];
+
+ // Initialize our 'Ogg page header' array with constant values:
+ u_int8_t* p = fPageHeaderBytes;
+ *p++=0x4f; *p++=0x67; *p++=0x67; *p++=0x53; // bytes 0..3: 'capture_pattern': "OggS"
+ *p++=0; // byte 4: 'stream_structure_version': 0
+ *p++=0; // byte 5: 'header_type_flag': set on each write
+ *p++=0; *p++=0; *p++=0; *p++=0; *p++=0; *p++=0; *p++=0; *p++=0;
+ // bytes 6..13: 'granule_position': set on each write
+ *p++=1; *p++=0; *p++=0; *p++=0; // bytes 14..17: 'bitstream_serial_number': 1
+ *p++=0; *p++=0; *p++=0; *p++=0; // bytes 18..21: 'page_sequence_number': set on each write
+ *p++=0; *p++=0; *p++=0; *p++=0; // bytes 22..25: 'CRC_checksum': set on each write
+ *p=0; // byte 26: 'number_page_segments': set on each write
+}
+
+OggFileSink::~OggFileSink() {
+ // We still have the previously-arrived frame, so write it to the file before we end:
+ fHaveSeenEOF = True;
+ OggFileSink::addData(fAltBuffer, fAltFrameSize, fAltPresentationTime);
+
+ delete[] fAltBuffer;
+}
+
+Boolean OggFileSink::continuePlaying() {
+ // Identical to "FileSink::continuePlaying()",
+ // except that we use our own 'on source closure' function:
+ if (fSource == NULL) return False;
+
+ fSource->getNextFrame(fBuffer, fBufferSize,
+ FileSink::afterGettingFrame, this,
+ ourOnSourceClosure, this);
+ return True;
+}
+
+#define PAGE_DATA_MAX_SIZE (255*255)
+
+void OggFileSink::addData(unsigned char const* data, unsigned dataSize,
+ struct timeval presentationTime) {
+ if (dataSize == 0) return;
+
+ // Set "fGranulePosition" for this frame:
+ if (fIsTheora) {
+ // Special case for Theora: "fGranulePosition" is supposed to be made up of a pair:
+ // (frame count to last key frame) | (frame count since last key frame)
+ // However, because there appears to be no easy way to figure out which frames are key frames,
+ // we just assume that all frames are key frames.
+ if (!(data[0] >= 0x80 && data[0] <= 0x82)) { // for header pages, "fGranulePosition" remains 0
+ fGranulePosition += fGranuleIncrementPerFrame;
+ }
+ } else {
+ double ptDiff
+ = (presentationTime.tv_sec - fFirstPresentationTime.tv_sec)
+ + (presentationTime.tv_usec - fFirstPresentationTime.tv_usec)/1000000.0;
+ int64_t newGranulePosition
+ = (int64_t)(fSamplingFrequency*ptDiff) + fGranulePositionAdjustment;
+ if (newGranulePosition < fGranulePosition) {
+ // Update "fGranulePositionAdjustment" so that "fGranulePosition" remains monotonic
+ fGranulePositionAdjustment += fGranulePosition - newGranulePosition;
+ } else {
+ fGranulePosition = newGranulePosition;
+ }
+ }
+
+ // Write the frame to the file as a single Ogg 'page' (or perhaps as multiple pages
+ // if it's too big for a single page). We don't aggregate more than one frame within
+ // an Ogg page because that's not legal for some headers, and because that would make
+ // it difficult for us to properly set the 'eos' (end of stream) flag on the last page.
+
+ // First, figure out how many pages to write here
+ // (a page can contain no more than PAGE_DATA_MAX_SIZE bytes)
+ unsigned numPagesToWrite = dataSize/PAGE_DATA_MAX_SIZE + 1;
+ // Note that if "dataSize" is a integral multiple of PAGE_DATA_MAX_SIZE, there will
+ // be an extra 0-size page at the end
+ for (unsigned i = 0; i < numPagesToWrite; ++i) {
+ // First, fill in the changeable parts of our 'page header' array;
+ u_int8_t header_type_flag = 0x0;
+ if (!fHaveWrittenFirstFrame && i == 0) {
+ header_type_flag |= 0x02; // 'bos'
+ fHaveWrittenFirstFrame = True; // for the future
+ }
+ if (i > 0) header_type_flag |= 0x01; // 'continuation'
+ if (fHaveSeenEOF && i == numPagesToWrite-1) header_type_flag |= 0x04; // 'eos'
+ fPageHeaderBytes[5] = header_type_flag;
+
+ if (i < numPagesToWrite-1) {
+ // For pages where the frame does not end, set 'granule_position' in the header to -1:
+ fPageHeaderBytes[6] = fPageHeaderBytes[7] = fPageHeaderBytes[8] = fPageHeaderBytes[9] =
+ fPageHeaderBytes[10] = fPageHeaderBytes[11] = fPageHeaderBytes[12] = fPageHeaderBytes[13]
+ = 0xFF;
+ } else {
+ fPageHeaderBytes[6] = (u_int8_t)fGranulePosition;
+ fPageHeaderBytes[7] = (u_int8_t)(fGranulePosition>>8);
+ fPageHeaderBytes[8] = (u_int8_t)(fGranulePosition>>16);
+ fPageHeaderBytes[9] = (u_int8_t)(fGranulePosition>>24);
+ fPageHeaderBytes[10] = (u_int8_t)(fGranulePosition>>32);
+ fPageHeaderBytes[11] = (u_int8_t)(fGranulePosition>>40);
+ fPageHeaderBytes[12] = (u_int8_t)(fGranulePosition>>48);
+ fPageHeaderBytes[13] = (u_int8_t)(fGranulePosition>>56);
+ }
+
+ fPageHeaderBytes[18] = (u_int8_t)fPageSequenceNumber;
+ fPageHeaderBytes[19] = (u_int8_t)(fPageSequenceNumber>>8);
+ fPageHeaderBytes[20] = (u_int8_t)(fPageSequenceNumber>>16);
+ fPageHeaderBytes[21] = (u_int8_t)(fPageSequenceNumber>>24);
+ ++fPageSequenceNumber;
+
+ unsigned pageDataSize;
+ u_int8_t number_page_segments;
+ if (dataSize >= PAGE_DATA_MAX_SIZE) {
+ pageDataSize = PAGE_DATA_MAX_SIZE;
+ number_page_segments = 255;
+ } else {
+ pageDataSize = dataSize;
+ number_page_segments = (pageDataSize+255)/255; // so that we don't end with a lacing of 255
+ }
+ fPageHeaderBytes[26] = number_page_segments;
+
+ u_int8_t segment_table[255];
+ for (unsigned j = 0; j < (unsigned)(number_page_segments-1); ++j) {
+ segment_table[j] = 255;
+ }
+ segment_table[number_page_segments-1] = pageDataSize%255;
+
+ // Compute the CRC from the 'page header' array, the 'segment_table', and the frame data:
+ u_int32_t crc = 0;
+ fPageHeaderBytes[22] = fPageHeaderBytes[23] = fPageHeaderBytes[24] = fPageHeaderBytes[25] = 0;
+ crc = calculateCRC(fPageHeaderBytes, 27, 0);
+ crc = calculateCRC(segment_table, number_page_segments, crc);
+ crc = calculateCRC(data, pageDataSize, crc);
+ fPageHeaderBytes[22] = (u_int8_t)crc;
+ fPageHeaderBytes[23] = (u_int8_t)(crc>>8);
+ fPageHeaderBytes[24] = (u_int8_t)(crc>>16);
+ fPageHeaderBytes[25] = (u_int8_t)(crc>>24);
+
+ // Then write out the 'page header' array:
+ FileSink::addData(fPageHeaderBytes, 27, presentationTime);
+
+ // Then write out the 'segment_table':
+ FileSink::addData(segment_table, number_page_segments, presentationTime);
+
+ // Then add frame data, to complete the page:
+ FileSink::addData(data, pageDataSize, presentationTime);
+ data += pageDataSize;
+ dataSize -= pageDataSize;
+ }
+}
+
+void OggFileSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
+ if (!fHaveWrittenFirstFrame) {
+ fFirstPresentationTime = presentationTime;
+
+ // If we have a 'config string' representing 'packed configuration headers'
+ // ("identification", "comment", "setup"), unpack them and prepend them to the file:
+ if (fConfigStr != NULL && fConfigStr[0] != '\0') {
+ u_int8_t* identificationHdr; unsigned identificationHdrSize;
+ u_int8_t* commentHdr; unsigned commentHdrSize;
+ u_int8_t* setupHdr; unsigned setupHdrSize;
+ u_int32_t identField;
+ parseVorbisOrTheoraConfigStr(fConfigStr,
+ identificationHdr, identificationHdrSize,
+ commentHdr, commentHdrSize,
+ setupHdr, setupHdrSize,
+ identField);
+ if (identificationHdrSize >= 42
+ && strncmp((const char*)&identificationHdr[1], "theora", 6) == 0) {
+ // Hack for Theora video: Parse the "identification" hdr to get the "KFGSHIFT" parameter:
+ fIsTheora = True;
+ u_int8_t const KFGSHIFT = ((identificationHdr[40]&3)<<3) | (identificationHdr[41]>>5);
+ fGranuleIncrementPerFrame = (u_int64_t)(1 << KFGSHIFT);
+ }
+ OggFileSink::addData(identificationHdr, identificationHdrSize, presentationTime);
+ OggFileSink::addData(commentHdr, commentHdrSize, presentationTime);
+
+ // Hack: Handle the "setup" header as if had arrived in the previous delivery, so it'll get
+ // written properly below:
+ if (setupHdrSize > fBufferSize) {
+ fAltFrameSize = fBufferSize;
+ fAltNumTruncatedBytes = setupHdrSize - fBufferSize;
+ } else {
+ fAltFrameSize = setupHdrSize;
+ fAltNumTruncatedBytes = 0;
+ }
+ memmove(fAltBuffer, setupHdr, fAltFrameSize);
+ fAltPresentationTime = presentationTime;
+
+ delete[] identificationHdr;
+ delete[] commentHdr;
+ delete[] setupHdr;
+ }
+ }
+
+ // Save this input frame for next time, and instead write the previous input frame now:
+ unsigned char* tmpPtr = fBuffer; fBuffer = fAltBuffer; fAltBuffer = tmpPtr;
+ unsigned prevFrameSize = fAltFrameSize; fAltFrameSize = frameSize;
+ unsigned prevNumTruncatedBytes = fAltNumTruncatedBytes; fAltNumTruncatedBytes = numTruncatedBytes;
+ struct timeval prevPresentationTime = fAltPresentationTime; fAltPresentationTime = presentationTime;
+
+ // Call the parent class to complete the normal file write with the (previous) input frame:
+ FileSink::afterGettingFrame(prevFrameSize, prevNumTruncatedBytes, prevPresentationTime);
+}
+
+void OggFileSink::ourOnSourceClosure(void* clientData) {
+ ((OggFileSink*)clientData)->ourOnSourceClosure();
+}
+
+void OggFileSink::ourOnSourceClosure() {
+ fHaveSeenEOF = True;
+
+ // We still have the previously-arrived frame, so write it to the file before we end:
+ OggFileSink::addData(fAltBuffer, fAltFrameSize, fAltPresentationTime);
+
+ // Handle the closure for real:
+ onSourceClosure();
+}
diff --git a/liveMedia/OnDemandServerMediaSubsession.cpp b/liveMedia/OnDemandServerMediaSubsession.cpp
index 38462a8..872deb6 100644
--- a/liveMedia/OnDemandServerMediaSubsession.cpp
+++ b/liveMedia/OnDemandServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand.
// Implementation
@@ -25,10 +25,19 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
OnDemandServerMediaSubsession
::OnDemandServerMediaSubsession(UsageEnvironment& env,
Boolean reuseFirstSource,
- portNumBits initialPortNum)
+ portNumBits initialPortNum,
+ Boolean multiplexRTCPWithRTP)
: ServerMediaSubsession(env),
- fSDPLines(NULL), fReuseFirstSource(reuseFirstSource), fInitialPortNum(initialPortNum), fLastStreamToken(NULL) {
+ fSDPLines(NULL), fReuseFirstSource(reuseFirstSource),
+ fMultiplexRTCPWithRTP(multiplexRTCPWithRTP), fLastStreamToken(NULL),
+ fAppHandlerTask(NULL), fAppHandlerClientData(NULL) {
fDestinationsHashTable = HashTable::create(ONE_WORD_HASH_KEYS);
+ if (fMultiplexRTCPWithRTP) {
+ fInitialPortNum = initialPortNum;
+ } else {
+ // Make sure RTP ports are even-numbered:
+ fInitialPortNum = (initialPortNum+1)&~1;
+ }
gethostname(fCNAME, sizeof fCNAME);
fCNAME[sizeof fCNAME-1] = '\0'; // just in case
}
@@ -59,13 +68,14 @@ OnDemandServerMediaSubsession::sdpLines() {
struct in_addr dummyAddr;
dummyAddr.s_addr = 0;
- Groupsock dummyGroupsock(envir(), dummyAddr, 0, 0);
+ Groupsock* dummyGroupsock = createGroupsock(dummyAddr, 0);
unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
- RTPSink* dummyRTPSink
- = createNewRTPSink(&dummyGroupsock, rtpPayloadType, inputSource);
+ RTPSink* dummyRTPSink = createNewRTPSink(dummyGroupsock, rtpPayloadType, inputSource);
+ if (dummyRTPSink != NULL && dummyRTPSink->estimatedBitrate() > 0) estBitrate = dummyRTPSink->estimatedBitrate();
setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);
Medium::close(dummyRTPSink);
+ delete dummyGroupsock;
closeStreamSource(inputSource);
}
@@ -105,66 +115,75 @@ void OnDemandServerMediaSubsession
// Create 'groupsock' and 'sink' objects for the destination,
// using previously unused server port numbers:
- RTPSink* rtpSink;
- BasicUDPSink* udpSink;
- Groupsock* rtpGroupsock;
- Groupsock* rtcpGroupsock;
- portNumBits serverPortNum;
- if (clientRTCPPort.num() == 0) {
- // We're streaming raw UDP (not RTP). Create a single groupsock:
- NoReuse dummy(envir()); // ensures that we skip over ports that are already in use
- for (serverPortNum = fInitialPortNum; ; ++serverPortNum) {
- struct in_addr dummyAddr; dummyAddr.s_addr = 0;
-
- serverRTPPort = serverPortNum;
- rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);
- if (rtpGroupsock->socketNum() >= 0) break; // success
- }
-
- rtcpGroupsock = NULL;
- rtpSink = NULL;
- udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);
- } else {
- // Normal case: We're streaming RTP (over UDP or TCP). Create a pair of
- // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even):
- NoReuse dummy(envir()); // ensures that we skip over ports that are already in use
- for (portNumBits serverPortNum = fInitialPortNum; ; serverPortNum += 2) {
- struct in_addr dummyAddr; dummyAddr.s_addr = 0;
-
- serverRTPPort = serverPortNum;
- rtpGroupsock = new Groupsock(envir(), dummyAddr, serverRTPPort, 255);
- if (rtpGroupsock->socketNum() < 0) {
- delete rtpGroupsock;
- continue; // try again
+ RTPSink* rtpSink = NULL;
+ BasicUDPSink* udpSink = NULL;
+ Groupsock* rtpGroupsock = NULL;
+ Groupsock* rtcpGroupsock = NULL;
+
+ if (clientRTPPort.num() != 0 || tcpSocketNum >= 0) { // Normal case: Create destinations
+ portNumBits serverPortNum;
+ if (clientRTCPPort.num() == 0) {
+ // We're streaming raw UDP (not RTP). Create a single groupsock:
+ NoReuse dummy(envir()); // ensures that we skip over ports that are already in use
+ for (serverPortNum = fInitialPortNum; ; ++serverPortNum) {
+ struct in_addr dummyAddr; dummyAddr.s_addr = 0;
+
+ serverRTPPort = serverPortNum;
+ rtpGroupsock = createGroupsock(dummyAddr, serverRTPPort);
+ if (rtpGroupsock->socketNum() >= 0) break; // success
}
- serverRTCPPort = serverPortNum+1;
- rtcpGroupsock = new Groupsock(envir(), dummyAddr, serverRTCPPort, 255);
- if (rtcpGroupsock->socketNum() < 0) {
- delete rtpGroupsock;
- delete rtcpGroupsock;
- continue; // try again
+ udpSink = BasicUDPSink::createNew(envir(), rtpGroupsock);
+ } else {
+ // Normal case: We're streaming RTP (over UDP or TCP). Create a pair of
+ // groupsocks (RTP and RTCP), with adjacent port numbers (RTP port number even).
+ // (If we're multiplexing RTCP and RTP over the same port number, it can be odd or even.)
+ NoReuse dummy(envir()); // ensures that we skip over ports that are already in use
+ for (portNumBits serverPortNum = fInitialPortNum; ; ++serverPortNum) {
+ struct in_addr dummyAddr; dummyAddr.s_addr = 0;
+
+ serverRTPPort = serverPortNum;
+ rtpGroupsock = createGroupsock(dummyAddr, serverRTPPort);
+ if (rtpGroupsock->socketNum() < 0) {
+ delete rtpGroupsock;
+ continue; // try again
+ }
+
+ if (fMultiplexRTCPWithRTP) {
+ // Use the RTP 'groupsock' object for RTCP as well:
+ serverRTCPPort = serverRTPPort;
+ rtcpGroupsock = rtpGroupsock;
+ } else {
+ // Create a separate 'groupsock' object (with the next (odd) port number) for RTCP:
+ serverRTCPPort = ++serverPortNum;
+ rtcpGroupsock = createGroupsock(dummyAddr, serverRTCPPort);
+ if (rtcpGroupsock->socketNum() < 0) {
+ delete rtpGroupsock;
+ delete rtcpGroupsock;
+ continue; // try again
+ }
+ }
+
+ break; // success
}
- break; // success
+ unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
+ rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);
+ if (rtpSink != NULL && rtpSink->estimatedBitrate() > 0) streamBitrate = rtpSink->estimatedBitrate();
}
- unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
- rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);
- udpSink = NULL;
- }
-
- // Turn off the destinations for each groupsock. They'll get set later
- // (unless TCP is used instead):
- if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations();
- if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations();
-
- if (rtpGroupsock != NULL) {
- // Try to use a big send buffer for RTP - at least 0.1 second of
- // specified bandwidth and at least 50 KB
- unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
- if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;
- increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize);
+ // Turn off the destinations for each groupsock. They'll get set later
+ // (unless TCP is used instead):
+ if (rtpGroupsock != NULL) rtpGroupsock->removeAllDestinations();
+ if (rtcpGroupsock != NULL) rtcpGroupsock->removeAllDestinations();
+
+ if (rtpGroupsock != NULL) {
+ // Try to use a big send buffer for RTP - at least 0.1 second of
+ // specified bandwidth and at least 50 KB
+ unsigned rtpBufSize = streamBitrate * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
+ if (rtpBufSize < 50 * 1024) rtpBufSize = 50 * 1024;
+ increaseSendBufferTo(envir(), rtpGroupsock->socketNum(), rtpBufSize);
+ }
}
// Set up the state of the stream. The stream will get started later:
@@ -196,7 +215,7 @@ void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,
Destinations* destinations
= (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId));
if (streamState != NULL) {
- streamState->startPlaying(destinations,
+ streamState->startPlaying(destinations, clientSessionId,
rtcpRRHandler, rtcpRRHandlerClientData,
serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
RTPSink* rtpSink = streamState->rtpSink(); // alias
@@ -245,11 +264,19 @@ void OnDemandServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,
}
}
-void OnDemandServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, void* streamToken) {
+void OnDemandServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, void* streamToken,
+ double streamEndTime, u_int64_t& numBytes) {
+ numBytes = 0; // by default: unknown
+
StreamState* streamState = (StreamState*)streamToken;
if (streamState != NULL && streamState->mediaSource() != NULL) {
// Because we're not seeking here, get the current NPT, and remember it as the new 'start' NPT:
streamState->startNPT() = getCurrentNPT(streamToken);
+
+ double duration = streamEndTime - streamState->startNPT();
+ if (duration < 0.0) duration = 0.0;
+ setStreamSourceDuration(streamState->mediaSource(), duration, numBytes);
+
RTPSink* rtpSink = streamState->rtpSink(); // alias
if (rtpSink != NULL) rtpSink->resetPresentationTimes();
}
@@ -277,7 +304,7 @@ float OnDemandServerMediaSubsession::getCurrentNPT(void* streamToken) {
return streamState->startNPT()
+ (rtpSink->mostRecentPresentationTime().tv_sec - rtpSink->initialPresentationTime().tv_sec)
- + (rtpSink->mostRecentPresentationTime().tv_sec - rtpSink->initialPresentationTime().tv_sec)/1000000.0f;
+ + (rtpSink->mostRecentPresentationTime().tv_usec - rtpSink->initialPresentationTime().tv_usec)/1000000.0f;
} while (0);
return 0.0;
@@ -290,6 +317,20 @@ FramedSource* OnDemandServerMediaSubsession::getStreamSource(void* streamToken)
return streamState->mediaSource();
}
+void OnDemandServerMediaSubsession
+::getRTPSinkandRTCP(void* streamToken,
+ RTPSink const*& rtpSink, RTCPInstance const*& rtcp) {
+ if (streamToken == NULL) {
+ rtpSink = NULL;
+ rtcp = NULL;
+ return;
+ }
+
+ StreamState* streamState = (StreamState*)streamToken;
+ rtpSink = streamState->rtpSink();
+ rtcp = streamState->rtcpInstance();
+}
+
void OnDemandServerMediaSubsession::deleteStream(unsigned clientSessionId,
void*& streamToken) {
StreamState* streamState = (StreamState*)streamToken;
@@ -301,7 +342,7 @@ void OnDemandServerMediaSubsession::deleteStream(unsigned clientSessionId,
fDestinationsHashTable->Remove((char const*)clientSessionId);
// Stop streaming to these destinations:
- if (streamState != NULL) streamState->endPlaying(destinations);
+ if (streamState != NULL) streamState->endPlaying(destinations, clientSessionId);
}
// Delete the "StreamState" structure if it's no longer being used:
@@ -326,6 +367,7 @@ char const* OnDemandServerMediaSubsession
void OnDemandServerMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/,
double& /*seekNPT*/, double /*streamDuration*/, u_int64_t& numBytes) {
// Default implementation: Do nothing
+ numBytes = 0;
}
void OnDemandServerMediaSubsession::seekStreamSource(FramedSource* /*inputSource*/,
@@ -340,10 +382,44 @@ void OnDemandServerMediaSubsession
// Default implementation: Do nothing
}
+void OnDemandServerMediaSubsession
+::setStreamSourceDuration(FramedSource* /*inputSource*/, double /*streamDuration*/, u_int64_t& numBytes) {
+ // Default implementation: Do nothing
+ numBytes = 0;
+}
+
void OnDemandServerMediaSubsession::closeStreamSource(FramedSource *inputSource) {
Medium::close(inputSource);
}
+Groupsock* OnDemandServerMediaSubsession
+::createGroupsock(struct in_addr const& addr, Port port) {
+ // Default implementation; may be redefined by subclasses:
+ return new Groupsock(envir(), addr, port, 255);
+}
+
+RTCPInstance* OnDemandServerMediaSubsession
+::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
+ unsigned char const* cname, RTPSink* sink) {
+ // Default implementation; may be redefined by subclasses:
+ return RTCPInstance::createNew(envir(), RTCPgs, totSessionBW, cname, sink, NULL/*we're a server*/);
+}
+
+void OnDemandServerMediaSubsession
+::setRTCPAppPacketHandler(RTCPAppHandlerFunc* handler, void* clientData) {
+ fAppHandlerTask = handler;
+ fAppHandlerClientData = clientData;
+}
+
+void OnDemandServerMediaSubsession
+::sendRTCPAppPacket(u_int8_t subtype, char const* name,
+ u_int8_t* appDependentData, unsigned appDependentDataSize) {
+ StreamState* streamState = (StreamState*)fLastStreamToken;
+ if (streamState != NULL) {
+ streamState->sendRTCPAppPacket(subtype, name, appDependentData, appDependentDataSize);
+ }
+}
+
void OnDemandServerMediaSubsession
::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) {
if (rtpSink == NULL) return;
@@ -352,6 +428,7 @@ void OnDemandServerMediaSubsession
unsigned char rtpPayloadType = rtpSink->rtpPayloadType();
AddressString ipAddressStr(fServerAddressForSDP);
char* rtpmapLine = rtpSink->rtpmapLine();
+ char const* rtcpmuxLine = fMultiplexRTCPWithRTP ? "a=rtcp-mux\r\n" : "";
char const* rangeLine = rangeSDPLine();
char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource);
if (auxSDPLine == NULL) auxSDPLine = "";
@@ -363,12 +440,14 @@ void OnDemandServerMediaSubsession
"%s"
"%s"
"%s"
+ "%s"
"a=control:%s\r\n";
unsigned sdpFmtSize = strlen(sdpFmt)
+ strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */
+ strlen(ipAddressStr.val())
+ 20 /* max int len */
+ strlen(rtpmapLine)
+ + strlen(rtcpmuxLine)
+ strlen(rangeLine)
+ strlen(auxSDPLine)
+ strlen(trackId());
@@ -380,6 +459,7 @@ void OnDemandServerMediaSubsession
ipAddressStr.val(), // c= address
estBitrate, // b=AS:<bandwidth>
rtpmapLine, // a=rtpmap:... (if present)
+ rtcpmuxLine, // a=rtcp-mux:... (if present)
rangeLine, // a=range:... (if present)
auxSDPLine, // optional extra SDP line
trackId()); // a=control:<track-id>
@@ -423,7 +503,7 @@ StreamState::~StreamState() {
}
void StreamState
-::startPlaying(Destinations* dests,
+::startPlaying(Destinations* dests, unsigned clientSessionId,
TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,
ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
void* serverRequestAlternativeByteHandlerClientData) {
@@ -431,11 +511,9 @@ void StreamState
if (fRTCPInstance == NULL && fRTPSink != NULL) {
// Create (and start) a 'RTCP instance' for this RTP sink:
- fRTCPInstance
- = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,
- fTotalBW, (unsigned char*)fMaster.fCNAME,
- fRTPSink, NULL /* we're a server */);
+ fRTCPInstance = fMaster.createRTCP(fRTCPgs, fTotalBW, (unsigned char*)fMaster.fCNAME, fRTPSink);
// Note: This starts RTCP running automatically
+ fRTCPInstance->setAppHandler(fMaster.fAppHandlerTask, fMaster.fAppHandlerClientData);
}
if (dests->isTCP) {
@@ -455,8 +533,10 @@ void StreamState
} else {
// Tell the RTP and RTCP 'groupsocks' about this destination
// (in case they don't already have it):
- if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort);
- if (fRTCPgs != NULL) fRTCPgs->addDestination(dests->addr, dests->rtcpPort);
+ if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort, clientSessionId);
+ if (fRTCPgs != NULL && !(fRTCPgs == fRTPgs && dests->rtcpPort.num() == dests->rtpPort.num())) {
+ fRTCPgs->addDestination(dests->addr, dests->rtcpPort, clientSessionId);
+ }
if (fRTCPInstance != NULL) {
fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,
rtcpRRHandler, rtcpRRHandlerClientData);
@@ -486,7 +566,7 @@ void StreamState::pause() {
fAreCurrentlyPlaying = False;
}
-void StreamState::endPlaying(Destinations* dests) {
+void StreamState::endPlaying(Destinations* dests, unsigned clientSessionId) {
#if 0
// The following code is temporarily disabled, because it erroneously sends RTCP "BYE"s to all clients if multiple
// clients are streaming from the same data source (i.e., if "reuseFirstSource" is True), and we don't want that to happen
@@ -510,14 +590,21 @@ void StreamState::endPlaying(Destinations* dests) {
}
} else {
// Tell the RTP and RTCP 'groupsocks' to stop using these destinations:
- if (fRTPgs != NULL) fRTPgs->removeDestination(dests->addr, dests->rtpPort);
- if (fRTCPgs != NULL) fRTCPgs->removeDestination(dests->addr, dests->rtcpPort);
+ if (fRTPgs != NULL) fRTPgs->removeDestination(clientSessionId);
+ if (fRTCPgs != NULL && fRTCPgs != fRTPgs) fRTCPgs->removeDestination(clientSessionId);
if (fRTCPInstance != NULL) {
fRTCPInstance->unsetSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort);
}
}
}
+void StreamState::sendRTCPAppPacket(u_int8_t subtype, char const* name,
+ u_int8_t* appDependentData, unsigned appDependentDataSize) {
+ if (fRTCPInstance != NULL) {
+ fRTCPInstance->sendAppPacket(subtype, name, appDependentData, appDependentDataSize);
+ }
+}
+
void StreamState::reclaim() {
// Delete allocated media objects
Medium::close(fRTCPInstance) /* will send a RTCP BYE */; fRTCPInstance = NULL;
@@ -527,6 +614,7 @@ void StreamState::reclaim() {
fMaster.closeStreamSource(fMediaSource); fMediaSource = NULL;
if (fMaster.fLastStreamToken == this) fMaster.fLastStreamToken = NULL;
- delete fRTPgs; fRTPgs = NULL;
- delete fRTCPgs; fRTCPgs = NULL;
+ delete fRTPgs;
+ if (fRTCPgs != fRTPgs) delete fRTCPgs;
+ fRTPgs = NULL; fRTCPgs = NULL;
}
diff --git a/liveMedia/OutputFile.cpp b/liveMedia/OutputFile.cpp
index d974367..8af20dd 100644
--- a/liveMedia/OutputFile.cpp
+++ b/liveMedia/OutputFile.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Common routines for opening/closing named output files
// Implementation
diff --git a/liveMedia/PassiveServerMediaSubsession.cpp b/liveMedia/PassiveServerMediaSubsession.cpp
index 384fc33..85c6231 100644
--- a/liveMedia/PassiveServerMediaSubsession.cpp
+++ b/liveMedia/PassiveServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that represents an existing
// 'RTPSink', rather than one that creates new 'RTPSink's on demand.
// Implementation
@@ -60,6 +60,13 @@ PassiveServerMediaSubsession::~PassiveServerMediaSubsession() {
delete fClientRTCPSourceRecords;
}
+Boolean PassiveServerMediaSubsession::rtcpIsMuxed() {
+ if (fRTCPInstance == NULL) return False;
+
+ // Check whether RTP and RTCP use the same "groupsock" object:
+ return &(fRTPSink.groupsockBeingUsed()) == fRTCPInstance->RTCPgs();
+}
+
char const*
PassiveServerMediaSubsession::sdpLines() {
if (fSDPLines == NULL ) {
@@ -74,6 +81,7 @@ PassiveServerMediaSubsession::sdpLines() {
unsigned estBitrate
= fRTCPInstance == NULL ? 50 : fRTCPInstance->totSessionBW();
char* rtpmapLine = fRTPSink.rtpmapLine();
+ char const* rtcpmuxLine = rtcpIsMuxed() ? "a=rtcp-mux\r\n" : "";
char const* rangeLine = rangeSDPLine();
char const* auxSDPLine = fRTPSink.auxSDPLine();
if (auxSDPLine == NULL) auxSDPLine = "";
@@ -85,12 +93,14 @@ PassiveServerMediaSubsession::sdpLines() {
"%s"
"%s"
"%s"
+ "%s"
"a=control:%s\r\n";
unsigned sdpFmtSize = strlen(sdpFmt)
+ strlen(mediaType) + 5 /* max short len */ + 3 /* max char len */
+ strlen(groupAddressStr.val()) + 3 /* max char len */
+ 20 /* max int len */
+ strlen(rtpmapLine)
+ + strlen(rtcpmuxLine)
+ strlen(rangeLine)
+ strlen(auxSDPLine)
+ strlen(trackId());
@@ -103,6 +113,7 @@ PassiveServerMediaSubsession::sdpLines() {
ttl, // c= TTL
estBitrate, // b=AS:<bandwidth>
rtpmapLine, // a=rtpmap:... (if present)
+ rtcpmuxLine, // a=rtcp-mux:... (if present)
rangeLine, // a=range:... (if present)
auxSDPLine, // optional extra SDP line
trackId()); // a=control:<track-id>
@@ -196,6 +207,13 @@ float PassiveServerMediaSubsession::getCurrentNPT(void* streamToken) {
return (float)(timeNow.tv_sec - creationTime.tv_sec + (timeNow.tv_usec - creationTime.tv_usec)/1000000.0);
}
+void PassiveServerMediaSubsession
+::getRTPSinkandRTCP(void* streamToken,
+ RTPSink const*& rtpSink, RTCPInstance const*& rtcp) {
+ rtpSink = &fRTPSink;
+ rtcp = fRTCPInstance;
+}
+
void PassiveServerMediaSubsession::deleteStream(unsigned clientSessionId, void*& /*streamToken*/) {
// Lookup and remove the 'RTCPSourceRecord' for this client. Also turn off RTCP "RR" handling:
RTCPSourceRecord* source = (RTCPSourceRecord*)(fClientRTCPSourceRecords->Lookup((char const*)clientSessionId));
diff --git a/liveMedia/ProxyServerMediaSession.cpp b/liveMedia/ProxyServerMediaSession.cpp
index 15cd6ac..f1f78f8 100644
--- a/liveMedia/ProxyServerMediaSession.cpp
+++ b/liveMedia/ProxyServerMediaSession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A subclass of "ServerMediaSession" that can be used to create a (unicast) RTSP servers that acts as a 'proxy' for
// another (unicast or multicast) RTSP/RTP stream.
// Implementation
@@ -31,10 +31,11 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
class ProxyServerMediaSubsession: public OnDemandServerMediaSubsession {
public:
- ProxyServerMediaSubsession(MediaSubsession& mediaSubsession);
+ ProxyServerMediaSubsession(MediaSubsession& mediaSubsession,
+ portNumBits initialPortNum, Boolean multiplexRTCPWithRTP);
virtual ~ProxyServerMediaSubsession();
- char const* codecName() const { return fClientMediaSubsession.codecName(); }
+ char const* codecName() const { return fCodecName; }
private: // redefined virtual functions
virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
@@ -43,6 +44,9 @@ private: // redefined virtual functions
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
unsigned char rtpPayloadTypeIfDynamic,
FramedSource* inputSource);
+ virtual Groupsock* createGroupsock(struct in_addr const& addr, Port port);
+ virtual RTCPInstance* createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
+ unsigned char const* cname, RTPSink* sink);
private:
static void subsessionByeHandler(void* clientData);
@@ -53,6 +57,7 @@ private:
private:
friend class ProxyRTSPClient;
MediaSubsession& fClientMediaSubsession; // the 'client' media subsession object that corresponds to this 'server' media subsession
+ char const* fCodecName; // copied from "fClientMediaSubsession" once it's been set up
ProxyServerMediaSubsession* fNext; // used when we're part of a queue
Boolean fHaveSetupStream;
};
@@ -75,27 +80,33 @@ defaultCreateNewProxyRTSPClientFunc(ProxyServerMediaSession& ourServerMediaSessi
}
ProxyServerMediaSession* ProxyServerMediaSession
-::createNew(UsageEnvironment& env, RTSPServer* ourRTSPServer,
+::createNew(UsageEnvironment& env, GenericMediaServer* ourMediaServer,
char const* inputStreamURL, char const* streamName,
char const* username, char const* password,
- portNumBits tunnelOverHTTPPortNum, int verbosityLevel, int socketNumToServer) {
- return new ProxyServerMediaSession(env, ourRTSPServer, inputStreamURL, streamName, username, password,
- tunnelOverHTTPPortNum, verbosityLevel, socketNumToServer);
+ portNumBits tunnelOverHTTPPortNum, int verbosityLevel, int socketNumToServer,
+ MediaTranscodingTable* transcodingTable) {
+ return new ProxyServerMediaSession(env, ourMediaServer, inputStreamURL, streamName, username, password,
+ tunnelOverHTTPPortNum, verbosityLevel, socketNumToServer,
+ transcodingTable);
}
ProxyServerMediaSession
-::ProxyServerMediaSession(UsageEnvironment& env, RTSPServer* ourRTSPServer,
+::ProxyServerMediaSession(UsageEnvironment& env, GenericMediaServer* ourMediaServer,
char const* inputStreamURL, char const* streamName,
char const* username, char const* password,
portNumBits tunnelOverHTTPPortNum, int verbosityLevel,
int socketNumToServer,
- createNewProxyRTSPClientFunc* ourCreateNewProxyRTSPClientFunc)
+ MediaTranscodingTable* transcodingTable,
+ createNewProxyRTSPClientFunc* ourCreateNewProxyRTSPClientFunc,
+ portNumBits initialPortNum, Boolean multiplexRTCPWithRTP)
: ServerMediaSession(env, streamName, NULL, NULL, False, NULL),
- describeCompletedFlag(0), fOurRTSPServer(ourRTSPServer), fClientMediaSession(NULL),
+ describeCompletedFlag(0), fOurMediaServer(ourMediaServer), fClientMediaSession(NULL),
fVerbosityLevel(verbosityLevel),
fPresentationTimeSessionNormalizer(new PresentationTimeSessionNormalizer(envir())),
- fCreateNewProxyRTSPClientFunc(ourCreateNewProxyRTSPClientFunc) {
+ fCreateNewProxyRTSPClientFunc(ourCreateNewProxyRTSPClientFunc),
+ fTranscodingTable(transcodingTable),
+ fInitialPortNum(initialPortNum), fMultiplexRTCPWithRTP(multiplexRTCPWithRTP) {
// Open a RTSP connection to the input stream, and send a "DESCRIBE" command.
// We'll use the SDP description in the response to set ourselves up.
fProxyRTSPClient
@@ -112,18 +123,33 @@ ProxyServerMediaSession::~ProxyServerMediaSession() {
}
// Begin by sending a "TEARDOWN" command (without checking for a response):
- if (fProxyRTSPClient != NULL) fProxyRTSPClient->sendTeardownCommand(*fClientMediaSession, NULL, fProxyRTSPClient->auth());
+ if (fProxyRTSPClient != NULL && fClientMediaSession != NULL) {
+ fProxyRTSPClient->sendTeardownCommand(*fClientMediaSession, NULL, fProxyRTSPClient->auth());
+ }
// Then delete our state:
+ Medium::close(fTranscodingTable);
Medium::close(fClientMediaSession);
Medium::close(fProxyRTSPClient);
- delete fPresentationTimeSessionNormalizer;
+ Medium::close(fPresentationTimeSessionNormalizer);
}
char const* ProxyServerMediaSession::url() const {
return fProxyRTSPClient == NULL ? NULL : fProxyRTSPClient->url();
}
+Groupsock* ProxyServerMediaSession::createGroupsock(struct in_addr const& addr, Port port) {
+ // Default implementation; may be redefined by subclasses:
+ return new Groupsock(envir(), addr, port, 255);
+}
+
+RTCPInstance* ProxyServerMediaSession
+::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
+ unsigned char const* cname, RTPSink* sink) {
+ // Default implementation; may be redefined by subclasses:
+ return RTCPInstance::createNew(envir(), RTCPgs, totSessionBW, cname, sink, NULL/*we're a server*/);
+}
+
void ProxyServerMediaSession::continueAfterDESCRIBE(char const* sdpDescription) {
describeCompletedFlag = 1;
@@ -135,7 +161,8 @@ void ProxyServerMediaSession::continueAfterDESCRIBE(char const* sdpDescription)
MediaSubsessionIterator iter(*fClientMediaSession);
for (MediaSubsession* mss = iter.next(); mss != NULL; mss = iter.next()) {
- ServerMediaSubsession* smss = new ProxyServerMediaSubsession(*mss);
+ ServerMediaSubsession* smss
+ = new ProxyServerMediaSubsession(*mss, fInitialPortNum, fMultiplexRTCPWithRTP);
addSubsession(smss);
if (fVerbosityLevel > 0) {
envir() << *this << " added new \"ProxyServerMediaSubsession\" for "
@@ -147,9 +174,9 @@ void ProxyServerMediaSession::continueAfterDESCRIBE(char const* sdpDescription)
void ProxyServerMediaSession::resetDESCRIBEState() {
// Delete all of our "ProxyServerMediaSubsession"s; they'll get set up again once we get a response to the new "DESCRIBE".
- if (fOurRTSPServer != NULL) {
- // First, close any RTSP client connections that may have already been set up:
- fOurRTSPServer->closeAllClientSessionsForServerMediaSession(this);
+ if (fOurMediaServer != NULL) {
+ // First, close any client connections that may have already been set up:
+ fOurMediaServer->closeAllClientSessionsForServerMediaSession(this);
}
deleteAllSubsessions();
@@ -174,9 +201,12 @@ static void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char*
}
static void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) {
- if (resultCode == 0) {
- ((ProxyRTSPClient*)rtspClient)->continueAfterSETUP();
- }
+ ((ProxyRTSPClient*)rtspClient)->continueAfterSETUP(resultCode);
+ delete[] resultString;
+}
+
+static void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) {
+ ((ProxyRTSPClient*)rtspClient)->continueAfterPLAY(resultCode);
delete[] resultString;
}
@@ -185,8 +215,8 @@ static void continueAfterOPTIONS(RTSPClient* rtspClient, int resultCode, char* r
if (resultCode == 0) {
// Note whether the server told us that it supports the "GET_PARAMETER" command:
serverSupportsGetParameter = RTSPOptionIsSupported("GET_PARAMETER", resultString);
- }
- ((ProxyRTSPClient*)rtspClient)->continueAfterLivenessCommand(resultCode, serverSupportsGetParameter);
+ }
+ ((ProxyRTSPClient*)rtspClient)->continueAfterLivenessCommand(resultCode, serverSupportsGetParameter);
delete[] resultString;
}
@@ -211,7 +241,7 @@ ProxyRTSPClient::ProxyRTSPClient(ProxyServerMediaSession& ourServerMediaSession,
tunnelOverHTTPPortNum == (portNumBits)(~0) ? 0 : tunnelOverHTTPPortNum, socketNumToServer),
fOurServerMediaSession(ourServerMediaSession), fOurURL(strDup(rtspURL)), fStreamRTPOverTCP(tunnelOverHTTPPortNum != 0),
fSetupQueueHead(NULL), fSetupQueueTail(NULL), fNumSetupsDone(0), fNextDESCRIBEDelay(1),
- fServerSupportsGetParameter(False), fLastCommandWasPLAY(False),
+ fServerSupportsGetParameter(False), fLastCommandWasPLAY(False), fResetOnNextLivenessTest(False),
fLivenessCommandTask(NULL), fDESCRIBECommandTask(NULL), fSubsessionTimerTask(NULL) {
if (username != NULL && password != NULL) {
fOurAuthenticator = new Authenticator(username, password);
@@ -258,6 +288,11 @@ void ProxyRTSPClient::continueAfterDESCRIBE(char const* sdpDescription) {
}
void ProxyRTSPClient::continueAfterLivenessCommand(int resultCode, Boolean serverSupportsGetParameter) {
+ if (fResetOnNextLivenessTest) {
+ // Hack: We've arranged to reset the connection with the server (regardless of "resultCode"):
+ fResetOnNextLivenessTest = False;
+ resultCode = 2; // arbitrary > 0
+ }
if (resultCode != 0) {
// The periodic 'liveness' command failed, suggesting that the back-end stream is no longer alive.
// We handle this by resetting our connection state with this server. Any current clients will be closed, but
@@ -290,12 +325,21 @@ void ProxyRTSPClient::continueAfterLivenessCommand(int resultCode, Boolean serve
#define SUBSESSION_TIMEOUT_SECONDS 10 // how many seconds to wait for the last track's "SETUP" to be done (note below)
-void ProxyRTSPClient::continueAfterSETUP() {
+void ProxyRTSPClient::continueAfterSETUP(int resultCode) {
+ if (resultCode != 0) {
+ // The "SETUP" command failed, so arrange to reset the state after the next RTSP 'liveness'
+ // command. (We don't do this now, because it deletes the "ProxyServerMediaSubsession",
+ // and we can't do that during "ProxyServerMediaSubsession::createNewStreamSource()".)
+ fResetOnNextLivenessTest = True;
+ envir().taskScheduler().rescheduleDelayedTask(fLivenessCommandTask, 0, sendLivenessCommand, this);
+ return;
+ }
+
if (fVerbosityLevel > 0) {
- envir() << *this << "::continueAfterSETUP(): head codec: " << fSetupQueueHead->fClientMediaSubsession.codecName()
+ envir() << *this << "::continueAfterSETUP(): head codec: " << fSetupQueueHead->codecName()
<< "; numSubsessions " << fSetupQueueHead->fParentSession->numSubsessions() << "\n\tqueue:";
for (ProxyServerMediaSubsession* p = fSetupQueueHead; p != NULL; p = p->fNext) {
- envir() << "\t" << p->fClientMediaSubsession.codecName();
+ envir() << "\t" << p->codecName();
}
envir() << "\n";
}
@@ -317,7 +361,7 @@ void ProxyRTSPClient::continueAfterSETUP() {
if (fNumSetupsDone >= smss->fParentSession->numSubsessions()) {
// We've now finished setting up each of our subsessions (i.e., 'tracks').
// Continue by sending a "PLAY" command (an 'aggregate' "PLAY" command, on the whole session):
- sendPlayCommand(smss->fClientMediaSubsession.parentSession(), NULL, -1.0f, -1.0f, 1.0f, fOurAuthenticator);
+ sendPlayCommand(smss->fClientMediaSubsession.parentSession(), ::continueAfterPLAY, -1.0f, -1.0f, 1.0f, fOurAuthenticator);
// the "-1.0f" "start" parameter causes the "PLAY" to be sent without a "Range:" header, in case we'd already done
// a "PLAY" before (as a result of a 'subsession timeout' (note below))
fLastCommandWasPLAY = True;
@@ -332,6 +376,17 @@ void ProxyRTSPClient::continueAfterSETUP() {
}
}
+void ProxyRTSPClient::continueAfterPLAY(int resultCode) {
+ if (resultCode != 0) {
+ // The "PLAY" command failed, so arrange to reset the state after the next RTSP 'liveness'
+ // command. (We don't do this now, because it deletes the "ProxyServerMediaSubsession",
+ // and we can't do that during "ProxyServerMediaSubsession::createNewStreamSource()".)
+ fResetOnNextLivenessTest = True;
+ envir().taskScheduler().rescheduleDelayedTask(fLivenessCommandTask, 0, sendLivenessCommand, this);
+ return;
+ }
+}
+
void ProxyRTSPClient::scheduleLivenessCommand() {
// Delay a random time before sending another 'liveness' command.
unsigned delayMax = sessionTimeoutParameter(); // if the server specified a maximum time between 'liveness' probes, then use that
@@ -398,16 +453,20 @@ void ProxyRTSPClient::subsessionTimeout(void* clientData) {
void ProxyRTSPClient::handleSubsessionTimeout() {
// We still have one or more subsessions ('tracks') left to "SETUP". But we can't wait any longer for them. Send a "PLAY" now:
MediaSession* sess = fOurServerMediaSession.fClientMediaSession;
- if (sess != NULL) sendPlayCommand(*sess, NULL, -1.0f, -1.0f, 1.0f, fOurAuthenticator);
+ if (sess != NULL) sendPlayCommand(*sess, ::continueAfterPLAY, -1.0f, -1.0f, 1.0f, fOurAuthenticator);
fLastCommandWasPLAY = True;
}
//////// "ProxyServerMediaSubsession" implementation //////////
-ProxyServerMediaSubsession::ProxyServerMediaSubsession(MediaSubsession& mediaSubsession)
- : OnDemandServerMediaSubsession(mediaSubsession.parentSession().envir(), True/*reuseFirstSource*/),
- fClientMediaSubsession(mediaSubsession), fNext(NULL), fHaveSetupStream(False) {
+ProxyServerMediaSubsession
+::ProxyServerMediaSubsession(MediaSubsession& mediaSubsession,
+ portNumBits initialPortNum, Boolean multiplexRTCPWithRTP)
+ : OnDemandServerMediaSubsession(mediaSubsession.parentSession().envir(), True/*reuseFirstSource*/,
+ initialPortNum, multiplexRTCPWithRTP),
+ fClientMediaSubsession(mediaSubsession), fCodecName(strDup(mediaSubsession.codecName())),
+ fNext(NULL), fHaveSetupStream(False) {
}
UsageEnvironment& operator<<(UsageEnvironment& env, const ProxyServerMediaSubsession& psmss) { // used for debugging
@@ -418,6 +477,8 @@ ProxyServerMediaSubsession::~ProxyServerMediaSubsession() {
if (verbosityLevel() > 0) {
envir() << *this << "::~ProxyServerMediaSubsession()\n";
}
+
+ delete[] (char*)fCodecName;
}
FramedSource* ProxyServerMediaSubsession::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
@@ -437,26 +498,45 @@ FramedSource* ProxyServerMediaSubsession::createNewStreamSource(unsigned clientS
}
if (fClientMediaSubsession.readSource() != NULL) {
- // Add to the front of all data sources a filter that will 'normalize' their frames' presentation times,
- // before the frames get re-transmitted by our server:
- char const* const codecName = fClientMediaSubsession.codecName();
+ // First, check whether we have defined a 'transcoder' filter to be used with this codec:
+ if (sms->fTranscodingTable != NULL) {
+ char* outputCodecName;
+ FramedFilter* transcoder
+ = sms->fTranscodingTable->lookupTranscoder(fClientMediaSubsession, outputCodecName);
+ if (transcoder != NULL) {
+ fClientMediaSubsession.addFilter(transcoder);
+ delete[] (char*)fCodecName; fCodecName = outputCodecName;
+ }
+ }
+
+ // Then, add to the front of all data sources a filter that will 'normalize' their frames'
+ // presentation times, before the frames get re-transmitted by our server:
FramedFilter* normalizerFilter = sms->fPresentationTimeSessionNormalizer
- ->createNewPresentationTimeSubsessionNormalizer(fClientMediaSubsession.readSource(), fClientMediaSubsession.rtpSource(),
- codecName);
+ ->createNewPresentationTimeSubsessionNormalizer(fClientMediaSubsession.readSource(),
+ fClientMediaSubsession.rtpSource(),
+ fCodecName);
fClientMediaSubsession.addFilter(normalizerFilter);
- // Some data sources require a 'framer' object to be added, before they can be fed into a "RTPSink". Adjust for this now:
- if (strcmp(codecName, "H264") == 0) {
- fClientMediaSubsession.addFilter(H264VideoStreamDiscreteFramer::createNew(envir(), fClientMediaSubsession.readSource()));
- } else if (strcmp(codecName, "MP4V-ES") == 0) {
+ // Some data sources require a 'framer' object to be added, before they can be fed into
+ // a "RTPSink". Adjust for this now:
+ if (strcmp(fCodecName, "H264") == 0) {
+ fClientMediaSubsession.addFilter(H264VideoStreamDiscreteFramer
+ ::createNew(envir(), fClientMediaSubsession.readSource()));
+ } else if (strcmp(fCodecName, "H265") == 0) {
+ fClientMediaSubsession.addFilter(H265VideoStreamDiscreteFramer
+ ::createNew(envir(), fClientMediaSubsession.readSource()));
+ } else if (strcmp(fCodecName, "MP4V-ES") == 0) {
fClientMediaSubsession.addFilter(MPEG4VideoStreamDiscreteFramer
- ::createNew(envir(), fClientMediaSubsession.readSource(), True/* leave PTs unmodified*/));
- } else if (strcmp(codecName, "MPV") == 0) {
- fClientMediaSubsession.addFilter(MPEG1or2VideoStreamDiscreteFramer::createNew(envir(), fClientMediaSubsession.readSource(),
- False, 5.0, True/* leave PTs unmodified*/));
- } else if (strcmp(codecName, "DV") == 0) {
- fClientMediaSubsession.addFilter(DVVideoStreamFramer::createNew(envir(), fClientMediaSubsession.readSource(),
- False, True/* leave PTs unmodified*/));
+ ::createNew(envir(), fClientMediaSubsession.readSource(),
+ True/* leave PTs unmodified*/));
+ } else if (strcmp(fCodecName, "MPV") == 0) {
+ fClientMediaSubsession.addFilter(MPEG1or2VideoStreamDiscreteFramer
+ ::createNew(envir(), fClientMediaSubsession.readSource(),
+ False, 5.0, True/* leave PTs unmodified*/));
+ } else if (strcmp(fCodecName, "DV") == 0) {
+ fClientMediaSubsession.addFilter(DVVideoStreamFramer
+ ::createNew(envir(), fClientMediaSubsession.readSource(),
+ False, True/* leave PTs unmodified*/));
}
}
@@ -493,7 +573,7 @@ FramedSource* ProxyServerMediaSubsession::createNewStreamSource(unsigned clientS
// have been called here), so we know that the substream was previously "PAUSE"d. Send "PLAY" downstream once again,
// to resume the stream:
if (!proxyRTSPClient->fLastCommandWasPLAY) { // so that we send only one "PLAY"; not one for each subsession
- proxyRTSPClient->sendPlayCommand(fClientMediaSubsession.parentSession(), NULL, -1.0f/*resume from previous point*/,
+ proxyRTSPClient->sendPlayCommand(fClientMediaSubsession.parentSession(), ::continueAfterPLAY, -1.0f/*resume from previous point*/,
-1.0f, 1.0f, proxyRTSPClient->auth());
proxyRTSPClient->fLastCommandWasPLAY = True;
}
@@ -530,70 +610,85 @@ RTPSink* ProxyServerMediaSubsession
}
// Create (and return) the appropriate "RTPSink" object for our codec:
+ // (Note: The configuration string might not be correct if a transcoder is used. FIX!) #####
RTPSink* newSink;
- char const* const codecName = fClientMediaSubsession.codecName();
- if (strcmp(codecName, "AC3") == 0 || strcmp(codecName, "EAC3") == 0) {
+ if (strcmp(fCodecName, "AC3") == 0 || strcmp(fCodecName, "EAC3") == 0) {
newSink = AC3AudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
fClientMediaSubsession.rtpTimestampFrequency());
#if 0 // This code does not work; do *not* enable it:
- } else if (strcmp(codecName, "AMR") == 0 || strcmp(codecName, "AMR-WB") == 0) {
- Boolean isWideband = strcmp(codecName, "AMR-WB") == 0;
+ } else if (strcmp(fCodecName, "AMR") == 0 || strcmp(fCodecName, "AMR-WB") == 0) {
+ Boolean isWideband = strcmp(fCodecName, "AMR-WB") == 0;
newSink = AMRAudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
isWideband, fClientMediaSubsession.numChannels());
#endif
- } else if (strcmp(codecName, "DV") == 0) {
+ } else if (strcmp(fCodecName, "DV") == 0) {
newSink = DVVideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
- } else if (strcmp(codecName, "GSM") == 0) {
+ } else if (strcmp(fCodecName, "GSM") == 0) {
newSink = GSMAudioRTPSink::createNew(envir(), rtpGroupsock);
- } else if (strcmp(codecName, "H263-1998") == 0 || strcmp(codecName, "H263-2000") == 0) {
+ } else if (strcmp(fCodecName, "H263-1998") == 0 || strcmp(fCodecName, "H263-2000") == 0) {
newSink = H263plusVideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
fClientMediaSubsession.rtpTimestampFrequency());
- } else if (strcmp(codecName, "H264") == 0) {
+ } else if (strcmp(fCodecName, "H264") == 0) {
newSink = H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
fClientMediaSubsession.fmtp_spropparametersets());
- } else if (strcmp(codecName, "JPEG") == 0) {
+ } else if (strcmp(fCodecName, "H265") == 0) {
+ newSink = H265VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ fClientMediaSubsession.fmtp_spropvps(),
+ fClientMediaSubsession.fmtp_spropsps(),
+ fClientMediaSubsession.fmtp_sproppps());
+ } else if (strcmp(fCodecName, "JPEG") == 0) {
newSink = SimpleRTPSink::createNew(envir(), rtpGroupsock, 26, 90000, "video", "JPEG",
1/*numChannels*/, False/*allowMultipleFramesPerPacket*/, False/*doNormalMBitRule*/);
- } else if (strcmp(codecName, "MP4A-LATM") == 0) {
+ } else if (strcmp(fCodecName, "MP4A-LATM") == 0) {
newSink = MPEG4LATMAudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
fClientMediaSubsession.rtpTimestampFrequency(),
fClientMediaSubsession.fmtp_config(),
fClientMediaSubsession.numChannels());
- } else if (strcmp(codecName, "MP4V-ES") == 0) {
+ } else if (strcmp(fCodecName, "MP4V-ES") == 0) {
newSink = MPEG4ESVideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
fClientMediaSubsession.rtpTimestampFrequency(),
- fClientMediaSubsession.fmtp_profile_level_id(), fClientMediaSubsession.fmtp_config());
- } else if (strcmp(codecName, "MPA") == 0) {
+ fClientMediaSubsession.attrVal_unsigned("profile-level-id"),
+ fClientMediaSubsession.fmtp_config());
+ } else if (strcmp(fCodecName, "MPA") == 0) {
newSink = MPEG1or2AudioRTPSink::createNew(envir(), rtpGroupsock);
- } else if (strcmp(codecName, "MPA-ROBUST") == 0) {
+ } else if (strcmp(fCodecName, "MPA-ROBUST") == 0) {
newSink = MP3ADURTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
- } else if (strcmp(codecName, "MPEG4-GENERIC") == 0) {
+ } else if (strcmp(fCodecName, "MPEG4-GENERIC") == 0) {
newSink = MPEG4GenericRTPSink::createNew(envir(), rtpGroupsock,
rtpPayloadTypeIfDynamic, fClientMediaSubsession.rtpTimestampFrequency(),
- fClientMediaSubsession.mediumName(), fClientMediaSubsession.fmtp_mode(),
+ fClientMediaSubsession.mediumName(),
+ fClientMediaSubsession.attrVal_str("mode"),
fClientMediaSubsession.fmtp_config(), fClientMediaSubsession.numChannels());
- } else if (strcmp(codecName, "MPV") == 0) {
+ } else if (strcmp(fCodecName, "MPV") == 0) {
newSink = MPEG1or2VideoRTPSink::createNew(envir(), rtpGroupsock);
- } else if (strcmp(codecName, "T140") == 0) {
+ } else if (strcmp(fCodecName, "OPUS") == 0) {
+ newSink = SimpleRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ 48000, "audio", "OPUS", 2, False/*only 1 Opus 'packet' in each RTP packet*/);
+ } else if (strcmp(fCodecName, "T140") == 0) {
newSink = T140TextRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
- } else if (strcmp(codecName, "VORBIS") == 0) {
+ } else if (strcmp(fCodecName, "THEORA") == 0) {
+ newSink = TheoraVideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
+ fClientMediaSubsession.fmtp_config());
+ } else if (strcmp(fCodecName, "VORBIS") == 0) {
newSink = VorbisAudioRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic,
fClientMediaSubsession.rtpTimestampFrequency(), fClientMediaSubsession.numChannels(),
fClientMediaSubsession.fmtp_config());
- } else if (strcmp(codecName, "VP8") == 0) {
+ } else if (strcmp(fCodecName, "VP8") == 0) {
newSink = VP8VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
- } else if (strcmp(codecName, "AMR") == 0 || strcmp(codecName, "AMR-WB") == 0) {
+ } else if (strcmp(fCodecName, "VP9") == 0) {
+ newSink = VP9VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
+ } else if (strcmp(fCodecName, "AMR") == 0 || strcmp(fCodecName, "AMR-WB") == 0) {
// Proxying of these codecs is currently *not* supported, because the data received by the "RTPSource" object is not in a
// form that can be fed directly into a corresponding "RTPSink" object.
if (verbosityLevel() > 0) {
envir() << "\treturns NULL (because we currently don't support the proxying of \""
- << fClientMediaSubsession.mediumName() << "/" << codecName << "\" streams)\n";
+ << fClientMediaSubsession.mediumName() << "/" << fCodecName << "\" streams)\n";
}
return NULL;
- } else if (strcmp(codecName, "QCELP") == 0 ||
- strcmp(codecName, "H261") == 0 ||
- strcmp(codecName, "H263-1998") == 0 || strcmp(codecName, "H263-2000") == 0 ||
- strcmp(codecName, "X-QT") == 0 || strcmp(codecName, "X-QUICKTIME") == 0) {
+ } else if (strcmp(fCodecName, "QCELP") == 0 ||
+ strcmp(fCodecName, "H261") == 0 ||
+ strcmp(fCodecName, "H263-1998") == 0 || strcmp(fCodecName, "H263-2000") == 0 ||
+ strcmp(fCodecName, "X-QT") == 0 || strcmp(fCodecName, "X-QUICKTIME") == 0) {
// This codec requires a specialized RTP payload format; however, we don't yet have an appropriate "RTPSink" subclass for it:
if (verbosityLevel() > 0) {
envir() << "\treturns NULL (because we don't have a \"RTPSink\" subclass for this RTP payload format)\n";
@@ -604,12 +699,12 @@ RTPSink* ProxyServerMediaSubsession
Boolean allowMultipleFramesPerPacket = True; // by default
Boolean doNormalMBitRule = True; // by default
// Some codecs change the above default parameters:
- if (strcmp(codecName, "MP2T") == 0) {
+ if (strcmp(fCodecName, "MP2T") == 0) {
doNormalMBitRule = False; // no RTP 'M' bit
}
newSink = SimpleRTPSink::createNew(envir(), rtpGroupsock,
rtpPayloadTypeIfDynamic, fClientMediaSubsession.rtpTimestampFrequency(),
- fClientMediaSubsession.mediumName(), fClientMediaSubsession.codecName(),
+ fClientMediaSubsession.mediumName(), fCodecName,
fClientMediaSubsession.numChannels(), allowMultipleFramesPerPacket, doNormalMBitRule);
}
@@ -619,10 +714,11 @@ RTPSink* ProxyServerMediaSubsession
// Also tell our "PresentationTimeSubsessionNormalizer" object about the "RTPSink", so it can enable RTCP "SR" reports later:
PresentationTimeSubsessionNormalizer* ssNormalizer;
- if (strcmp(codecName, "H264") == 0 ||
- strcmp(codecName, "MP4V-ES") == 0 ||
- strcmp(codecName, "MPV") == 0 ||
- strcmp(codecName, "DV") == 0) {
+ if (strcmp(fCodecName, "H264") == 0 ||
+ strcmp(fCodecName, "H265") == 0 ||
+ strcmp(fCodecName, "MP4V-ES") == 0 ||
+ strcmp(fCodecName, "MPV") == 0 ||
+ strcmp(fCodecName, "DV") == 0) {
// There was a separate 'framer' object in front of the "PresentationTimeSubsessionNormalizer", so go back one object to get it:
ssNormalizer = (PresentationTimeSubsessionNormalizer*)(((FramedFilter*)inputSource)->inputSource());
} else {
@@ -633,6 +729,18 @@ RTPSink* ProxyServerMediaSubsession
return newSink;
}
+Groupsock* ProxyServerMediaSubsession::createGroupsock(struct in_addr const& addr, Port port) {
+ ProxyServerMediaSession* parentSession = (ProxyServerMediaSession*)fParentSession;
+ return parentSession->createGroupsock(addr, port);
+}
+
+RTCPInstance* ProxyServerMediaSubsession
+::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
+ unsigned char const* cname, RTPSink* sink) {
+ ProxyServerMediaSession* parentSession = (ProxyServerMediaSession*)fParentSession;
+ return parentSession->createRTCP(RTCPgs, totSessionBW, cname, sink);
+}
+
void ProxyServerMediaSubsession::subsessionByeHandler(void* clientData) {
((ProxyServerMediaSubsession*)clientData)->subsessionByeHandler();
}
@@ -644,7 +752,9 @@ void ProxyServerMediaSubsession::subsessionByeHandler() {
// This "BYE" signals that our input source has (effectively) closed, so pass this onto the front-end clients:
fHaveSetupStream = False; // hack to stop "PAUSE" getting sent by:
- FramedSource::handleClosure(fClientMediaSubsession.readSource());
+ if (fClientMediaSubsession.readSource() != NULL) {
+ fClientMediaSubsession.readSource()->handleClosure();
+ }
// And then treat this as if we had lost connection to the back-end server,
// and can reestablish streaming from it only by sending another "DESCRIBE":
@@ -665,20 +775,21 @@ PresentationTimeSessionNormalizer::PresentationTimeSessionNormalizer(UsageEnviro
PresentationTimeSessionNormalizer::~PresentationTimeSessionNormalizer() {
while (fSubsessionNormalizers != NULL) {
- delete fSubsessionNormalizers;
+ Medium::close(fSubsessionNormalizers);
}
}
-PresentationTimeSubsessionNormalizer*
-PresentationTimeSessionNormalizer::createNewPresentationTimeSubsessionNormalizer(FramedSource* inputSource, RTPSource* rtpSource,
- char const* codecName) {
+PresentationTimeSubsessionNormalizer* PresentationTimeSessionNormalizer
+::createNewPresentationTimeSubsessionNormalizer(FramedSource* inputSource, RTPSource* rtpSource,
+ char const* codecName) {
fSubsessionNormalizers
= new PresentationTimeSubsessionNormalizer(*this, inputSource, rtpSource, codecName, fSubsessionNormalizers);
return fSubsessionNormalizers;
}
-void PresentationTimeSessionNormalizer::normalizePresentationTime(PresentationTimeSubsessionNormalizer* ssNormalizer,
- struct timeval& toPT, struct timeval const& fromPT) {
+void PresentationTimeSessionNormalizer
+::normalizePresentationTime(PresentationTimeSubsessionNormalizer* ssNormalizer,
+ struct timeval& toPT, struct timeval const& fromPT) {
Boolean const hasBeenSynced = ssNormalizer->fRTPSource->hasBeenSynchronizedUsingRTCP();
if (!hasBeenSynced) {
diff --git a/liveMedia/QCELPAudioRTPSource.cpp b/liveMedia/QCELPAudioRTPSource.cpp
index 051d298..b99cff9 100644
--- a/liveMedia/QCELPAudioRTPSource.cpp
+++ b/liveMedia/QCELPAudioRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Qualcomm "PureVoice" (aka. "QCELP") Audio RTP Sources
// Implementation
diff --git a/liveMedia/QuickTimeFileSink.cpp b/liveMedia/QuickTimeFileSink.cpp
index e54f446..d2eaf9e 100644
--- a/liveMedia/QuickTimeFileSink.cpp
+++ b/liveMedia/QuickTimeFileSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A sink that generates a QuickTime file from a composite media session
// Implementation
@@ -318,7 +318,7 @@ QuickTimeFileSink::~QuickTimeFileSink() {
MediaSubsessionIterator iter(fInputSession);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
- subsession->readSource()->stopGettingFrames();
+ if (subsession->readSource() != NULL) subsession->readSource()->stopGettingFrames();
SubsessionIOState* ioState
= (SubsessionIOState*)(subsession->miscPtr);
@@ -355,6 +355,12 @@ QuickTimeFileSink::createNew(UsageEnvironment& env,
return newSink;
}
+void QuickTimeFileSink
+::noteRecordedFrame(MediaSubsession& /*inputSubsession*/,
+ unsigned /*packetDataSize*/, struct timeval const& /*presentationTime*/) {
+ // Default implementation: Do nothing
+}
+
Boolean QuickTimeFileSink::startPlaying(afterPlayingFunc* afterFunc,
void* afterClientData) {
// Make sure we're not already being played:
@@ -717,6 +723,8 @@ void SubsessionIOState::afterGettingFrame(unsigned packetDataSize,
fLastPacketRTPSeqNum = rtpSeqNum;
// Now, continue working with the frame that we just got
+ fOurSink.noteRecordedFrame(fOurSubsession, packetDataSize, presentationTime);
+
if (fBuffer->bytesInUse() == 0) {
fBuffer->setPresentationTime(presentationTime);
}
@@ -1036,8 +1044,8 @@ void SubsessionIOState::useFrameForHinting(unsigned frameSize,
}
} else if (hackm4a_generic) {
// Synthesize a special header, so that this frame can be in its own RTP packet.
- unsigned const sizeLength = fOurSubsession.fmtp_sizelength();
- unsigned const indexLength = fOurSubsession.fmtp_indexlength();
+ unsigned const sizeLength = fOurSubsession.attrVal_unsigned("sizelength");
+ unsigned const indexLength = fOurSubsession.attrVal_unsigned("indexlength");
if (sizeLength + indexLength != 16) {
envir() << "Warning: unexpected 'sizeLength' " << sizeLength
<< " and 'indexLength' " << indexLength
@@ -2010,13 +2018,20 @@ addAtom(stss); // Sync-Sample
unsigned numEntries = 0, numSamplesSoFar = 0;
if (fCurrentIOState->fHeadSyncFrame != NULL) {
SyncFrame* currentSyncFrame = fCurrentIOState->fHeadSyncFrame;
- while(currentSyncFrame != NULL) {
+
+ // First, count the number of frames (to use as a sanity check; see below):
+ unsigned totNumFrames = 0;
+ for (ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk; chunk != NULL; chunk = chunk->fNextChunk) totNumFrames += chunk->fNumFrames;
+
+ while (currentSyncFrame != NULL) {
+ if (currentSyncFrame->sfFrameNum >= totNumFrames) break; // sanity check
+
++numEntries;
size += addWord(currentSyncFrame->sfFrameNum);
currentSyncFrame = currentSyncFrame->nextSyncFrame;
}
} else {
- // Then, run through the chunk descriptors, counting up the total nuber of samples:
+ // First, run through the chunk descriptors, counting up the total number of samples:
unsigned const samplesPerFrame = fCurrentIOState->fQTSamplesPerFrame;
ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk;
while (chunk != NULL) {
diff --git a/liveMedia/QuickTimeGenericRTPSource.cpp b/liveMedia/QuickTimeGenericRTPSource.cpp
index 01a2b42..e6386ae 100644
--- a/liveMedia/QuickTimeGenericRTPSource.cpp
+++ b/liveMedia/QuickTimeGenericRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP Sources containing generic QuickTime stream data, as defined in
// <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
// Implementation
diff --git a/liveMedia/RTCP.cpp b/liveMedia/RTCP.cpp
index acf69c3..2559181 100644
--- a/liveMedia/RTCP.cpp
+++ b/liveMedia/RTCP.cpp
@@ -14,13 +14,16 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTCP
// Implementation
#include "RTCP.hh"
#include "GroupsockHelper.hh"
#include "rtcp_from_spec.h"
+#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
+#define snprintf _snprintf
+#endif
////////// RTCPMemberDatabase //////////
@@ -99,7 +102,7 @@ void RTCPMemberDatabase::reapOldMembers(unsigned threshold) {
#ifdef DEBUG
fprintf(stderr, "reap: removing SSRC 0x%x\n", oldSSRC);
#endif
- fOurRTCPInstance.removeSSRC(oldSSRC, True);
+ fOurRTCPInstance.removeSSRC(oldSSRC, True);
}
} while (foundOldMember);
}
@@ -113,14 +116,14 @@ static double dTimeNow() {
return (double) (timeNow.tv_sec + timeNow.tv_usec/1000000.0);
}
-static unsigned const maxRTCPPacketSize = 1450;
+static unsigned const maxRTCPPacketSize = 1456;
// bytes (1500, minus some allowance for IP, UDP, UMTP headers)
-static unsigned const preferredPacketSize = 1000; // bytes
+static unsigned const preferredRTCPPacketSize = 1000; // bytes
RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs,
unsigned totSessionBW,
unsigned char const* cname,
- RTPSink* sink, RTPSource const* source,
+ RTPSink* sink, RTPSource* source,
Boolean isSSMSource)
: Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW),
fSink(sink), fSource(source), fIsSSMSource(isSSMSource),
@@ -132,7 +135,8 @@ RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs,
fByeHandlerTask(NULL), fByeHandlerClientData(NULL),
fSRHandlerTask(NULL), fSRHandlerClientData(NULL),
fRRHandlerTask(NULL), fRRHandlerClientData(NULL),
- fSpecificRRHandlerTable(NULL) {
+ fSpecificRRHandlerTable(NULL),
+ fAppHandlerTask(NULL), fAppHandlerClientData(NULL) {
#ifdef DEBUG
fprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n", this);
#endif
@@ -151,17 +155,19 @@ RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs,
if (fKnownMembers == NULL || fInBuf == NULL) return;
fNumBytesAlreadyRead = 0;
- // A hack to save buffer space, because RTCP packets are always small:
- unsigned savedMaxSize = OutPacketBuffer::maxSize;
- OutPacketBuffer::maxSize = maxRTCPPacketSize;
- fOutBuf = new OutPacketBuffer(preferredPacketSize, maxRTCPPacketSize);
- OutPacketBuffer::maxSize = savedMaxSize;
+ fOutBuf = new OutPacketBuffer(preferredRTCPPacketSize, maxRTCPPacketSize, maxRTCPPacketSize);
if (fOutBuf == NULL) return;
- // Arrange to handle incoming reports from others:
- TaskScheduler::BackgroundHandlerProc* handler
- = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler;
- fRTCPInterface.startNetworkReading(handler);
+ if (fSource != NULL && fSource->RTPgs() == RTCPgs) {
+ // We're receiving RTCP reports that are multiplexed with RTP, so ask the RTP source
+ // to give them to us:
+ fSource->registerForMultiplexedRTCPPackets(this);
+ } else {
+ // Arrange to handle incoming reports from the network:
+ TaskScheduler::BackgroundHandlerProc* handler
+ = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler;
+ fRTCPInterface.startNetworkReading(handler);
+ }
// Send our first report.
fTypeOfEvent = EVENT_REPORT;
@@ -182,6 +188,14 @@ RTCPInstance::~RTCPInstance() {
fTypeOfEvent = EVENT_BYE; // not used, but...
sendBYE();
+ if (fSource != NULL && fSource->RTPgs() == fRTCPInterface.gs()) {
+ // We were receiving RTCP reports that were multiplexed with RTP, so tell the RTP source
+ // to stop giving them to us:
+ fSource->deregisterForMultiplexedRTCPPackets();
+ fRTCPInterface.forgetOurGroupsock();
+ // so that the "fRTCPInterface" destructor doesn't turn off background read handling
+ }
+
if (fSpecificRRHandlerTable != NULL) {
AddressPortLookupTable::Iterator iter(*fSpecificRRHandlerTable);
RRHandlerRecord* rrHandler;
@@ -196,10 +210,42 @@ RTCPInstance::~RTCPInstance() {
delete[] fInBuf;
}
+void RTCPInstance::noteArrivingRR(struct sockaddr_in const& fromAddressAndPort,
+ int tcpSocketNum, unsigned char tcpStreamChannelId) {
+ // If a 'RR handler' was set, call it now:
+
+ // Specific RR handler:
+ if (fSpecificRRHandlerTable != NULL) {
+ netAddressBits fromAddr;
+ portNumBits fromPortNum;
+ if (tcpSocketNum < 0) {
+ // Normal case: We read the RTCP packet over UDP
+ fromAddr = fromAddressAndPort.sin_addr.s_addr;
+ fromPortNum = ntohs(fromAddressAndPort.sin_port);
+ } else {
+ // Special case: We read the RTCP packet over TCP (interleaved)
+ // Hack: Use the TCP socket and channel id to look up the handler
+ fromAddr = tcpSocketNum;
+ fromPortNum = tcpStreamChannelId;
+ }
+ Port fromPort(fromPortNum);
+ RRHandlerRecord* rrHandler
+ = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort));
+ if (rrHandler != NULL) {
+ if (rrHandler->rrHandlerTask != NULL) {
+ (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData);
+ }
+ }
+ }
+
+ // General RR handler:
+ if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData);
+}
+
RTCPInstance* RTCPInstance::createNew(UsageEnvironment& env, Groupsock* RTCPgs,
unsigned totSessionBW,
unsigned char const* cname,
- RTPSink* sink, RTPSource const* source,
+ RTPSink* sink, RTPSource* source,
Boolean isSSMSource) {
return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source,
isSSMSource);
@@ -280,6 +326,46 @@ void RTCPInstance
}
}
+void RTCPInstance::setAppHandler(RTCPAppHandlerFunc* handlerTask, void* clientData) {
+ fAppHandlerTask = handlerTask;
+ fAppHandlerClientData = clientData;
+}
+
+void RTCPInstance::sendAppPacket(u_int8_t subtype, char const* name,
+ u_int8_t* appDependentData, unsigned appDependentDataSize) {
+ // Set up the first 4 bytes: V,PT,subtype,PT,length:
+ u_int32_t rtcpHdr = 0x80000000; // version 2, no padding
+ rtcpHdr |= (subtype&0x1F)<<24;
+ rtcpHdr |= (RTCP_PT_APP<<16);
+ unsigned length = 2 + (appDependentDataSize+3)/4;
+ rtcpHdr |= (length&0xFFFF);
+ fOutBuf->enqueueWord(rtcpHdr);
+
+ // Set up the next 4 bytes: SSRC:
+ fOutBuf->enqueueWord(fSource != NULL ? fSource->SSRC() : fSink != NULL ? fSink->SSRC() : 0);
+
+ // Set up the next 4 bytes: name:
+ char nameBytes[4];
+ nameBytes[0] = nameBytes[1] = nameBytes[2] = nameBytes[3] = '\0'; // by default
+ if (name != NULL) {
+ snprintf(nameBytes, 4, "%s", name);
+ }
+ fOutBuf->enqueue((u_int8_t*)nameBytes, 4);
+
+ // Set up the remaining bytes (if any): application-dependent data (+ padding):
+ if (appDependentData != NULL && appDependentDataSize > 0) {
+ fOutBuf->enqueue(appDependentData, appDependentDataSize);
+
+ unsigned modulo = appDependentDataSize%4;
+ unsigned paddingSize = modulo == 0 ? 0 : 4-modulo;
+ u_int8_t const paddingByte = 0x00;
+ for (unsigned i = 0; i < paddingSize; ++i) fOutBuf->enqueue(&paddingByte, 1);
+ }
+
+ // Finally, send the packet:
+ sendBuiltPacket();
+}
+
void RTCPInstance::setStreamSocket(int sockNum,
unsigned char streamChannelId) {
// Turn off background read handling:
@@ -308,6 +394,14 @@ void RTCPInstance::addStreamSocket(int sockNum,
fRTCPInterface.startNetworkReading(handler);
}
+void RTCPInstance
+::injectReport(u_int8_t const* packet, unsigned packetSize, struct sockaddr_in const& fromAddress) {
+ if (packetSize > maxRTCPPacketSize) packetSize = maxRTCPPacketSize;
+ memmove(fInBuf, packet, packetSize);
+
+ processIncomingReport(packetSize, fromAddress, -1, 0xFF); // assume report received over UDP
+}
+
static unsigned const IP_UDP_HDR_SIZE = 28;
// overhead (bytes) of IP and UDP hdrs
@@ -320,20 +414,23 @@ void RTCPInstance::incomingReportHandler(RTCPInstance* instance,
void RTCPInstance::incomingReportHandler1() {
do {
- Boolean callByeHandler = False;
- int tcpReadStreamSocketNum = fRTCPInterface.nextTCPReadStreamSocketNum();
- unsigned char tcpReadStreamChannelId = fRTCPInterface.nextTCPReadStreamChannelId();
- unsigned packetSize = 0;
- unsigned numBytesRead;
- struct sockaddr_in fromAddress;
- Boolean packetReadWasIncomplete;
if (fNumBytesAlreadyRead >= maxRTCPPacketSize) {
envir() << "RTCPInstance error: Hit limit when reading incoming packet over TCP. Increase \"maxRTCPPacketSize\"\n";
break;
}
+
+ unsigned numBytesRead;
+ struct sockaddr_in fromAddress;
+ int tcpSocketNum;
+ unsigned char tcpStreamChannelId;
+ Boolean packetReadWasIncomplete;
Boolean readResult
= fRTCPInterface.handleRead(&fInBuf[fNumBytesAlreadyRead], maxRTCPPacketSize - fNumBytesAlreadyRead,
- numBytesRead, fromAddress, packetReadWasIncomplete);
+ numBytesRead, fromAddress,
+ tcpSocketNum, tcpStreamChannelId,
+ packetReadWasIncomplete);
+
+ unsigned packetSize = 0;
if (packetReadWasIncomplete) {
fNumBytesAlreadyRead += numBytesRead;
return; // more reads are needed to get the entire packet
@@ -359,35 +456,50 @@ void RTCPInstance::incomingReportHandler1() {
}
}
- unsigned char* pkt = fInBuf;
if (fIsSSMSource && !packetWasFromOurHost) {
- // This packet is assumed to have been received via unicast (because we're a SSM source, and SSM receivers send back RTCP "RR"
- // packets via unicast). 'Reflect' the packet by resending it to the multicast group, so that any other receivers can also
- // get to see it.
+ // This packet is assumed to have been received via unicast (because we're a SSM source,
+ // and SSM receivers send back RTCP "RR" packets via unicast).
+ // 'Reflect' the packet by resending it to the multicast group, so that any other receivers
+ // can also get to see it.
// NOTE: Denial-of-service attacks are possible here.
// Users of this software may wish to add their own,
// application-specific mechanism for 'authenticating' the
// validity of this packet before reflecting it.
- // NOTE: The test for "!packetWasFromOurHost" means that we won't reflect RTCP packets that come from other processes on
- // the same host as us. The reason for this is that the 'packet size' test above is not 100% reliable; some packets
- // that were truly looped back from us might not be detected as such, and this might lead to infinite forwarding/receiving
- // of some packets. To avoid this possibility, we only reflect RTCP packets that we know for sure originated elsewhere.
- // (Note, though, that if we ever re-enable the code in "Groupsock::multicastSendOnly()", then we could remove the test for
- // "!packetWasFromOurHost".)
- fRTCPInterface.sendPacket(pkt, packetSize);
+ // NOTE: The test for "!packetWasFromOurHost" means that we won't reflect RTCP packets
+ // that come from other processes on the same host as us. The reason for this is that the
+ // 'packet size' test above is not 100% reliable; some packets that were truly looped back
+ // from us might not be detected as such, and this might lead to infinite
+ // forwarding/receiving of some packets. To avoid this possibility, we reflect only
+ // RTCP packets that we know for sure originated elsewhere.
+ // (Note, though, that if we ever re-enable the code in "Groupsock::multicastSendOnly()",
+ // then we could remove the test for "!packetWasFromOurHost".)
+ fRTCPInterface.sendPacket(fInBuf, packetSize);
fHaveJustSentPacket = True;
fLastPacketSentSize = packetSize;
}
+ processIncomingReport(packetSize, fromAddress, tcpSocketNum, tcpStreamChannelId);
+ } while (0);
+}
+
+void RTCPInstance
+::processIncomingReport(unsigned packetSize, struct sockaddr_in const& fromAddressAndPort,
+ int tcpSocketNum, unsigned char tcpStreamChannelId) {
+ do {
+ Boolean callByeHandler = False;
+ unsigned char* pkt = fInBuf;
+
#ifdef DEBUG
- fprintf(stderr, "[%p]saw incoming RTCP packet", this);
- if (tcpReadStreamSocketNum < 0) {
- // Note that "fromAddress" is valid only if we're receiving over UDP (not over TCP):
- fprintf(stderr, " (from address %s, port %d)", AddressString(fromAddress).val(), ntohs(fromAddress.sin_port));
+ fprintf(stderr, "[%p]saw incoming RTCP packet (from ", this);
+ if (tcpSocketNum < 0) {
+ // Note that "fromAddressAndPort" is valid only if we're receiving over UDP (not over TCP):
+ fprintf(stderr, "address %s, port %d", AddressString(fromAddressAndPort).val(), ntohs(fromAddressAndPort.sin_port));
+ } else {
+ fprintf(stderr, "TCP socket #%d, stream channel id %d", tcpSocketNum, tcpStreamChannelId);
}
- fprintf(stderr, "\n");
+ fprintf(stderr, ")\n");
for (unsigned i = 0; i < packetSize; ++i) {
if (i%4 == 0) fprintf(stderr, " ");
fprintf(stderr, "%02x", pkt[i]);
@@ -399,10 +511,11 @@ void RTCPInstance::incomingReportHandler1() {
// Check the RTCP packet for validity:
// It must at least contain a header (4 bytes), and this header
// must be version=2, with no padding bit, and a payload type of
- // SR (200) or RR (201):
+ // SR (200), RR (201), or APP (204):
if (packetSize < 4) break;
unsigned rtcpHdr = ntohl(*(u_int32_t*)pkt);
- if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR<<16))) {
+ if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR<<16)) &&
+ (rtcpHdr & 0xE0FF0000) != (0x80000000 | (RTCP_PT_APP<<16))) {
#ifdef DEBUG
fprintf(stderr, "rejected bad RTCP packet: header 0x%08x\n", rtcpHdr);
#endif
@@ -415,8 +528,8 @@ void RTCPInstance::incomingReportHandler1() {
unsigned reportSenderSSRC = 0;
Boolean packetOK = False;
while (1) {
- unsigned rc = (rtcpHdr>>24)&0x1F;
- unsigned pt = (rtcpHdr>>16)&0xFF;
+ u_int8_t rc = (rtcpHdr>>24)&0x1F;
+ u_int8_t pt = (rtcpHdr>>16)&0xFF;
unsigned length = 4*(rtcpHdr&0xFFFF); // doesn't count hdr
ADVANCE(4); // skip over the header
if (length > packetSize) break;
@@ -424,6 +537,15 @@ void RTCPInstance::incomingReportHandler1() {
// Assume that each RTCP subpacket begins with a 4-byte SSRC:
if (length < 4) break; length -= 4;
reportSenderSSRC = ntohl(*(u_int32_t*)pkt); ADVANCE(4);
+#ifdef HACK_FOR_CHROME_WEBRTC_BUG
+ if (reportSenderSSRC == 0x00000001 && pt == RTCP_PT_RR) {
+ // Chrome (and Opera) WebRTC receivers have a bug that causes them to always send
+ // SSRC 1 in their "RR"s. To work around this (to help us distinguish between different
+ // receivers), we use a fake SSRC in this case consisting of the IP address, XORed with
+ // the port number:
+ reportSenderSSRC = fromAddressAndPort.sin_addr.s_addr^fromAddressAndPort.sin_port;
+ }
+#endif
Boolean subPacketOK = False;
switch (pt) {
@@ -470,7 +592,7 @@ void RTCPInstance::incomingReportHandler1() {
unsigned jitter = ntohl(*(u_int32_t*)pkt); ADVANCE(4);
unsigned timeLastSR = ntohl(*(u_int32_t*)pkt); ADVANCE(4);
unsigned timeSinceLastSR = ntohl(*(u_int32_t*)pkt); ADVANCE(4);
- transmissionStats.noteIncomingRR(reportSenderSSRC, fromAddress,
+ transmissionStats.noteIncomingRR(reportSenderSSRC, fromAddressAndPort,
lossStats,
highestReceived, jitter,
timeLastSR, timeSinceLastSR);
@@ -483,34 +605,7 @@ void RTCPInstance::incomingReportHandler1() {
}
if (pt == RTCP_PT_RR) { // i.e., we didn't fall through from 'SR'
- // If a 'RR handler' was set, call it now:
-
- // Specific RR handler:
- if (fSpecificRRHandlerTable != NULL) {
- netAddressBits fromAddr;
- portNumBits fromPortNum;
- if (tcpReadStreamSocketNum < 0) {
- // Normal case: We read the RTCP packet over UDP
- fromAddr = fromAddress.sin_addr.s_addr;
- fromPortNum = ntohs(fromAddress.sin_port);
- } else {
- // Special case: We read the RTCP packet over TCP (interleaved)
- // Hack: Use the TCP socket and channel id to look up the handler
- fromAddr = tcpReadStreamSocketNum;
- fromPortNum = tcpReadStreamChannelId;
- }
- Port fromPort(fromPortNum);
- RRHandlerRecord* rrHandler
- = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort));
- if (rrHandler != NULL) {
- if (rrHandler->rrHandlerTask != NULL) {
- (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData);
- }
- }
- }
-
- // General RR handler:
- if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData);
+ noteArrivingRR(fromAddressAndPort, tcpSocketNum, tcpStreamChannelId);
}
subPacketOK = True;
@@ -538,20 +633,161 @@ void RTCPInstance::incomingReportHandler1() {
typeOfPacket = PACKET_BYE;
break;
}
- // Later handle SDES, APP, and compound RTCP packets #####
- default:
+ case RTCP_PT_APP: {
+ u_int8_t& subtype = rc; // In "APP" packets, the "rc" field gets used as "subtype"
+#ifdef DEBUG
+ fprintf(stderr, "APP (subtype 0x%02x)\n", subtype);
+#endif
+ if (length < 4) {
#ifdef DEBUG
- fprintf(stderr, "UNSUPPORTED TYPE(0x%x)\n", pt);
+ fprintf(stderr, "\tError: No \"name\" field!\n");
+#endif
+ break;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "\tname:%c%c%c%c\n", pkt[0], pkt[1], pkt[2], pkt[3]);
+#endif
+ u_int32_t nameBytes = (pkt[0]<<24)|(pkt[1]<<16)|(pkt[2]<<8)|(pkt[3]);
+ ADVANCE(4); // skip over "name", to the 'application-dependent data'
+#ifdef DEBUG
+ fprintf(stderr, "\tapplication-dependent data size: %d bytes\n", length);
+#endif
+
+ // If an 'APP' packet handler was set, call it now:
+ if (fAppHandlerTask != NULL) {
+ (*fAppHandlerTask)(fAppHandlerClientData, subtype, nameBytes, pkt, length);
+ }
+ subPacketOK = True;
+ typeOfPacket = PACKET_RTCP_APP;
+ break;
+ }
+ // Other RTCP packet types that we don't yet handle:
+ case RTCP_PT_SDES: {
+#ifdef DEBUG
+ // 'Handle' SDES packets only in debugging code, by printing out the 'SDES items':
+ fprintf(stderr, "SDES\n");
+
+ // Process each 'chunk':
+ Boolean chunkOK = False;
+ ADVANCE(-4); length += 4; // hack so that we see the first SSRC/CSRC again
+ while (length >= 8) { // A valid chunk must be at least 8 bytes long
+ chunkOK = False; // until we learn otherwise
+
+ u_int32_t SSRC_CSRC = ntohl(*(u_int32_t*)pkt); ADVANCE(4); length -= 4;
+ fprintf(stderr, "\tSSRC/CSRC: 0x%08x\n", SSRC_CSRC);
+
+ // Process each 'SDES item' in the chunk:
+ u_int8_t itemType = *pkt; ADVANCE(1); --length;
+ while (itemType != 0) {
+ unsigned itemLen = *pkt; ADVANCE(1); --length;
+ // Make sure "itemLen" allows for at least 1 zero byte at the end of the chunk:
+ if (itemLen + 1 > length || pkt[itemLen] != 0) break;
+
+ fprintf(stderr, "\t\t%s:%s\n",
+ itemType == 1 ? "CNAME" :
+ itemType == 2 ? "NAME" :
+ itemType == 3 ? "EMAIL" :
+ itemType == 4 ? "PHONE" :
+ itemType == 5 ? "LOC" :
+ itemType == 6 ? "TOOL" :
+ itemType == 7 ? "NOTE" :
+ itemType == 8 ? "PRIV" :
+ "(unknown)",
+ itemType < 8 ? (char*)pkt // hack, because we know it's '\0'-terminated
+ : "???"/* don't try to print out PRIV or unknown items */);
+ ADVANCE(itemLen); length -= itemLen;
+
+ itemType = *pkt; ADVANCE(1); --length;
+ }
+ if (itemType != 0) break; // bad 'SDES item'
+
+ // Thus, itemType == 0. This zero 'type' marks the end of the list of SDES items.
+ // Skip over remaining zero padding bytes, so that this chunk ends on a 4-byte boundary:
+ while (length%4 > 0 && *pkt == 0) { ADVANCE(1); --length; }
+ if (length%4 > 0) break; // Bad (non-zero) padding byte
+
+ chunkOK = True;
+ }
+ if (!chunkOK || length > 0) break; // bad chunk, or not enough bytes for the last chunk
#endif
subPacketOK = True;
break;
+ }
+ case RTCP_PT_RTPFB: {
+#ifdef DEBUG
+ fprintf(stderr, "RTPFB(unhandled)\n");
+#endif
+ subPacketOK = True;
+ break;
+ }
+ case RTCP_PT_PSFB: {
+#ifdef DEBUG
+ fprintf(stderr, "PSFB(unhandled)\n");
+ // Temporary code to show "Receiver Estimated Maximum Bitrate" (REMB) feedback reports:
+ //#####
+ if (length >= 12 && pkt[4] == 'R' && pkt[5] == 'E' && pkt[6] == 'M' && pkt[7] == 'B') {
+ u_int8_t exp = pkt[9]>>2;
+ u_int32_t mantissa = ((pkt[9]&0x03)<<16)|(pkt[10]<<8)|pkt[11];
+ double remb = (double)mantissa;
+ while (exp > 0) {
+ remb *= 2.0;
+ exp /= 2;
+ }
+ fprintf(stderr, "\tReceiver Estimated Max Bitrate (REMB): %g bps\n", remb);
+ }
+#endif
+ subPacketOK = True;
+ break;
+ }
+ case RTCP_PT_XR: {
+#ifdef DEBUG
+ fprintf(stderr, "XR(unhandled)\n");
+#endif
+ subPacketOK = True;
+ break;
+ }
+ case RTCP_PT_AVB: {
+#ifdef DEBUG
+ fprintf(stderr, "AVB(unhandled)\n");
+#endif
+ subPacketOK = True;
+ break;
+ }
+ case RTCP_PT_RSI: {
+#ifdef DEBUG
+ fprintf(stderr, "RSI(unhandled)\n");
+#endif
+ subPacketOK = True;
+ break;
+ }
+ case RTCP_PT_TOKEN: {
+#ifdef DEBUG
+ fprintf(stderr, "TOKEN(unhandled)\n");
+#endif
+ subPacketOK = True;
+ break;
+ }
+ case RTCP_PT_IDMS: {
+#ifdef DEBUG
+ fprintf(stderr, "IDMS(unhandled)\n");
+#endif
+ subPacketOK = True;
+ break;
+ }
+ default: {
+#ifdef DEBUG
+ fprintf(stderr, "UNKNOWN TYPE(0x%x)\n", pt);
+#endif
+ subPacketOK = True;
+ break;
+ }
}
if (!subPacketOK) break;
// need to check for (& handle) SSRC collision! #####
#ifdef DEBUG
- fprintf(stderr, "validated RTCP subpacket (type %d): %d, %d, %d, 0x%08x\n", typeOfPacket, rc, pt, length, reportSenderSSRC);
+ fprintf(stderr, "validated RTCP subpacket: rc:%d, pt:%d, bytes remaining:%d, report sender SSRC:0x%08x\n", rc, pt, length, reportSenderSSRC);
#endif
// Skip over any remaining bytes in this subpacket:
@@ -707,7 +943,8 @@ Boolean RTCPInstance::addReport(Boolean alwaysAdd) {
}
addSR();
- } else if (fSource != NULL) {
+ }
+ if (fSource != NULL) {
if (!alwaysAdd) {
if (!fSource->enableRTCPReports()) return False;
}
diff --git a/liveMedia/RTPInterface.cpp b/liveMedia/RTPInterface.cpp
index 3d88d55..212a551 100644
--- a/liveMedia/RTPInterface.cpp
+++ b/liveMedia/RTPInterface.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// An abstraction of a network interface used for RTP (or RTCP).
// (This allows the RTP-over-TCP hack (RFC 2326, section 10.12) to
// be implemented transparently.)
@@ -138,6 +138,7 @@ void RTPInterface::setStreamSocket(int sockNum,
fGS->removeAllDestinations();
envir().taskScheduler().disableBackgroundHandling(fGS->socketNum()); // turn off any reading on our datagram socket
fGS->reset(); // and close our datagram socket, because we won't be using it anymore
+
addStreamSocket(sockNum, streamChannelId);
}
@@ -171,19 +172,28 @@ static void deregisterSocket(UsageEnvironment& env, int sockNum, unsigned char s
void RTPInterface::removeStreamSocket(int sockNum,
unsigned char streamChannelId) {
- for (tcpStreamRecord** streamsPtr = &fTCPStreams; *streamsPtr != NULL;
- streamsPtr = &((*streamsPtr)->fNext)) {
- if ((*streamsPtr)->fStreamSocketNum == sockNum
- && (*streamsPtr)->fStreamChannelId == streamChannelId) {
- deregisterSocket(envir(), sockNum, streamChannelId);
-
- // Then remove the record pointed to by *streamsPtr :
- tcpStreamRecord* next = (*streamsPtr)->fNext;
- (*streamsPtr)->fNext = NULL;
- delete (*streamsPtr);
- *streamsPtr = next;
- return;
+ while (1) {
+ tcpStreamRecord** streamsPtr = &fTCPStreams;
+
+ while (*streamsPtr != NULL) {
+ if ((*streamsPtr)->fStreamSocketNum == sockNum
+ && (streamChannelId == 0xFF || streamChannelId == (*streamsPtr)->fStreamChannelId)) {
+ // Delete the record pointed to by *streamsPtr :
+ tcpStreamRecord* next = (*streamsPtr)->fNext;
+ (*streamsPtr)->fNext = NULL;
+ delete (*streamsPtr);
+ *streamsPtr = next;
+
+ // And 'deregister' this socket,channelId pair:
+ deregisterSocket(envir(), sockNum, streamChannelId);
+
+ if (streamChannelId != 0xFF) return; // we're done
+ break; // start again from the beginning of the list, in case the list has changed
+ } else {
+ streamsPtr = &((*streamsPtr)->fNext);
+ }
}
+ if (*streamsPtr == NULL) break;
}
}
@@ -202,13 +212,14 @@ Boolean RTPInterface::sendPacket(unsigned char* packet, unsigned packetSize) {
Boolean success = True; // we'll return False instead if any of the sends fail
// Normal case: Send as a UDP packet:
- if (!fGS->output(envir(), fGS->ttl(), packet, packetSize)) success = False;
+ if (!fGS->output(envir(), packet, packetSize)) success = False;
// Also, send over each of our TCP sockets:
- for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;
- streams = streams->fNext) {
+ tcpStreamRecord* nextStream;
+ for (tcpStreamRecord* stream = fTCPStreams; stream != NULL; stream = nextStream) {
+ nextStream = stream->fNext; // Set this now, in case the following deletes "stream":
if (!sendRTPorRTCPPacketOverTCP(packet, packetSize,
- streams->fStreamSocketNum, streams->fStreamChannelId)) {
+ stream->fStreamSocketNum, stream->fStreamChannelId)) {
success = False;
}
}
@@ -235,14 +246,20 @@ void RTPInterface
}
Boolean RTPInterface::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
- unsigned& bytesRead, struct sockaddr_in& fromAddress, Boolean& packetReadWasIncomplete) {
+ unsigned& bytesRead, struct sockaddr_in& fromAddress,
+ int& tcpSocketNum, unsigned char& tcpStreamChannelId,
+ Boolean& packetReadWasIncomplete) {
packetReadWasIncomplete = False; // by default
Boolean readSuccess;
if (fNextTCPReadStreamSocketNum < 0) {
// Normal case: read from the (datagram) 'groupsock':
+ tcpSocketNum = -1;
readSuccess = fGS->handleRead(buffer, bufferMaxSize, bytesRead, fromAddress);
} else {
// Read from the TCP connection:
+ tcpSocketNum = fNextTCPReadStreamSocketNum;
+ tcpStreamChannelId = fNextTCPReadStreamChannelId;
+
bytesRead = 0;
unsigned totBytesToRead = fNextTCPReadSize;
if (totBytesToRead > bufferMaxSize) totBytesToRead = bufferMaxSize;
@@ -280,7 +297,7 @@ Boolean RTPInterface::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
void RTPInterface::stopNetworkReading() {
// Normal case
- envir().taskScheduler().turnOffBackgroundReadHandling(fGS->socketNum());
+ if (fGS != NULL) envir().taskScheduler().turnOffBackgroundReadHandling(fGS->socketNum());
// Also turn off read handling on each of our TCP connections:
for (tcpStreamRecord* streams = fTCPStreams; streams != NULL; streams = streams->fNext) {
@@ -324,6 +341,10 @@ Boolean RTPInterface::sendRTPorRTCPPacketOverTCP(u_int8_t* packet, unsigned pack
return False;
}
+#ifndef RTPINTERFACE_BLOCKING_WRITE_TIMEOUT_MS
+#define RTPINTERFACE_BLOCKING_WRITE_TIMEOUT_MS 500
+#endif
+
Boolean RTPInterface::sendDataOverTCP(int socketNum, u_int8_t const* data, unsigned dataSize, Boolean forceSendToSucceed) {
int sendResult = send(socketNum, (char const*)data, dataSize, 0/*flags*/);
if (sendResult < (int)dataSize) {
@@ -338,11 +359,29 @@ Boolean RTPInterface::sendDataOverTCP(int socketNum, u_int8_t const* data, unsig
#ifdef DEBUG_SEND
fprintf(stderr, "sendDataOverTCP: resending %d-byte send (blocking)\n", numBytesRemainingToSend); fflush(stderr);
#endif
- makeSocketBlocking(socketNum);
+ makeSocketBlocking(socketNum, RTPINTERFACE_BLOCKING_WRITE_TIMEOUT_MS);
sendResult = send(socketNum, (char const*)(&data[numBytesSentSoFar]), numBytesRemainingToSend, 0/*flags*/);
+ if ((unsigned)sendResult != numBytesRemainingToSend) {
+ // The blocking "send()" failed, or timed out. In either case, we assume that the
+ // TCP connection has failed (or is 'hanging' indefinitely), and we stop using it
+ // (for both RTP and RTP).
+ // (If we kept using the socket here, the RTP or RTCP packet write would be in an
+ // incomplete, inconsistent state.)
+#ifdef DEBUG_SEND
+ fprintf(stderr, "sendDataOverTCP: blocking send() failed (delivering %d bytes out of %d); closing socket %d\n", sendResult, numBytesRemainingToSend, socketNum); fflush(stderr);
+#endif
+ removeStreamSocket(socketNum, 0xFF);
+ return False;
+ }
makeSocketNonBlocking(socketNum);
- return sendResult == (int)numBytesRemainingToSend;
+
+ return True;
+ } else if (sendResult < 0 && envir().getErrno() != EAGAIN) {
+ // Because the "send()" call failed, assume that the socket is now unusable, so stop
+ // using it (for both RTP and RTCP):
+ removeStreamSocket(socketNum, 0xFF);
}
+
return False;
}
@@ -420,7 +459,7 @@ void SocketDescriptor
#endif
fSubChannelHashTable->Remove((char const*)(long)streamChannelId);
- if (fSubChannelHashTable->IsEmpty()) {
+ if (fSubChannelHashTable->IsEmpty() || streamChannelId == 0xFF) {
// No more interfaces are using us, so it's curtains for us now:
if (fAreInReadHandlerLoop) {
fDeleteMyselfNext = True; // we can't delete ourself yet, but we'll do so from "tcpReadHandler()" below
diff --git a/liveMedia/RTPSink.cpp b/liveMedia/RTPSink.cpp
index 4646bf1..2d85a3b 100644
--- a/liveMedia/RTPSink.cpp
+++ b/liveMedia/RTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP Sinks
// Implementation
@@ -52,7 +52,7 @@ RTPSink::RTPSink(UsageEnvironment& env,
fRTPPayloadType(rtpPayloadType),
fPacketCount(0), fOctetCount(0), fTotalOctetCount(0),
fTimestampFrequency(rtpTimestampFrequency), fNextTimestampHasBeenPreset(False), fEnableRTCPReports(True),
- fNumChannels(numChannels) {
+ fNumChannels(numChannels), fEstimatedBitrate(0) {
fRTPPayloadFormatName
= strDup(rtpPayloadFormatName == NULL ? "???" : rtpPayloadFormatName);
gettimeofday(&fCreationTime, NULL);
@@ -69,6 +69,9 @@ RTPSink::RTPSink(UsageEnvironment& env,
RTPSink::~RTPSink() {
delete fTransmissionStatsDB;
delete[] (char*)fRTPPayloadFormatName;
+ fRTPInterface.forgetOurGroupsock();
+ // so that the "fRTCPInterface" destructor doesn't turn off background read handling (in case
+ // its 'groupsock' is being shared with something else that does background read handling).
}
u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) {
diff --git a/liveMedia/RTPSource.cpp b/liveMedia/RTPSource.cpp
index 64b21f8..1946d99 100644
--- a/liveMedia/RTPSource.cpp
+++ b/liveMedia/RTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP Sources
// Implementation
@@ -54,6 +54,7 @@ RTPSource::RTPSource(UsageEnvironment& env, Groupsock* RTPgs,
: FramedSource(env),
fRTPInterface(this, RTPgs),
fCurPacketHasBeenSynchronizedUsingRTCP(False), fLastReceivedSSRC(0),
+ fRTCPInstanceForMultiplexedRTCPPackets(NULL),
fRTPPayloadFormat(rtpPayloadFormat), fTimestampFrequency(rtpTimestampFrequency),
fSSRC(our_random32()), fEnableRTCPReports(True) {
fReceptionStatsDB = new RTPReceptionStatsDB();
diff --git a/liveMedia/RTSPClient.cpp b/liveMedia/RTSPClient.cpp
index e51c12c..3731783 100644
--- a/liveMedia/RTSPClient.cpp
+++ b/liveMedia/RTSPClient.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTSP client
// Implementation
@@ -37,7 +37,7 @@ RTSPClient* RTSPClient::createNew(UsageEnvironment& env, char const* rtspURL,
}
unsigned RTSPClient::sendDescribeCommand(responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "DESCRIBE", responseHandler));
}
@@ -47,7 +47,7 @@ unsigned RTSPClient::sendOptionsCommand(responseHandler* responseHandler, Authen
}
unsigned RTSPClient::sendAnnounceCommand(char const* sdpDescription, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "ANNOUNCE", responseHandler, NULL, NULL, False, 0.0, 0.0, 0.0, sdpDescription));
}
@@ -55,7 +55,7 @@ unsigned RTSPClient::sendSetupCommand(MediaSubsession& subsession, responseHandl
Boolean streamOutgoing, Boolean streamUsingTCP, Boolean forceMulticastOnUnspecified,
Authenticator* authenticator) {
if (fTunnelOverHTTPPortNum != 0) streamUsingTCP = True; // RTSP-over-HTTP tunneling uses TCP (by definition)
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
u_int32_t booleanFlags = 0;
if (streamUsingTCP) booleanFlags |= 0x1;
@@ -67,7 +67,7 @@ unsigned RTSPClient::sendSetupCommand(MediaSubsession& subsession, responseHandl
unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
double start, double end, float scale,
Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
sendDummyUDPPackets(session); // hack to improve NAT traversal
return sendRequest(new RequestRecord(++fCSeq, "PLAY", responseHandler, &session, NULL, 0, start, end, scale));
}
@@ -75,7 +75,7 @@ unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* res
unsigned RTSPClient::sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
double start, double end, float scale,
Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
sendDummyUDPPackets(subsession); // hack to improve NAT traversal
return sendRequest(new RequestRecord(++fCSeq, "PLAY", responseHandler, NULL, &subsession, 0, start, end, scale));
}
@@ -83,7 +83,7 @@ unsigned RTSPClient::sendPlayCommand(MediaSubsession& subsession, responseHandle
unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
char const* absStartTime, char const* absEndTime, float scale,
Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
sendDummyUDPPackets(session); // hack to improve NAT traversal
return sendRequest(new RequestRecord(++fCSeq, responseHandler, absStartTime, absEndTime, scale, &session, NULL));
}
@@ -91,45 +91,45 @@ unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* res
unsigned RTSPClient::sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
char const* absStartTime, char const* absEndTime, float scale,
Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
sendDummyUDPPackets(subsession); // hack to improve NAT traversal
return sendRequest(new RequestRecord(++fCSeq, responseHandler, absStartTime, absEndTime, scale, NULL, &subsession));
}
unsigned RTSPClient::sendPauseCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "PAUSE", responseHandler, &session));
}
unsigned RTSPClient::sendPauseCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "PAUSE", responseHandler, NULL, &subsession));
}
unsigned RTSPClient::sendRecordCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "RECORD", responseHandler, &session));
}
unsigned RTSPClient::sendRecordCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "RECORD", responseHandler, NULL, &subsession));
}
unsigned RTSPClient::sendTeardownCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "TEARDOWN", responseHandler, &session));
}
unsigned RTSPClient::sendTeardownCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
return sendRequest(new RequestRecord(++fCSeq, "TEARDOWN", responseHandler, NULL, &subsession));
}
unsigned RTSPClient::sendSetParameterCommand(MediaSession& session, responseHandler* responseHandler,
char const* parameterName, char const* parameterValue,
Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
char* paramString = new char[strlen(parameterName) + strlen(parameterValue) + 10];
sprintf(paramString, "%s: %s\r\n", parameterName, parameterValue);
unsigned result = sendRequest(new RequestRecord(++fCSeq, "SET_PARAMETER", responseHandler, &session, NULL, False, 0.0, 0.0, 0.0, paramString));
@@ -139,7 +139,7 @@ unsigned RTSPClient::sendSetParameterCommand(MediaSession& session, responseHand
unsigned RTSPClient::sendGetParameterCommand(MediaSession& session, responseHandler* responseHandler, char const* parameterName,
Authenticator* authenticator) {
- if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
+ if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator;
// We assume that:
// parameterName is NULL means: Send no body in the request.
@@ -175,8 +175,23 @@ void RTSPClient::sendDummyUDPPackets(MediaSubsession& subsession, unsigned numDu
if (subsession.rtcpInstance() != NULL) gs2 = subsession.rtcpInstance()->RTCPgs();
u_int32_t const dummy = 0xFEEDFACE;
for (unsigned i = 0; i < numDummyPackets; ++i) {
- if (gs1 != NULL) gs1->output(envir(), 255, (unsigned char*)&dummy, sizeof dummy);
- if (gs2 != NULL) gs2->output(envir(), 255, (unsigned char*)&dummy, sizeof dummy);
+ if (gs1 != NULL) gs1->output(envir(), (unsigned char*)&dummy, sizeof dummy);
+ if (gs2 != NULL) gs2->output(envir(), (unsigned char*)&dummy, sizeof dummy);
+ }
+}
+
+void RTSPClient::setSpeed(MediaSession& session, float speed) {
+ // Optionally set download speed for session to be used later on PLAY command:
+ // The user should call this function after the MediaSession is instantiated, but before the
+ // first "sendPlayCommand()" is called.
+ if (&session != NULL) {
+ session.speed() = speed;
+ MediaSubsessionIterator iter(session);
+ MediaSubsession* subsession;
+
+ while ((subsession = iter.next()) != NULL) {
+ subsession->speed() = speed;
+ }
}
}
@@ -210,6 +225,27 @@ Boolean RTSPClient::lookupByName(UsageEnvironment& env,
return True;
}
+static void copyUsernameOrPasswordStringFromURL(char* dest, char const* src, unsigned len) {
+ // Normally, we just copy from the source to the destination. However, if the source contains
+ // %-encoded characters, then we decode them while doing the copy:
+ while (len > 0) {
+ int nBefore = 0;
+ int nAfter = 0;
+
+ if (*src == '%' && len >= 3 && sscanf(src+1, "%n%2hhx%n", &nBefore, dest, &nAfter) == 1) {
+ unsigned codeSize = nAfter - nBefore; // should be 1 or 2
+
+ ++dest;
+ src += (1 + codeSize);
+ len -= (1 + codeSize);
+ } else {
+ *dest++ = *src++;
+ --len;
+ }
+ }
+ *dest = '\0';
+}
+
Boolean RTSPClient::parseRTSPURL(UsageEnvironment& env, char const* url,
char*& username, char*& password,
NetAddress& address,
@@ -243,15 +279,13 @@ Boolean RTSPClient::parseRTSPURL(UsageEnvironment& env, char const* url,
char const* usernameStart = from;
unsigned usernameLen = colonPasswordStart - usernameStart;
username = new char[usernameLen + 1] ; // allow for the trailing '\0'
- for (unsigned i = 0; i < usernameLen; ++i) username[i] = usernameStart[i];
- username[usernameLen] = '\0';
+ copyUsernameOrPasswordStringFromURL(username, usernameStart, usernameLen);
char const* passwordStart = colonPasswordStart;
if (passwordStart < p) ++passwordStart; // skip over the ':'
unsigned passwordLen = p - passwordStart;
password = new char[passwordLen + 1]; // allow for the trailing '\0'
- for (unsigned j = 0; j < passwordLen; ++j) password[j] = passwordStart[j];
- password[passwordLen] = '\0';
+ copyUsernameOrPasswordStringFromURL(password, passwordStart, passwordLen);
from = p + 1; // skip over the '@'
break;
@@ -325,8 +359,10 @@ RTSPClient::RTSPClient(UsageEnvironment& env, char const* rtspURL,
int verbosityLevel, char const* applicationName,
portNumBits tunnelOverHTTPPortNum, int socketNumToServer)
: Medium(env),
- fVerbosityLevel(verbosityLevel), fCSeq(1), fServerAddress(0),
- fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum), fUserAgentHeaderStr(NULL), fUserAgentHeaderStrLen(0),
+ desiredMaxIncomingPacketSize(0), fVerbosityLevel(verbosityLevel), fCSeq(1),
+ fAllowBasicAuthentication(True), fServerAddress(0),
+ fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum),
+ fUserAgentHeaderStr(NULL), fUserAgentHeaderStrLen(0),
fInputSocketNum(-1), fOutputSocketNum(-1), fBaseURL(NULL), fTCPStreamIdCount(0),
fLastSessionId(NULL), fSessionTimeoutParameter(0), fSessionCookieCounter(0), fHTTPTunnelingConnectionIsPending(False) {
setBaseURL(rtspURL);
@@ -539,6 +575,19 @@ static char* createSessionString(char const* sessionId) {
return sessionStr;
}
+// Add support for faster download thru "speed:" option on PLAY
+static char* createSpeedString(float speed) {
+ char buf[100];
+ if (speed == 1.0f ) {
+ // This is the default value; we don't need a "Speed:" header:
+ buf[0] = '\0';
+ } else {
+ sprintf(buf, "Speed: %.3f\r\n",speed);
+ }
+
+ return strDup(buf);
+}
+
static char* createScaleString(float scale, float currentScale) {
char buf[100];
if (scale == 1.0f && currentScale == 1.0f) {
@@ -638,14 +687,14 @@ Boolean RTSPClient::setRequestFields(RequestRecord* request,
Boolean requestMulticastStreaming
= IsMulticastAddress(connectionAddress) || (connectionAddress == 0 && forceMulticastOnUnspecified);
transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
- portTypeStr = ";client_port";
+ portTypeStr = requestMulticastStreaming ? ";port" : ";client_port";
rtpNumber = subsession.clientPortNum();
if (rtpNumber == 0) {
envir().setResultMsg("Client port number unknown\n");
delete[] cmdURL;
return False;
}
- rtcpNumber = rtpNumber + 1;
+ rtcpNumber = subsession.rtcpIsMuxed() ? rtpNumber : rtpNumber + 1;
}
unsigned transportSize = strlen(transportFmt)
+ strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */;
@@ -656,11 +705,15 @@ Boolean RTSPClient::setRequestFields(RequestRecord* request,
// When sending more than one "SETUP" request, include a "Session:" header in the 2nd and later commands:
char* sessionStr = createSessionString(fLastSessionId);
- // The "Transport:" and "Session:" (if present) headers make up the 'extra headers':
- extraHeaders = new char[transportSize + strlen(sessionStr)];
+ // Optionally include a "Blocksize:" string:
+ char* blocksizeStr = createBlocksizeString(streamUsingTCP);
+
+ // The "Transport:" and "Session:" (if present) and "Blocksize:" (if present) headers
+ // make up the 'extra headers':
+ extraHeaders = new char[transportSize + strlen(sessionStr) + strlen(blocksizeStr)];
extraHeadersWereAllocated = True;
- sprintf(extraHeaders, "%s%s", transportStr, sessionStr);
- delete[] transportStr; delete[] sessionStr;
+ sprintf(extraHeaders, "%s%s%s", transportStr, sessionStr, blocksizeStr);
+ delete[] transportStr; delete[] sessionStr; delete[] blocksizeStr;
} else if (strcmp(request->commandName(), "GET") == 0 || strcmp(request->commandName(), "POST") == 0) {
// We will be sending a HTTP (not a RTSP) request.
// Begin by re-parsing our RTSP URL, to get the stream name (which we'll use as our 'cmdURL'
@@ -750,14 +803,17 @@ Boolean RTSPClient::setRequestFields(RequestRecord* request,
}
if (strcmp(request->commandName(), "PLAY") == 0) {
- // Create "Session:", "Scale:", and "Range:" headers; these make up the 'extra headers':
+ // Create possible "Session:", "Scale:", "Speed:", and "Range:" headers;
+ // these make up the 'extra headers':
char* sessionStr = createSessionString(sessionId);
char* scaleStr = createScaleString(request->scale(), originalScale);
+ float speed = request->session() != NULL ? request->session()->speed() : request->subsession()->speed();
+ char* speedStr = createSpeedString(speed);
char* rangeStr = createRangeString(request->start(), request->end(), request->absStartTime(), request->absEndTime());
- extraHeaders = new char[strlen(sessionStr) + strlen(scaleStr) + strlen(rangeStr) + 1];
+ extraHeaders = new char[strlen(sessionStr) + strlen(scaleStr) + strlen(speedStr) + strlen(rangeStr) + 1];
extraHeadersWereAllocated = True;
- sprintf(extraHeaders, "%s%s%s", sessionStr, scaleStr, rangeStr);
- delete[] sessionStr; delete[] scaleStr; delete[] rangeStr;
+ sprintf(extraHeaders, "%s%s%s%s", sessionStr, scaleStr, speedStr, rangeStr);
+ delete[] sessionStr; delete[] scaleStr; delete[] speedStr; delete[] rangeStr;
} else {
// Create a "Session:" header; this makes up our 'extra headers':
extraHeaders = createSessionString(sessionId);
@@ -888,6 +944,28 @@ char* RTSPClient::createAuthenticatorString(char const* cmd, char const* url) {
return strDup("");
}
+char* RTSPClient::createBlocksizeString(Boolean streamUsingTCP) {
+ char* blocksizeStr;
+ u_int16_t maxPacketSize = desiredMaxIncomingPacketSize;
+
+ // Allow for the RTP header (if streaming over TCP)
+ // or the IP/UDP/RTP headers (if streaming over UDP):
+ u_int16_t const headerAllowance = streamUsingTCP ? 12 : 50/*conservative*/;
+ if (maxPacketSize < headerAllowance) {
+ maxPacketSize = 0;
+ } else {
+ maxPacketSize -= headerAllowance;
+ }
+
+ if (maxPacketSize > 0) {
+ blocksizeStr = new char[25]; // more than enough space
+ sprintf(blocksizeStr, "Blocksize: %u\r\n", maxPacketSize);
+ } else {
+ blocksizeStr = strDup("");
+ }
+ return blocksizeStr;
+}
+
void RTSPClient::handleRequestError(RequestRecord* request) {
int resultCode = -envir().getErrno();
if (resultCode == 0) {
@@ -1039,6 +1117,11 @@ Boolean RTSPClient::parseScaleParam(char const* paramStr, float& scale) {
return sscanf(paramStr, "%f", &scale) == 1;
}
+Boolean RTSPClient::parseSpeedParam(char const* paramStr, float& speed) {
+ Locale l("C", Numeric);
+ return sscanf(paramStr, "%f", &speed) >= 1;
+}
+
Boolean RTSPClient::parseRTPInfoParams(char const*& paramsStr, u_int16_t& seqNum, u_int32_t& timestamp) {
if (paramsStr == NULL || paramsStr[0] == '\0') return False;
while (paramsStr[0] == ',') ++paramsStr;
@@ -1125,15 +1208,22 @@ Boolean RTSPClient::handleSETUPResponse(MediaSubsession& subsession, char const*
}
Boolean RTSPClient::handlePLAYResponse(MediaSession& session, MediaSubsession& subsession,
- char const* scaleParamsStr, char const* rangeParamsStr, char const* rtpInfoParamsStr) {
- Boolean scaleOK = False, rangeOK = False;
+ char const* scaleParamsStr, char const* speedParamsStr,
+ char const* rangeParamsStr, char const* rtpInfoParamsStr) {
+ Boolean scaleOK = False, rangeOK = False, speedOK = False;
do {
if (&session != NULL) {
// The command was on the whole session
if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, session.scale())) break;
scaleOK = True;
- if (rangeParamsStr != NULL && !parseRangeParam(rangeParamsStr, session.playStartTime(), session.playEndTime(),
- session._absStartTime(), session._absEndTime())) break;
+ if (speedParamsStr != NULL && !parseSpeedParam(speedParamsStr, session.speed())) break;
+ speedOK = True;
+ Boolean startTimeIsNow;
+ if (rangeParamsStr != NULL &&
+ !parseRangeParam(rangeParamsStr,
+ session.playStartTime(), session.playEndTime(),
+ session._absStartTime(), session._absEndTime(),
+ startTimeIsNow)) break;
rangeOK = True;
MediaSubsessionIterator iter(session);
@@ -1153,8 +1243,14 @@ Boolean RTSPClient::handlePLAYResponse(MediaSession& session, MediaSubsession& s
// The command was on a subsession
if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, subsession.scale())) break;
scaleOK = True;
- if (rangeParamsStr != NULL && !parseRangeParam(rangeParamsStr, subsession._playStartTime(), subsession._playEndTime(),
- subsession._absStartTime(), subsession._absEndTime())) break;
+ if (speedParamsStr != NULL && !parseSpeedParam(speedParamsStr, session.speed())) break;
+ speedOK = True;
+ Boolean startTimeIsNow;
+ if (rangeParamsStr != NULL &&
+ !parseRangeParam(rangeParamsStr,
+ subsession._playStartTime(), subsession._playEndTime(),
+ subsession._absStartTime(), subsession._absEndTime(),
+ startTimeIsNow)) break;
rangeOK = True;
u_int16_t seqNum; u_int32_t timestamp;
@@ -1174,6 +1270,8 @@ Boolean RTSPClient::handlePLAYResponse(MediaSession& session, MediaSubsession& s
// An error occurred:
if (!scaleOK) {
envir().setResultMsg("Bad \"Scale:\" header");
+ } else if (!speedOK) {
+ envir().setResultMsg("Bad \"Speed:\" header");
} else if (!rangeOK) {
envir().setResultMsg("Bad \"Range:\" header");
} else {
@@ -1187,7 +1285,7 @@ Boolean RTSPClient::handleTEARDOWNResponse(MediaSession& /*session*/, MediaSubse
return True;
}
-Boolean RTSPClient::handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString) {
+Boolean RTSPClient::handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString, char* resultValueStringEnd) {
do {
// If "parameterName" is non-empty, it may be (possibly followed by ':' and whitespace) at the start of the result string:
if (parameterName != NULL && parameterName[0] != '\0') {
@@ -1196,15 +1294,26 @@ Boolean RTSPClient::handleGET_PARAMETERResponse(char const* parameterName, char*
unsigned parameterNameLen = strlen(parameterName);
// ASSERT: parameterNameLen >= 2;
parameterNameLen -= 2; // because of the trailing \r\n
+ if (resultValueString + parameterNameLen > resultValueStringEnd) break; // not enough space
if (_strncasecmp(resultValueString, parameterName, parameterNameLen) == 0) {
resultValueString += parameterNameLen;
+ // ASSERT: resultValueString <= resultValueStringEnd
+ if (resultValueString == resultValueStringEnd) break;
+
if (resultValueString[0] == ':') ++resultValueString;
- while (resultValueString[0] == ' ' || resultValueString[0] == '\t') ++resultValueString;
+ while (resultValueString < resultValueStringEnd
+ && (resultValueString[0] == ' ' || resultValueString[0] == '\t')) {
+ ++resultValueString;
+ }
}
}
// The rest of "resultValueStr" should be our desired result, but first trim off any \r and/or \n characters at the end:
+ char saved = *resultValueStringEnd;
+ *resultValueStringEnd = '\0';
unsigned resultLen = strlen(resultValueString);
+ *resultValueStringEnd = saved;
+
while (resultLen > 0 && (resultValueString[resultLen-1] == '\r' || resultValueString[resultLen-1] == '\n')) --resultLen;
resultValueString[resultLen] = '\0';
@@ -1220,23 +1329,34 @@ Boolean RTSPClient::handleAuthenticationFailure(char const* paramsStr) {
if (paramsStr == NULL) return False; // There was no "WWW-Authenticate:" header; we can't proceed.
// Fill in "fCurrentAuthenticator" with the information from the "WWW-Authenticate:" header:
- Boolean alreadyHadRealm = fCurrentAuthenticator.realm() != NULL;
+ Boolean realmHasChanged = False; // by default
+ Boolean isStale = False; // by default
char* realm = strDupSize(paramsStr);
char* nonce = strDupSize(paramsStr);
+ char* stale = strDupSize(paramsStr);
Boolean success = True;
- if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
+ if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) {
+ realmHasChanged = fCurrentAuthenticator.realm() == NULL || strcmp(fCurrentAuthenticator.realm(), realm) != 0;
+ isStale = _strncasecmp(stale, "true", 4) == 0;
+ fCurrentAuthenticator.setRealmAndNonce(realm, nonce);
+ } else if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
+ realmHasChanged = fCurrentAuthenticator.realm() == NULL || strcmp(fCurrentAuthenticator.realm(), realm) != 0;
fCurrentAuthenticator.setRealmAndNonce(realm, nonce);
- } else if (sscanf(paramsStr, "Basic realm=\"%[^\"]\"", realm) == 1) {
+ } else if (sscanf(paramsStr, "Basic realm=\"%[^\"]\"", realm) == 1 && fAllowBasicAuthentication) {
+ realmHasChanged = fCurrentAuthenticator.realm() == NULL || strcmp(fCurrentAuthenticator.realm(), realm) != 0;
fCurrentAuthenticator.setRealmAndNonce(realm, NULL); // Basic authentication
} else {
success = False; // bad "WWW-Authenticate:" header
}
- delete[] realm; delete[] nonce;
-
- if (alreadyHadRealm || fCurrentAuthenticator.username() == NULL || fCurrentAuthenticator.password() == NULL) {
- // We already had a 'realm', or don't have a username and/or password,
- // so the new "WWW-Authenticate:" header information won't help us. We remain unauthenticated.
- success = False;
+ delete[] realm; delete[] nonce; delete[] stale;
+
+ if (success) {
+ if ((!realmHasChanged && !isStale) || fCurrentAuthenticator.username() == NULL || fCurrentAuthenticator.password() == NULL) {
+ // We already tried with the same realm (and a non-stale nonce),
+ // or don't have a username and/or password, so the new "WWW-Authenticate:" header
+ // information won't help us. We remain unauthenticated.
+ success = False;
+ }
}
return success;
@@ -1521,6 +1641,7 @@ void RTSPClient::handleResponseBytes(int newBytesRead) {
char const* sessionParamsStr = NULL;
char const* transportParamsStr = NULL;
char const* scaleParamsStr = NULL;
+ char const* speedParamsStr = NULL;
char const* rangeParamsStr = NULL;
char const* rtpInfoParamsStr = NULL;
char const* wwwAuthenticateParamsStr = NULL;
@@ -1533,8 +1654,12 @@ void RTSPClient::handleResponseBytes(int newBytesRead) {
strncpy(headerDataCopy, fResponseBuffer, fResponseBytesAlreadySeen);
headerDataCopy[fResponseBytesAlreadySeen] = '\0';
- char* lineStart = headerDataCopy;
- char* nextLineStart = getLine(lineStart);
+ char* lineStart;
+ char* nextLineStart = headerDataCopy;
+ do {
+ lineStart = nextLineStart;
+ nextLineStart = getLine(lineStart);
+ } while (lineStart[0] == '\0' && nextLineStart != NULL); // skip over any blank lines at the start
if (!parseResponseCode(lineStart, responseCode, responseStr)) {
// This does not appear to be a RTSP response; perhaps it's a RTSP request instead?
handleIncomingRequest();
@@ -1590,6 +1715,7 @@ void RTSPClient::handleResponseBytes(int newBytesRead) {
} else if (checkForHeader(lineStart, "Session:", 8, sessionParamsStr)) {
} else if (checkForHeader(lineStart, "Transport:", 10, transportParamsStr)) {
} else if (checkForHeader(lineStart, "Scale:", 6, scaleParamsStr)) {
+ } else if (checkForHeader(lineStart, "Speed:", 6, speedParamsStr)) {
} else if (checkForHeader(lineStart, "Range:", 6, rangeParamsStr)) {
} else if (checkForHeader(lineStart, "RTP-Info:", 9, rtpInfoParamsStr)) {
} else if (checkForHeader(lineStart, "WWW-Authenticate:", 17, headerParamsStr)) {
@@ -1659,11 +1785,11 @@ void RTSPClient::handleResponseBytes(int newBytesRead) {
if (strcmp(foundRequest->commandName(), "SETUP") == 0) {
if (!handleSETUPResponse(*foundRequest->subsession(), sessionParamsStr, transportParamsStr, foundRequest->booleanFlags()&0x1)) break;
} else if (strcmp(foundRequest->commandName(), "PLAY") == 0) {
- if (!handlePLAYResponse(*foundRequest->session(), *foundRequest->subsession(), scaleParamsStr, rangeParamsStr, rtpInfoParamsStr)) break;
+ if (!handlePLAYResponse(*foundRequest->session(), *foundRequest->subsession(), scaleParamsStr, speedParamsStr, rangeParamsStr, rtpInfoParamsStr)) break;
} else if (strcmp(foundRequest->commandName(), "TEARDOWN") == 0) {
if (!handleTEARDOWNResponse(*foundRequest->session(), *foundRequest->subsession())) break;
} else if (strcmp(foundRequest->commandName(), "GET_PARAMETER") == 0) {
- if (!handleGET_PARAMETERResponse(foundRequest->contentStr(), bodyStart)) break;
+ if (!handleGET_PARAMETERResponse(foundRequest->contentStr(), bodyStart, responseEnd)) break;
}
} else if (responseCode == 401 && handleAuthenticationFailure(wwwAuthenticateParamsStr)) {
// We need to resend the command, with an "Authorization:" header:
@@ -1682,7 +1808,7 @@ void RTSPClient::handleResponseBytes(int newBytesRead) {
if (needToResendCommand) {
resetResponseBuffer();
- if (!resendCommand(foundRequest)) break;
+ (void)resendCommand(foundRequest);
delete[] headerDataCopy;
return; // without calling our response handler; the response to the resent command will do that
}
diff --git a/liveMedia/RTSPCommon.cpp b/liveMedia/RTSPCommon.cpp
index 25d371c..28898c9 100644
--- a/liveMedia/RTSPCommon.cpp
+++ b/liveMedia/RTSPCommon.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Common routines used by both RTSP clients and servers
// Implementation
@@ -25,12 +25,6 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include <ctype.h> // for "isxdigit()
#include <time.h> // for "strftime()" and "gmtime()"
-#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
-#else
-#include <signal.h>
-#define USE_SIGNALS 1
-#endif
-
static void decodeURL(char* url) {
// Replace (in place) any %<hex><hex> sequences with the appropriate 8-bit character.
char* cursor = url;
@@ -214,35 +208,42 @@ Boolean parseRTSPRequestString(char const* reqStr,
return True;
}
-Boolean parseRangeParam(char const* paramStr, double& rangeStart, double& rangeEnd, char*& absStartTime, char*& absEndTime) {
+Boolean parseRangeParam(char const* paramStr,
+ double& rangeStart, double& rangeEnd,
+ char*& absStartTime, char*& absEndTime,
+ Boolean& startTimeIsNow) {
delete[] absStartTime; delete[] absEndTime;
absStartTime = absEndTime = NULL; // by default, unless "paramStr" is a "clock=..." string
+ startTimeIsNow = False; // by default
double start, end;
- int numCharsMatched = 0;
+ int numCharsMatched1 = 0, numCharsMatched2 = 0, numCharsMatched3 = 0, numCharsMatched4 = 0;
Locale l("C", Numeric);
if (sscanf(paramStr, "npt = %lf - %lf", &start, &end) == 2) {
rangeStart = start;
rangeEnd = end;
- } else if (sscanf(paramStr, "npt = %lf -", &start) == 1) {
- if (start < 0.0) {
- // special case for "npt = -<endtime>", which seems to match here:
- rangeStart = 0.0;
+ } else if (sscanf(paramStr, "npt = %n%lf -", &numCharsMatched1, &start) == 1) {
+ if (paramStr[numCharsMatched1] == '-') {
+ // special case for "npt = -<endtime>", which matches here:
+ rangeStart = 0.0; startTimeIsNow = True;
rangeEnd = -start;
} else {
rangeStart = start;
rangeEnd = 0.0;
}
- } else if (strcmp(paramStr, "npt=now-") == 0) {
- rangeStart = 0.0;
+ } else if (sscanf(paramStr, "npt = now - %lf", &end) == 1) {
+ rangeStart = 0.0; startTimeIsNow = True;
+ rangeEnd = end;
+ } else if (sscanf(paramStr, "npt = now -%n", &numCharsMatched2) == 0 && numCharsMatched2 > 0) {
+ rangeStart = 0.0; startTimeIsNow = True;
rangeEnd = 0.0;
- } else if (sscanf(paramStr, "clock = %n", &numCharsMatched) == 0 && numCharsMatched > 0) {
+ } else if (sscanf(paramStr, "clock = %n", &numCharsMatched3) == 0 && numCharsMatched3 > 0) {
rangeStart = rangeEnd = 0.0;
- char const* utcTimes = ¶mStr[numCharsMatched];
+ char const* utcTimes = ¶mStr[numCharsMatched3];
size_t len = strlen(utcTimes) + 1;
char* as = new char[len];
char* ae = new char[len];
- int sscanfResult = sscanf(utcTimes, "%[^-]-%s", as, ae);
+ int sscanfResult = sscanf(utcTimes, "%[^-]-%[^\r\n]", as, ae);
if (sscanfResult == 2) {
absStartTime = as;
absEndTime = ae;
@@ -253,7 +254,7 @@ Boolean parseRangeParam(char const* paramStr, double& rangeStart, double& rangeE
delete[] as; delete[] ae;
return False;
}
- } else if (sscanf(paramStr, "smtpe = %n", &numCharsMatched) == 0 && numCharsMatched > 0) {
+ } else if (sscanf(paramStr, "smtpe = %n", &numCharsMatched4) == 0 && numCharsMatched4 > 0) {
// We accept "smtpe=" parameters, but currently do not interpret them.
} else {
return False; // The header is malformed
@@ -262,7 +263,10 @@ Boolean parseRangeParam(char const* paramStr, double& rangeStart, double& rangeE
return True;
}
-Boolean parseRangeHeader(char const* buf, double& rangeStart, double& rangeEnd, char*& absStartTime, char*& absEndTime) {
+Boolean parseRangeHeader(char const* buf,
+ double& rangeStart, double& rangeEnd,
+ char*& absStartTime, char*& absEndTime,
+ Boolean& startTimeIsNow) {
// First, find "Range:"
while (1) {
if (*buf == '\0') return False; // not found
@@ -270,10 +274,9 @@ Boolean parseRangeHeader(char const* buf, double& rangeStart, double& rangeEnd,
++buf;
}
- // Then, run through each of the fields, looking for ones we handle:
char const* fields = buf + 7;
while (*fields == ' ') ++fields;
- return parseRangeParam(fields, rangeStart, rangeEnd, absStartTime, absEndTime);
+ return parseRangeParam(fields, rangeStart, rangeEnd, absStartTime, absEndTime, startTimeIsNow);
}
Boolean parseScaleHeader(char const* buf, float& scale) {
@@ -287,7 +290,6 @@ Boolean parseScaleHeader(char const* buf, float& scale) {
++buf;
}
- // Then, run through each of the fields, looking for ones we handle:
char const* fields = buf + 6;
while (*fields == ' ') ++fields;
float sc;
@@ -359,14 +361,3 @@ char const* dateHeader() {
#endif
return buf;
}
-
-void ignoreSigPipeOnSocket(int socketNum) {
-#ifdef USE_SIGNALS
-#ifdef SO_NOSIGPIPE
- int set_option = 1;
- setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
-#else
- signal(SIGPIPE, SIG_IGN);
-#endif
-#endif
-}
diff --git a/liveMedia/RTSPRegisterSender.cpp b/liveMedia/RTSPRegisterSender.cpp
index 9d5b610..d8285f8 100644
--- a/liveMedia/RTSPRegisterSender.cpp
+++ b/liveMedia/RTSPRegisterSender.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A special object which, when created, sends a custom RTSP "REGISTER" command to a specified client.
// Implementation
@@ -89,11 +89,11 @@ Boolean RTSPRegisterSender::setRequestFields(RequestRecord* request,
sprintf(proxyURLSuffixParameterStr, proxyURLSuffixParameterFmt, request_REGISTER->proxyURLSuffix());
}
- char const* transportHeaderFmt = "Transport: reuse_connection=%d; preferred_delivery_protocol=%s%s\r\n";
+ char const* transportHeaderFmt = "Transport: %spreferred_delivery_protocol=%s%s\r\n";
unsigned transportHeaderSize = strlen(transportHeaderFmt) + 100/*conservative*/ + strlen(proxyURLSuffixParameterStr);
char* transportHeaderStr = new char[transportHeaderSize];
sprintf(transportHeaderStr, transportHeaderFmt,
- request_REGISTER->reuseConnection(),
+ request_REGISTER->reuseConnection() ? "reuse_connection; " : "",
request_REGISTER->requestStreamingViaTCP() ? "interleaved" : "udp",
proxyURLSuffixParameterStr);
delete[] proxyURLSuffixParameterStr;
diff --git a/liveMedia/RTSPServer.cpp b/liveMedia/RTSPServer.cpp
index 99b5b12..86f3eed 100644
--- a/liveMedia/RTSPServer.cpp
+++ b/liveMedia/RTSPServer.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A RTSP server
// Implementation
@@ -30,11 +30,11 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
RTSPServer*
RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase,
- unsigned reclamationTestSeconds) {
+ unsigned reclamationSeconds) {
int ourSocket = setUpOurSocket(env, ourPort);
if (ourSocket == -1) return NULL;
- return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
+ return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds);
}
Boolean RTSPServer::lookupByName(UsageEnvironment& env,
@@ -54,64 +54,6 @@ Boolean RTSPServer::lookupByName(UsageEnvironment& env,
return True;
}
-void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
- if (serverMediaSession == NULL) return;
-
- char const* sessionName = serverMediaSession->streamName();
- if (sessionName == NULL) sessionName = "";
- removeServerMediaSession(sessionName); // in case an existing "ServerMediaSession" with this name already exists
-
- fServerMediaSessions->Add(sessionName, (void*)serverMediaSession);
-}
-
-ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {
- return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
-}
-
-void RTSPServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) {
- if (serverMediaSession == NULL) return;
-
- fServerMediaSessions->Remove(serverMediaSession->streamName());
- if (serverMediaSession->referenceCount() == 0) {
- Medium::close(serverMediaSession);
- } else {
- serverMediaSession->deleteWhenUnreferenced() = True;
- }
-}
-
-void RTSPServer::removeServerMediaSession(char const* streamName) {
- removeServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
-}
-
-void RTSPServer::closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession) {
- if (serverMediaSession == NULL) return;
-
- HashTable::Iterator* iter = HashTable::Iterator::create(*fClientSessions);
- RTSPServer::RTSPClientSession* clientSession;
- char const* key; // dummy
- while ((clientSession = (RTSPServer::RTSPClientSession*)(iter->next(key))) != NULL) {
- if (clientSession->fOurServerMediaSession == serverMediaSession) {
- delete clientSession;
- }
- }
- delete iter;
-}
-
-void RTSPServer::closeAllClientSessionsForServerMediaSession(char const* streamName) {
- closeAllClientSessionsForServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
-}
-
-void RTSPServer::deleteServerMediaSession(ServerMediaSession* serverMediaSession) {
- if (serverMediaSession == NULL) return;
-
- closeAllClientSessionsForServerMediaSession(serverMediaSession);
- removeServerMediaSession(serverMediaSession);
-}
-
-void RTSPServer::deleteServerMediaSession(char const* streamName) {
- deleteServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
-}
-
void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward
// A class that represents the state of a "REGISTER" request in progress:
@@ -135,7 +77,7 @@ public:
ourServer.fPendingRegisterRequests->Add((char const*)this, this);
}
- virtual ~RegisterRequestRecord(){
+ virtual ~RegisterRequestRecord() {
// Remove ourself from the server's 'pending REGISTER requests' hash table before we go:
fOurServer.fPendingRegisterRequests->Remove((char const*)this);
}
@@ -222,7 +164,7 @@ char* RTSPServer::rtspURLPrefix(int clientSocket) const {
char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/"
- portNumBits portNumHostOrder = ntohs(fRTSPServerPort.num());
+ portNumBits portNumHostOrder = ntohs(fServerPort.num());
if (portNumHostOrder == 554 /* the default port number */) {
sprintf(urlBuffer, "rtsp://%s/", AddressString(ourAddress).val());
} else {
@@ -245,7 +187,7 @@ Boolean RTSPServer::setUpTunnelingOverHTTP(Port httpPort) {
if (fHTTPServerSocket >= 0) {
fHTTPServerPort = httpPort;
envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocket,
- (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerHTTP, this);
+ incomingConnectionHandlerHTTP, this);
return True;
}
@@ -256,42 +198,6 @@ portNumBits RTSPServer::httpServerPortNum() const {
return ntohs(fHTTPServerPort.num());
}
-#define LISTEN_BACKLOG_SIZE 20
-
-int RTSPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
- int ourSocket = -1;
-
- do {
- // The following statement is enabled by default.
- // Don't disable it (by defining ALLOW_RTSP_SERVER_PORT_REUSE) unless you know what you're doing.
-#ifndef ALLOW_RTSP_SERVER_PORT_REUSE
- NoReuse dummy(env); // Don't use this socket if there's already a local server using it
-#endif
-
- ourSocket = setupStreamSocket(env, ourPort);
- if (ourSocket < 0) break;
-
- // Make sure we have a big send buffer:
- if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
-
- // Allow multiple simultaneous connections:
- if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
- env.setResultErrMsg("listen() failed: ");
- break;
- }
-
- if (ourPort.num() == 0) {
- // bind() will have chosen a port for us; return it also:
- if (!getSourcePort(env, ourSocket, ourPort)) break;
- }
-
- return ourSocket;
- } while (0);
-
- if (ourSocket != -1) ::closeSocket(ourSocket);
- return -1;
-}
-
char const* RTSPServer::allowedCommandNames() {
return "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER";
}
@@ -327,51 +233,38 @@ Boolean RTSPServer::specialClientUserAccessCheck(int /*clientSocket*/, struct so
RTSPServer::RTSPServer(UsageEnvironment& env,
int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase,
- unsigned reclamationTestSeconds)
- : Medium(env),
- fRTSPServerPort(ourPort), fRTSPServerSocket(ourSocket), fHTTPServerSocket(-1), fHTTPServerPort(0),
- fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
- fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
+ unsigned reclamationSeconds)
+ : GenericMediaServer(env, ourSocket, ourPort, reclamationSeconds),
+ fHTTPServerSocket(-1), fHTTPServerPort(0),
fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
- fClientSessions(HashTable::create(STRING_HASH_KEYS)),
+ fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)),
fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)), fRegisterRequestCounter(0),
- fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds) {
- ignoreSigPipeOnSocket(ourSocket); // so that clients on the same host that are killed don't also kill us
-
- // Arrange to handle connections from others:
- env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
- (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
+ fAuthDB(authDatabase), fAllowStreamingRTPOverTCP(True) {
}
+// A data structure that is used to implement "fTCPStreamingDatabase"
+// (and the "noteTCPStreamingOnSocket()" and "stopTCPStreamingOnSocket()" member functions):
+class streamingOverTCPRecord {
+public:
+ streamingOverTCPRecord(u_int32_t sessionId, unsigned trackNum, streamingOverTCPRecord* next)
+ : fNext(next), fSessionId(sessionId), fTrackNum(trackNum) {
+ }
+ virtual ~streamingOverTCPRecord() {
+ delete fNext;
+ }
+
+ streamingOverTCPRecord* fNext;
+ u_int32_t fSessionId;
+ unsigned fTrackNum;
+};
+
RTSPServer::~RTSPServer() {
- // Turn off background read handling:
- envir().taskScheduler().turnOffBackgroundReadHandling(fRTSPServerSocket);
- ::closeSocket(fRTSPServerSocket);
-
+ // Turn off background HTTP read handling (if any):
envir().taskScheduler().turnOffBackgroundReadHandling(fHTTPServerSocket);
::closeSocket(fHTTPServerSocket);
+ delete fClientConnectionsForHTTPTunneling;
- // Close all client connection objects:
- RTSPServer::RTSPClientConnection* connection;
- while ((connection = (RTSPServer::RTSPClientConnection*)fClientConnections->getFirst()) != NULL) {
- delete connection;
- }
- delete fClientConnections;
- delete fClientConnectionsForHTTPTunneling; // all content was already removed as a result of the loop above
-
- // Close all client session objects:
- RTSPServer::RTSPClientSession* clientSession;
- while ((clientSession = (RTSPServer::RTSPClientSession*)fClientSessions->getFirst()) != NULL) {
- delete clientSession;
- }
- delete fClientSessions;
-
- // Delete all server media sessions
- ServerMediaSession* serverMediaSession;
- while ((serverMediaSession = (ServerMediaSession*)fServerMediaSessions->getFirst()) != NULL) {
- removeServerMediaSession(serverMediaSession); // will delete it, because it no longer has any 'client session' objects using it
- }
- delete fServerMediaSessions;
+ cleanup(); // Removes all "ClientSession" and "ClientConnection" objects, and their tables.
// Delete any pending REGISTER requests:
RegisterRequestRecord* registerRequest;
@@ -379,48 +272,93 @@ RTSPServer::~RTSPServer() {
delete registerRequest;
}
delete fPendingRegisterRequests;
+
+ // Empty out and close "fTCPStreamingDatabase":
+ streamingOverTCPRecord* sotcp;
+ while ((sotcp = (streamingOverTCPRecord*)fTCPStreamingDatabase->getFirst()) != NULL) {
+ delete sotcp;
+ }
+ delete fTCPStreamingDatabase;
}
Boolean RTSPServer::isRTSPServer() const {
return True;
}
-void RTSPServer::incomingConnectionHandlerRTSP(void* instance, int /*mask*/) {
+void RTSPServer::incomingConnectionHandlerHTTP(void* instance, int /*mask*/) {
RTSPServer* server = (RTSPServer*)instance;
- server->incomingConnectionHandlerRTSP1();
+ server->incomingConnectionHandlerHTTP();
}
-void RTSPServer::incomingConnectionHandlerRTSP1() {
- incomingConnectionHandler(fRTSPServerSocket);
+void RTSPServer::incomingConnectionHandlerHTTP() {
+ incomingConnectionHandlerOnSocket(fHTTPServerSocket);
}
-void RTSPServer::incomingConnectionHandlerHTTP(void* instance, int /*mask*/) {
- RTSPServer* server = (RTSPServer*)instance;
- server->incomingConnectionHandlerHTTP1();
-}
-void RTSPServer::incomingConnectionHandlerHTTP1() {
- incomingConnectionHandler(fHTTPServerSocket);
+void RTSPServer
+::noteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum) {
+ streamingOverTCPRecord* sotcpCur
+ = (streamingOverTCPRecord*)fTCPStreamingDatabase->Lookup((char const*)socketNum);
+ streamingOverTCPRecord* sotcpNew
+ = new streamingOverTCPRecord(clientSession->fOurSessionId, trackNum, sotcpCur);
+ fTCPStreamingDatabase->Add((char const*)socketNum, sotcpNew);
}
-void RTSPServer::incomingConnectionHandler(int serverSocket) {
- struct sockaddr_in clientAddr;
- SOCKLEN_T clientAddrLen = sizeof clientAddr;
- int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
- if (clientSocket < 0) {
- int err = envir().getErrno();
- if (err != EWOULDBLOCK) {
- envir().setResultErrMsg("accept() failed: ");
+void RTSPServer
+::unnoteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum) {
+ if (socketNum < 0) return;
+ streamingOverTCPRecord* sotcpHead
+ = (streamingOverTCPRecord*)fTCPStreamingDatabase->Lookup((char const*)socketNum);
+ if (sotcpHead == NULL) return;
+
+ // Look for a record of the (session,track); remove it if found:
+ streamingOverTCPRecord* sotcp = sotcpHead;
+ streamingOverTCPRecord* sotcpPrev = sotcpHead;
+ do {
+ if (sotcp->fSessionId == clientSession->fOurSessionId && sotcp->fTrackNum == trackNum) break;
+ sotcpPrev = sotcp;
+ sotcp = sotcp->fNext;
+ } while (sotcp != NULL);
+ if (sotcp == NULL) return; // not found
+
+ if (sotcp == sotcpHead) {
+ // We found it at the head of the list. Remove it and reinsert the tail into the hash table:
+ sotcpHead = sotcp->fNext;
+ sotcp->fNext = NULL;
+ delete sotcp;
+
+ if (sotcpHead == NULL) {
+ // There were no more entries on the list. Remove the original entry from the hash table:
+ fTCPStreamingDatabase->Remove((char const*)socketNum);
+ } else {
+ // Add the rest of the list into the hash table (replacing the original):
+ fTCPStreamingDatabase->Add((char const*)socketNum, sotcpHead);
}
- return;
+ } else {
+ // We found it on the list, but not at the head. Unlink it:
+ sotcpPrev->fNext = sotcp->fNext;
+ sotcp->fNext = NULL;
+ delete sotcp;
+ }
+}
+
+void RTSPServer::stopTCPStreamingOnSocket(int socketNum) {
+ // Close any stream that is streaming over "socketNum" (using RTP/RTCP-over-TCP streaming):
+ streamingOverTCPRecord* sotcp
+ = (streamingOverTCPRecord*)fTCPStreamingDatabase->Lookup((char const*)socketNum);
+ if (sotcp != NULL) {
+ do {
+ RTSPClientSession* clientSession
+ = (RTSPServer::RTSPClientSession*)lookupClientSession(sotcp->fSessionId);
+ if (clientSession != NULL) {
+ clientSession->deleteStreamByTrack(sotcp->fTrackNum);
+ }
+
+ streamingOverTCPRecord* sotcpNext = sotcp->fNext;
+ sotcp->fNext = NULL;
+ delete sotcp;
+ sotcp = sotcpNext;
+ } while (sotcp != NULL);
+ fTCPStreamingDatabase->Remove((char const*)socketNum);
}
- makeSocketNonBlocking(clientSocket);
- increaseSendBufferTo(envir(), clientSocket, 50*1024);
-
-#ifdef DEBUG
- envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
-#endif
-
- // Create a new object for handling this RTSP connection:
- (void)createNewClientConnection(clientSocket, clientAddr);
}
@@ -428,29 +366,20 @@ void RTSPServer::incomingConnectionHandler(int serverSocket) {
RTSPServer::RTSPClientConnection
::RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
- : fOurServer(ourServer), fIsActive(True),
- fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
- fRecursionCount(0), fOurSessionCookie(NULL) {
- // Add ourself to our 'client connections' table:
- fOurServer.fClientConnections->Add((char const*)this, this);
-
- // Arrange to handle incoming requests:
+ : GenericMediaServer::ClientConnection(ourServer, clientSocket, clientAddr),
+ fOurRTSPServer(ourServer), fClientInputSocket(fOurSocket), fClientOutputSocket(fOurSocket),
+ fIsActive(True), fRecursionCount(0), fOurSessionCookie(NULL) {
resetRequestBuffer();
- envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
- (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
}
RTSPServer::RTSPClientConnection::~RTSPClientConnection() {
- // Remove ourself from the server's 'client connections' hash table before we go:
- fOurServer.fClientConnections->Remove((char const*)this);
-
if (fOurSessionCookie != NULL) {
// We were being used for RTSP-over-HTTP tunneling. Also remove ourselves from the 'session cookie' hash table before we go:
- fOurServer.fClientConnectionsForHTTPTunneling->Remove(fOurSessionCookie);
+ fOurRTSPServer.fClientConnectionsForHTTPTunneling->Remove(fOurSessionCookie);
delete[] fOurSessionCookie;
}
- closeSockets();
+ closeSocketsRTSP();
}
// Special mechanism for handling our custom "REGISTER" command:
@@ -471,7 +400,7 @@ RTSPServer::RTSPClientConnection::ParamsForREGISTER::~ParamsForREGISTER() {
void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS() {
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
- fCurrentCSeq, dateHeader(), fOurServer.allowedCommandNames());
+ fCurrentCSeq, dateHeader(), fOurRTSPServer.allowedCommandNames());
}
void RTSPServer::RTSPClientConnection
@@ -492,14 +421,12 @@ void RTSPServer::RTSPClientConnection
void RTSPServer::RTSPClientConnection
::handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
+ ServerMediaSession* session = NULL;
char* sdpDescription = NULL;
char* rtspURL = NULL;
do {
- char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
- if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2 > sizeof urlTotalSuffix) {
- handleCmd_bad();
- break;
- }
+ char urlTotalSuffix[2*RTSP_PARAM_STRING_MAX];
+ // enough space for urlPreSuffix/urlSuffix'\0'
urlTotalSuffix[0] = '\0';
if (urlPreSuffix[0] != '\0') {
strcat(urlTotalSuffix, urlPreSuffix);
@@ -513,12 +440,16 @@ void RTSPServer::RTSPClientConnection
// for "application/sdp", because that's what we're sending back #####
// Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
- ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
+ session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
if (session == NULL) {
handleCmd_notFound();
break;
}
+ // Increment the "ServerMediaSession" object's reference count, in case someone removes it
+ // while we're using it:
+ session->incrementReferenceCount();
+
// Then, assemble a SDP description for this session:
sdpDescription = session->generateSDPDescription();
if (sdpDescription == NULL) {
@@ -531,7 +462,7 @@ void RTSPServer::RTSPClientConnection
// Also, generate our RTSP URL, for the "Content-Base:" header
// (which is necessary to ensure that the correct URL gets used in subsequent "SETUP" requests).
- rtspURL = fOurServer.rtspURL(session, fClientInputSocket);
+ rtspURL = fOurRTSPServer.rtspURL(session, fClientInputSocket);
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
@@ -547,6 +478,14 @@ void RTSPServer::RTSPClientConnection
sdpDescription);
} while (0);
+ if (session != NULL) {
+ // Decrement its reference count, now that we're done using it:
+ session->decrementReferenceCount();
+ if (session->referenceCount() == 0 && session->deleteWhenUnreferenced()) {
+ fOurServer.removeServerMediaSession(session);
+ }
+ }
+
delete[] sdpDescription;
delete[] rtspURL;
}
@@ -577,7 +516,7 @@ void RTSPServer
::RTSPClientConnection::handleCmd_REGISTER(char const* url, char const* urlSuffix, char const* fullRequestStr,
Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix) {
char* responseStr;
- if (fOurServer.weImplementREGISTER(proxyURLSuffix, responseStr)) {
+ if (fOurRTSPServer.weImplementREGISTER(proxyURLSuffix, responseStr)) {
// The "REGISTER" command - if we implement it - may require access control:
if (!authenticationOK("REGISTER", urlSuffix, fullRequestStr)) return;
@@ -600,13 +539,13 @@ void RTSPServer::RTSPClientConnection::handleCmd_bad() {
// Don't do anything with "fCurrentCSeq", because it might be nonsense
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n",
- dateHeader(), fOurServer.allowedCommandNames());
+ dateHeader(), fOurRTSPServer.allowedCommandNames());
}
void RTSPServer::RTSPClientConnection::handleCmd_notSupported() {
snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
"RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
- fCurrentCSeq, dateHeader(), fOurServer.allowedCommandNames());
+ fCurrentCSeq, dateHeader(), fOurRTSPServer.allowedCommandNames());
}
void RTSPServer::RTSPClientConnection::handleCmd_notFound() {
@@ -705,11 +644,11 @@ void RTSPServer::RTSPClientConnection::handleHTTPCmd_OPTIONS() {
void RTSPServer::RTSPClientConnection::handleHTTPCmd_TunnelingGET(char const* sessionCookie) {
// Record ourself as having this 'session cookie', so that a subsequent HTTP "POST" command (with the same 'session cookie')
// can find us:
- if (fOurServer.fClientConnectionsForHTTPTunneling == NULL) {
- fOurServer.fClientConnectionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
+ if (fOurRTSPServer.fClientConnectionsForHTTPTunneling == NULL) {
+ fOurRTSPServer.fClientConnectionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
}
delete[] fOurSessionCookie; fOurSessionCookie = strDup(sessionCookie);
- fOurServer.fClientConnectionsForHTTPTunneling->Add(sessionCookie, (void*)this);
+ fOurRTSPServer.fClientConnectionsForHTTPTunneling->Add(sessionCookie, (void*)this);
#ifdef DEBUG
fprintf(stderr, "Handled HTTP \"GET\" request (client output socket: %d)\n", fClientOutputSocket);
#endif
@@ -729,11 +668,11 @@ Boolean RTSPServer::RTSPClientConnection
::handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize) {
// Use the "sessionCookie" string to look up the separate "RTSPClientConnection" object that should have been used to handle
// an earlier HTTP "GET" request:
- if (fOurServer.fClientConnectionsForHTTPTunneling == NULL) {
- fOurServer.fClientConnectionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
+ if (fOurRTSPServer.fClientConnectionsForHTTPTunneling == NULL) {
+ fOurRTSPServer.fClientConnectionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
}
RTSPServer::RTSPClientConnection* prevClientConnection
- = (RTSPServer::RTSPClientConnection*)(fOurServer.fClientConnectionsForHTTPTunneling->Lookup(sessionCookie));
+ = (RTSPServer::RTSPClientConnection*)(fOurRTSPServer.fClientConnectionsForHTTPTunneling->Lookup(sessionCookie));
if (prevClientConnection == NULL) {
// There was no previous HTTP "GET" request; treat this "POST" request as bad:
handleHTTPCmd_notSupported();
@@ -756,40 +695,29 @@ void RTSPServer::RTSPClientConnection::handleHTTPCmd_StreamingGET(char const* /*
}
void RTSPServer::RTSPClientConnection::resetRequestBuffer() {
- fRequestBytesAlreadySeen = 0;
- fRequestBufferBytesLeft = sizeof fRequestBuffer;
+ ClientConnection::resetRequestBuffer();
+
fLastCRLF = &fRequestBuffer[-3]; // hack: Ensures that we don't think we have end-of-msg if the data starts with <CR><LF>
fBase64RemainderCount = 0;
}
-void RTSPServer::RTSPClientConnection::closeSockets() {
+void RTSPServer::RTSPClientConnection::closeSocketsRTSP() {
+ // First, tell our server to stop any streaming that it might be doing over our output socket:
+ fOurRTSPServer.stopTCPStreamingOnSocket(fClientOutputSocket);
+
// Turn off background handling on our input socket (and output socket, if different); then close it (or them):
if (fClientOutputSocket != fClientInputSocket) {
envir().taskScheduler().disableBackgroundHandling(fClientOutputSocket);
::closeSocket(fClientOutputSocket);
}
+ fClientOutputSocket = -1;
- envir().taskScheduler().disableBackgroundHandling(fClientInputSocket);
- ::closeSocket(fClientInputSocket);
-
- fClientInputSocket = fClientOutputSocket = -1;
-}
-
-void RTSPServer::RTSPClientConnection::incomingRequestHandler(void* instance, int /*mask*/) {
- RTSPClientConnection* session = (RTSPClientConnection*)instance;
- session->incomingRequestHandler1();
-}
-
-void RTSPServer::RTSPClientConnection::incomingRequestHandler1() {
- struct sockaddr_in dummy; // 'from' address, meaningless in this case
-
- int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
- handleRequestBytes(bytesRead);
+ closeSockets(); // closes fClientInputSocket
}
void RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(void* instance, u_int8_t requestByte) {
- RTSPClientConnection* session = (RTSPClientConnection*)instance;
- session->handleAlternativeRequestByte1(requestByte);
+ RTSPClientConnection* connection = (RTSPClientConnection*)instance;
+ connection->handleAlternativeRequestByte1(requestByte);
}
void RTSPServer::RTSPClientConnection::handleAlternativeRequestByte1(u_int8_t requestByte) {
@@ -799,10 +727,10 @@ void RTSPServer::RTSPClientConnection::handleAlternativeRequestByte1(u_int8_t re
} else if (requestByte == 0xFE) {
// Another hack: The new handler of the input TCP socket no longer needs it, so take back control of it:
envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
- (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
+ incomingRequestHandler, this);
} else {
// Normal case: Add this character to our buffer; then try to handle the data that we have buffered so far:
- if (fRequestBufferBytesLeft == 0 || fRequestBytesAlreadySeen >= RTSP_BUFFER_SIZE) return;
+ if (fRequestBufferBytesLeft == 0 || fRequestBytesAlreadySeen >= REQUEST_BUFFER_SIZE) return;
fRequestBuffer[fRequestBytesAlreadySeen] = requestByte;
handleRequestBytes(1);
}
@@ -826,15 +754,13 @@ static void parseTransportHeaderForREGISTER(char const* buf,
++buf;
}
- int reuseConnectionNum;
-
// Then, run through each of the fields, looking for ones we handle:
char const* fields = buf + 10;
while (*fields == ' ') ++fields;
char* field = strDupSize(fields);
while (sscanf(fields, "%[^;\r\n]", field) == 1) {
- if (sscanf(field, "reuse_connection = %d", &reuseConnectionNum) == 1) {
- reuseConnection = reuseConnectionNum != 0;
+ if (strcmp(field, "reuse_connection") == 0) {
+ reuseConnection = True;
} else if (_strncasecmp(field, "preferred_delivery_protocol=udp", 31) == 0) {
deliverViaTCP = False;
} else if (_strncasecmp(field, "preferred_delivery_protocol=interleaved", 39) == 0) {
@@ -910,25 +836,27 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
// Then copy any remaining (undecoded) bytes to the end:
for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
- newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
+ newBytesRead = decodedSize - fBase64RemainderCount + newBase64RemainderCount;
+ // adjust to allow for the size of the new decoded data (+ remainder)
delete[] decodedBytes;
}
fBase64RemainderCount = newBase64RemainderCount;
- if (fBase64RemainderCount > 0) break; // because we know that we have more input bytes still to receive
}
- // Look for the end of the message: <CR><LF><CR><LF>
- unsigned char *tmpPtr = fLastCRLF + 2;
- if (tmpPtr < fRequestBuffer) tmpPtr = fRequestBuffer;
- while (tmpPtr < &ptr[newBytesRead-1]) {
- if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
- if (tmpPtr - fLastCRLF == 2) { // This is it:
- endOfMsg = True;
- break;
+ unsigned char* tmpPtr = fLastCRLF + 2;
+ if (fBase64RemainderCount == 0) { // no more Base-64 bytes remain to be read/decoded
+ // Look for the end of the message: <CR><LF><CR><LF>
+ if (tmpPtr < fRequestBuffer) tmpPtr = fRequestBuffer;
+ while (tmpPtr < &ptr[newBytesRead-1]) {
+ if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
+ if (tmpPtr - fLastCRLF == 2) { // This is it:
+ endOfMsg = True;
+ break;
+ }
+ fLastCRLF = tmpPtr;
}
- fLastCRLF = tmpPtr;
+ ++tmpPtr;
}
- ++tmpPtr;
}
fRequestBufferBytesLeft -= newBytesRead;
@@ -953,9 +881,10 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
sessionIdStr, sizeof sessionIdStr,
contentLength);
fLastCRLF[2] = '\r'; // restore its value
+ Boolean playAfterSetup = False;
if (parseSucceeded) {
#ifdef DEBUG
- fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %ld bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
+ fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
#endif
// If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us
@@ -964,7 +893,8 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
// current ongoing, then use this command to indicate 'liveness' on that client session:
Boolean const requestIncludedSessionId = sessionIdStr[0] != '\0';
if (requestIncludedSessionId) {
- clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
+ clientSession
+ = (RTSPServer::RTSPClientSession*)(fOurRTSPServer.lookupClientSession(sessionIdStr));
if (clientSession != NULL) clientSession->noteLiveness();
}
@@ -972,7 +902,14 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
// Handle the specified command (beginning with commands that are session-independent):
fCurrentCSeq = cseq;
if (strcmp(cmdName, "OPTIONS") == 0) {
- handleCmd_OPTIONS();
+ // If the "OPTIONS" command included a "Session:" id for a session that doesn't exist,
+ // then treat this as an error:
+ if (requestIncludedSessionId && clientSession == NULL) {
+ handleCmd_sessionNotFound();
+ } else {
+ // Normal case:
+ handleCmd_OPTIONS();
+ }
} else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {
// The special "*" URL means: an operation on the entire server. This works only for GET_PARAMETER and SET_PARAMETER:
if (strcmp(cmdName, "GET_PARAMETER") == 0) {
@@ -985,23 +922,33 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
} else if (strcmp(cmdName, "DESCRIBE") == 0) {
handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
} else if (strcmp(cmdName, "SETUP") == 0) {
+ Boolean areAuthenticated = True;
+
if (!requestIncludedSessionId) {
- // No session id was present in the request. So create a new "RTSPClientSession" object
- // for this request. Choose a random (unused) 32-bit integer for the session id
- // (it will be encoded as a 8-digit hex number). (We avoid choosing session id 0,
- // because that has a special use (by "OnDemandServerMediaSubsession").)
- u_int32_t sessionId;
- do {
- sessionId = (u_int32_t)our_random32();
- sprintf(sessionIdStr, "%08X", sessionId);
- } while (sessionId == 0 || fOurServer.fClientSessions->Lookup(sessionIdStr) != NULL);
- clientSession = fOurServer.createNewClientSession(sessionId);
- fOurServer.fClientSessions->Add(sessionIdStr, clientSession);
+ // No session id was present in the request.
+ // So create a new "RTSPClientSession" object for this request.
+
+ // But first, make sure that we're authenticated to perform this command:
+ char urlTotalSuffix[2*RTSP_PARAM_STRING_MAX];
+ // enough space for urlPreSuffix/urlSuffix'\0'
+ urlTotalSuffix[0] = '\0';
+ if (urlPreSuffix[0] != '\0') {
+ strcat(urlTotalSuffix, urlPreSuffix);
+ strcat(urlTotalSuffix, "/");
+ }
+ strcat(urlTotalSuffix, urlSuffix);
+ if (authenticationOK("SETUP", urlTotalSuffix, (char const*)fRequestBuffer)) {
+ clientSession
+ = (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();
+ } else {
+ areAuthenticated = False;
+ }
}
if (clientSession != NULL) {
clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
- } else {
- handleCmd_sessionNotFound();
+ playAfterSetup = clientSession->fStreamAfterSETUP;
+ } else if (areAuthenticated) {
+ handleCmd_sessionNotFound();
}
} else if (strcmp(cmdName, "TEARDOWN") == 0
|| strcmp(cmdName, "PLAY") == 0
@@ -1093,7 +1040,7 @@ void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
#endif
send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
- if (clientSession != NULL && clientSession->fStreamAfterSETUP && strcmp(cmdName, "SETUP") == 0) {
+ if (playAfterSetup) {
// The client has asked for streaming to commence now, rather than after a
// subsequent "PLAY" command. So, simulate the effect of a "PLAY" command:
clientSession->handleCmd_withinSession(this, "PLAY", urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
@@ -1169,13 +1116,13 @@ static Boolean parseAuthorizationHeader(char const* buf,
Boolean RTSPServer::RTSPClientConnection
::authenticationOK(char const* cmdName, char const* urlSuffix, char const* fullRequestStr) {
- if (!fOurServer.specialClientAccessCheck(fClientInputSocket, fClientAddr, urlSuffix)) {
+ if (!fOurRTSPServer.specialClientAccessCheck(fClientInputSocket, fClientAddr, urlSuffix)) {
setRTSPResponse("401 Unauthorized");
return False;
}
// If we weren't set up with an authentication database, we're OK:
- UserAuthenticationDatabase* authDB = fOurServer.getAuthenticationDatabaseForCommand(cmdName);
+ UserAuthenticationDatabase* authDB = fOurRTSPServer.getAuthenticationDatabaseForCommand(cmdName);
if (authDB == NULL) return True;
char const* username = NULL; char const* realm = NULL; char const* nonce = NULL;
@@ -1221,7 +1168,7 @@ Boolean RTSPServer::RTSPClientConnection
if (success) {
// The user has been authenticated.
// Now allow subclasses a chance to validate the user against the IP address and/or URL suffix.
- if (!fOurServer.specialClientUserAccessCheck(fClientInputSocket, fClientAddr, urlSuffix, username)) {
+ if (!fOurRTSPServer.specialClientUserAccessCheck(fClientInputSocket, fClientAddr, urlSuffix, username)) {
// Note: We don't return a "WWW-Authenticate" header here, because the user is valid,
// even though the server has decided that they should not have access.
setRTSPResponse("401 Unauthorized");
@@ -1313,7 +1260,7 @@ void RTSPServer::RTSPClientConnection
envir().taskScheduler().disableBackgroundHandling(fClientInputSocket);
fClientInputSocket = newSocketNum;
envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
- (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
+ incomingRequestHandler, this);
// Also write any extra data to our buffer, and handle it:
if (extraDataSize > 0 && extraDataSize <= fRequestBufferBytesLeft/*sanity check; should always be true*/) {
@@ -1333,7 +1280,7 @@ void RTSPServer::RTSPClientConnection::continueHandlingREGISTER1(ParamsForREGIST
// Reuse our socket if requested:
int socketNumToBackEndServer = params->fReuseConnection ? fClientOutputSocket : -1;
- RTSPServer* ourServer = &fOurServer; // copy the pointer now, in case we "delete this" below
+ RTSPServer* ourServer = &fOurRTSPServer; // copy the pointer now, in case we "delete this" below
if (socketNumToBackEndServer >= 0) {
// Because our socket will no longer be used by the server to handle incoming requests, we can now delete this
@@ -1353,35 +1300,37 @@ void RTSPServer::RTSPClientConnection::continueHandlingREGISTER1(ParamsForREGIST
RTSPServer::RTSPClientSession
::RTSPClientSession(RTSPServer& ourServer, u_int32_t sessionId)
- : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL), fIsMulticast(False), fStreamAfterSETUP(False),
- fTCPStreamIdCount(0), fLivenessCheckTask(NULL), fNumStreamStates(0), fStreamStates(NULL) {
- noteLiveness();
+ : GenericMediaServer::ClientSession(ourServer, sessionId),
+ fOurRTSPServer(ourServer), fIsMulticast(False), fStreamAfterSETUP(False),
+ fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) {
}
RTSPServer::RTSPClientSession::~RTSPClientSession() {
- // Turn off any liveness checking:
- envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
-
- // Remove ourself from the server's 'client sessions' hash table before we go:
- char sessionIdStr[9];
- sprintf(sessionIdStr, "%08X", fOurSessionId);
- fOurServer.fClientSessions->Remove(sessionIdStr);
-
reclaimStreamStates();
+}
+
+void RTSPServer::RTSPClientSession::deleteStreamByTrack(unsigned trackNum) {
+ if (trackNum >= fNumStreamStates) return; // sanity check; shouldn't happen
+ if (fStreamStates[trackNum].subsession != NULL) {
+ fStreamStates[trackNum].subsession->deleteStream(fOurSessionId, fStreamStates[trackNum].streamToken);
+ fStreamStates[trackNum].subsession = NULL;
+ }
- if (fOurServerMediaSession != NULL) {
- fOurServerMediaSession->decrementReferenceCount();
- if (fOurServerMediaSession->referenceCount() == 0
- && fOurServerMediaSession->deleteWhenUnreferenced()) {
- fOurServer.removeServerMediaSession(fOurServerMediaSession);
- fOurServerMediaSession = NULL;
+ // Optimization: If all subsessions have now been deleted, then we can delete ourself now:
+ Boolean noSubsessionsRemain = True;
+ for (unsigned i = 0; i < fNumStreamStates; ++i) {
+ if (fStreamStates[i].subsession != NULL) {
+ noSubsessionsRemain = False;
+ break;
}
}
+ if (noSubsessionsRemain) delete this;
}
void RTSPServer::RTSPClientSession::reclaimStreamStates() {
for (unsigned i = 0; i < fNumStreamStates; ++i) {
if (fStreamStates[i].subsession != NULL) {
+ fOurRTSPServer.unnoteTCPStreamingOnSocket(fStreamStates[i].tcpSocketNum, this, i);
fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
}
}
@@ -1484,7 +1433,8 @@ void RTSPServer::RTSPClientSession
do {
// First, make sure the specified stream name exists:
- ServerMediaSession* sms = fOurServer.lookupServerMediaSession(streamName);
+ ServerMediaSession* sms
+ = fOurServer.lookupServerMediaSession(streamName, fOurServerMediaSession == NULL);
if (sms == NULL) {
// Check for the special case (noted above), before we give up:
if (urlPreSuffix[0] == '\0') {
@@ -1497,7 +1447,7 @@ void RTSPServer::RTSPClientSession
trackId = NULL;
// Check again:
- sms = fOurServer.lookupServerMediaSession(streamName);
+ sms = fOurServer.lookupServerMediaSession(streamName, fOurServerMediaSession == NULL);
}
if (sms == NULL) {
if (fOurServerMediaSession == NULL) {
@@ -1532,19 +1482,20 @@ void RTSPServer::RTSPClientSession
for (unsigned i = 0; i < fNumStreamStates; ++i) {
subsession = iter.next();
fStreamStates[i].subsession = subsession;
+ fStreamStates[i].tcpSocketNum = -1; // for now; may get set for RTP-over-TCP streaming
fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
}
}
// Look up information for the specified subsession (track):
ServerMediaSubsession* subsession = NULL;
- unsigned streamNum;
+ unsigned trackNum;
if (trackId != NULL && trackId[0] != '\0') { // normal case
- for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
- subsession = fStreamStates[streamNum].subsession;
+ for (trackNum = 0; trackNum < fNumStreamStates; ++trackNum) {
+ subsession = fStreamStates[trackNum].subsession;
if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
}
- if (streamNum >= fNumStreamStates) {
+ if (trackNum >= fNumStreamStates) {
// The specified track id doesn't exist, so this request fails:
ourClientConnection->handleCmd_notFound();
break;
@@ -1556,11 +1507,20 @@ void RTSPServer::RTSPClientSession
ourClientConnection->handleCmd_bad();
break;
}
- streamNum = 0;
- subsession = fStreamStates[streamNum].subsession;
+ trackNum = 0;
+ subsession = fStreamStates[trackNum].subsession;
}
// ASSERT: subsession != NULL
+ void*& token = fStreamStates[trackNum].streamToken; // alias
+ if (token != NULL) {
+ // We already handled a "SETUP" for this track (to the same client),
+ // so stop any existing streaming of it, before we set it up again:
+ subsession->pauseStream(fOurSessionId, token);
+ fOurRTSPServer.unnoteTCPStreamingOnSocket(fStreamStates[trackNum].tcpSocketNum, this, trackNum);
+ subsession->deleteStream(fOurSessionId, token);
+ }
+
// Look for a "Transport:" header in the request string, to extract client parameters:
StreamingMode streamingMode;
char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
@@ -1590,7 +1550,8 @@ void RTSPServer::RTSPClientSession
// This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
double rangeStart = 0.0, rangeEnd = 0.0;
char* absStart = NULL; char* absEnd = NULL;
- if (parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd)) {
+ Boolean startTimeIsNow;
+ if (parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd, startTimeIsNow)) {
delete[] absStart; delete[] absEnd;
fStreamAfterSETUP = True;
} else if (parsePlayNowHeader(fullRequestStr)) {
@@ -1600,7 +1561,11 @@ void RTSPServer::RTSPClientSession
}
// Then, get server parameters from the 'subsession':
- int tcpSocketNum = streamingMode == RTP_TCP ? ourClientConnection->fClientOutputSocket : -1;
+ if (streamingMode == RTP_TCP) {
+ // Note that we'll be streaming over the RTSP TCP connection:
+ fStreamStates[trackNum].tcpSocketNum = ourClientConnection->fClientOutputSocket;
+ fOurRTSPServer.noteTCPStreamingOnSocket(fStreamStates[trackNum].tcpSocketNum, this, trackNum);
+ }
netAddressBits destinationAddress = 0;
u_int8_t destinationTTL = 255;
#ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
@@ -1630,18 +1595,18 @@ void RTSPServer::RTSPClientSession
subsession->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
clientRTPPort, clientRTCPPort,
- tcpSocketNum, rtpChannelId, rtcpChannelId,
+ fStreamStates[trackNum].tcpSocketNum, rtpChannelId, rtcpChannelId,
destinationAddress, destinationTTL, fIsMulticast,
serverRTPPort, serverRTCPPort,
- fStreamStates[streamNum].streamToken);
+ fStreamStates[trackNum].streamToken);
SendingInterfaceAddr = origSendingInterfaceAddr;
ReceivingInterfaceAddr = origReceivingInterfaceAddr;
AddressString destAddrStr(destinationAddress);
AddressString sourceAddrStr(sourceAddr);
char timeoutParameterString[100];
- if (fOurServer.fReclamationTestSeconds > 0) {
- sprintf(timeoutParameterString, ";timeout=%u", fOurServer.fReclamationTestSeconds);
+ if (fOurRTSPServer.fReclamationSeconds > 0) {
+ sprintf(timeoutParameterString, ";timeout=%u", fOurRTSPServer.fReclamationSeconds);
} else {
timeoutParameterString[0] = '\0';
}
@@ -1695,16 +1660,20 @@ void RTSPServer::RTSPClientSession
break;
}
case RTP_TCP: {
- snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
- "RTSP/1.0 200 OK\r\n"
- "CSeq: %s\r\n"
- "%s"
- "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
- "Session: %08X%s\r\n\r\n",
- ourClientConnection->fCurrentCSeq,
- dateHeader(),
- destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
- fOurSessionId, timeoutParameterString);
+ if (!fOurRTSPServer.fAllowStreamingRTPOverTCP) {
+ ourClientConnection->handleCmd_unsupportedTransport();
+ } else {
+ snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
+ "RTSP/1.0 200 OK\r\n"
+ "CSeq: %s\r\n"
+ "%s"
+ "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
+ "Session: %08X%s\r\n\r\n",
+ ourClientConnection->fCurrentCSeq,
+ dateHeader(),
+ destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
+ fOurSessionId, timeoutParameterString);
+ }
break;
}
case RAW_UDP: {
@@ -1797,6 +1766,7 @@ void RTSPServer::RTSPClientSession
if (subsession == NULL /* means: aggregated operation */
|| subsession == fStreamStates[i].subsession) {
if (fStreamStates[i].subsession != NULL) {
+ fOurRTSPServer.unnoteTCPStreamingOnSocket(fStreamStates[i].tcpSocketNum, this, i);
fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
fStreamStates[i].subsession = NULL;
}
@@ -1820,7 +1790,8 @@ void RTSPServer::RTSPClientSession
void RTSPServer::RTSPClientSession
::handleCmd_PLAY(RTSPServer::RTSPClientConnection* ourClientConnection,
ServerMediaSubsession* subsession, char const* fullRequestStr) {
- char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, ourClientConnection->fClientInputSocket);
+ char* rtspURL
+ = fOurRTSPServer.rtspURL(fOurServerMediaSession, ourClientConnection->fClientInputSocket);
unsigned rtspURLSize = strlen(rtspURL);
// Parse the client's "Scale:" header, if any:
@@ -1847,7 +1818,9 @@ void RTSPServer::RTSPClientSession
float duration = 0.0;
double rangeStart = 0.0, rangeEnd = 0.0;
char* absStart = NULL; char* absEnd = NULL;
- Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd);
+ Boolean startTimeIsNow;
+ Boolean sawRangeHeader
+ = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd, startTimeIsNow);
if (sawRangeHeader && absStart == NULL/*not seeking by 'absolute' time*/) {
// Use this information, plus the stream's duration (if known), to create our own "Range:" header, for the response:
@@ -1859,8 +1832,8 @@ void RTSPServer::RTSPClientSession
duration = -duration;
}
- // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header) have sane values
- // before we send back our own "Range:" header in our response:
+ // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header)
+ // have sane values, before we send back our own "Range:" header in our response:
if (rangeStart < 0.0) rangeStart = 0.0;
else if (rangeStart > duration) rangeStart = duration;
if (rangeEnd < 0.0) rangeEnd = 0.0;
@@ -1887,66 +1860,49 @@ void RTSPServer::RTSPClientSession
unsigned i, numRTPInfoItems = 0;
// Do any required seeking/scaling on each subsession, before starting streaming.
- // (However, we don't do this if the "PLAY" request was for just a single subsession of a multiple-subsession stream;
- // for such streams, seeking/scaling can be done only with an aggregate "PLAY".)
+ // (However, we don't do this if the "PLAY" request was for just a single subsession
+ // of a multiple-subsession stream; for such streams, seeking/scaling can be done
+ // only with an aggregate "PLAY".)
for (i = 0; i < fNumStreamStates; ++i) {
if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {
- if (sawScaleHeader) {
- if (fStreamStates[i].subsession != NULL) {
+ if (fStreamStates[i].subsession != NULL) {
+ if (sawScaleHeader) {
fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale);
}
- }
- if (sawRangeHeader) {
if (absStart != NULL) {
// Special case handling for seeking by 'absolute' time:
-
- if (fStreamStates[i].subsession != NULL) {
- fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);
- }
+
+ fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);
} else {
// Seeking by relative (NPT) time:
- double streamDuration = 0.0; // by default; means: stream until the end of the media
- if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places
- // We want the stream to end early. Set the duration we want:
- streamDuration = rangeEnd - rangeStart;
- if (streamDuration < 0.0) streamDuration = -streamDuration; // should happen only if scale < 0.0
- }
- if (fStreamStates[i].subsession != NULL) {
- u_int64_t numBytes;
+ u_int64_t numBytes;
+ if (!sawRangeHeader || startTimeIsNow) {
+ // We're resuming streaming without seeking, so we just do a 'null' seek
+ // (to get our NPT, and to specify when to end streaming):
+ fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken,
+ rangeEnd, numBytes);
+ } else {
+ // We do a real 'seek':
+ double streamDuration = 0.0; // by default; means: stream until the end of the media
+ if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) {
+ // the 0.001 is because we limited the values to 3 decimal places
+ // We want the stream to end early. Set the duration we want:
+ streamDuration = rangeEnd - rangeStart;
+ if (streamDuration < 0.0) streamDuration = -streamDuration;
+ // should happen only if scale < 0.0
+ }
fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken,
rangeStart, streamDuration, numBytes);
}
}
- } else {
- // No "Range:" header was specified in the "PLAY", so we do a 'null' seek (i.e., we don't seek at all):
- if (fStreamStates[i].subsession != NULL) {
- fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken);
- }
}
}
}
// Create the "Range:" header that we'll send back in our response.
// (Note that we do this after seeking, in case the seeking operation changed the range start time.)
- char* rangeHeader;
- if (!sawRangeHeader) {
- // There wasn't a "Range:" header in the request, so, in our response, begin the range with the current NPT (normal play time):
- float curNPT = 0.0;
- for (i = 0; i < fNumStreamStates; ++i) {
- if (subsession == NULL /* means: aggregated operation */
- || subsession == fStreamStates[i].subsession) {
- if (fStreamStates[i].subsession == NULL) continue;
- float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);
- if (npt > curNPT) curNPT = npt;
- // Note: If this is an aggregate "PLAY" on a multi-subsession stream, then it's conceivable that the NPTs of each subsession
- // may differ (if there has been a previous seek on just one subsession). In this (unusual) case, we just return the
- // largest NPT; I hope that turns out OK...
- }
- }
-
- sprintf(buf, "Range: npt=%.3f-\r\n", curNPT);
- } else if (absStart != NULL) {
+ if (absStart != NULL) {
// We're seeking by 'absolute' time:
if (absEnd == NULL) {
sprintf(buf, "Range: clock=%s-\r\n", absStart);
@@ -1956,13 +1912,31 @@ void RTSPServer::RTSPClientSession
delete[] absStart; delete[] absEnd;
} else {
// We're seeking by relative (NPT) time:
+ if (!sawRangeHeader || startTimeIsNow) {
+ // We didn't seek, so in our response, begin the range with the current NPT (normal play time):
+ float curNPT = 0.0;
+ for (i = 0; i < fNumStreamStates; ++i) {
+ if (subsession == NULL /* means: aggregated operation */
+ || subsession == fStreamStates[i].subsession) {
+ if (fStreamStates[i].subsession == NULL) continue;
+ float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);
+ if (npt > curNPT) curNPT = npt;
+ // Note: If this is an aggregate "PLAY" on a multi-subsession stream,
+ // then it's conceivable that the NPTs of each subsession may differ
+ // (if there has been a previous seek on just one subsession).
+ // In this (unusual) case, we just return the largest NPT; I hope that turns out OK...
+ }
+ }
+ rangeStart = curNPT;
+ }
+
if (rangeEnd == 0.0 && scale >= 0.0) {
sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
} else {
sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
}
}
- rangeHeader = strDup(buf);
+ char* rangeHeader = strDup(buf);
// Now, start streaming:
for (i = 0; i < fNumStreamStates; ++i) {
@@ -2057,126 +2031,37 @@ void RTSPServer::RTSPClientSession
setRTSPResponse(ourClientConnection, "200 OK", fOurSessionId);
}
-RTSPServer::RTSPClientConnection*
+GenericMediaServer::ClientConnection*
RTSPServer::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {
return new RTSPClientConnection(*this, clientSocket, clientAddr);
}
-RTSPServer::RTSPClientSession*
+GenericMediaServer::ClientSession*
RTSPServer::createNewClientSession(u_int32_t sessionId) {
return new RTSPClientSession(*this, sessionId);
}
-void RTSPServer::RTSPClientSession::noteLiveness() {
- if (fOurServer.fReclamationTestSeconds > 0) {
- envir().taskScheduler()
- .rescheduleDelayedTask(fLivenessCheckTask,
- fOurServer.fReclamationTestSeconds*1000000,
- (TaskFunc*)livenessTimeoutTask, this);
- }
-}
-
-void RTSPServer::RTSPClientSession
-::noteClientLiveness(RTSPClientSession* clientSession) {
-#ifdef DEBUG
- char const* streamName
- = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
- fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\"): Liveness indication\n",
- clientSession->fOurSessionId, streamName);
-#endif
- clientSession->noteLiveness();
-}
-
-void RTSPServer::RTSPClientSession
-::livenessTimeoutTask(RTSPClientSession* clientSession) {
- // If this gets called, the client session is assumed to have timed out,
- // so delete it:
-#ifdef DEBUG
- char const* streamName
- = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
- fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\") has timed out (due to inactivity)\n",
- clientSession->fOurSessionId, streamName);
-#endif
- delete clientSession;
-}
-
-
-////////// ServerMediaSessionIterator implementation //////////
-
-RTSPServer::ServerMediaSessionIterator
-::ServerMediaSessionIterator(RTSPServer& server)
- : fOurIterator((server.fServerMediaSessions == NULL)
- ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) {
-}
-
-RTSPServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() {
- delete fOurIterator;
-}
-
-ServerMediaSession* RTSPServer::ServerMediaSessionIterator::next() {
- if (fOurIterator == NULL) return NULL;
-
- char const* key; // dummy
- return (ServerMediaSession*)(fOurIterator->next(key));
-}
-
-
-////////// UserAuthenticationDatabase implementation //////////
-
-UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm,
- Boolean passwordsAreMD5)
- : fTable(HashTable::create(STRING_HASH_KEYS)),
- fRealm(strDup(realm == NULL ? "LIVE555 Streaming Media" : realm)),
- fPasswordsAreMD5(passwordsAreMD5) {
-}
-
-UserAuthenticationDatabase::~UserAuthenticationDatabase() {
- delete[] fRealm;
-
- // Delete the allocated 'password' strings that we stored in the table, and then the table itself:
- char* password;
- while ((password = (char*)fTable->RemoveNext()) != NULL) {
- delete[] password;
- }
- delete fTable;
-}
-
-void UserAuthenticationDatabase::addUserRecord(char const* username,
- char const* password) {
- fTable->Add(username, (void*)(strDup(password)));
-}
-
-void UserAuthenticationDatabase::removeUserRecord(char const* username) {
- char* password = (char*)(fTable->Lookup(username));
- fTable->Remove(username);
- delete[] password;
-}
-
-char const* UserAuthenticationDatabase::lookupPassword(char const* username) {
- return (char const*)(fTable->Lookup(username));
-}
-
///////// RTSPServerWithREGISTERProxying implementation /////////
RTSPServerWithREGISTERProxying* RTSPServerWithREGISTERProxying
::createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER,
- unsigned reclamationTestSeconds,
+ unsigned reclamationSeconds,
Boolean streamRTPOverTCP, int verbosityLevelForProxying) {
int ourSocket = setUpOurSocket(env, ourPort);
if (ourSocket == -1) return NULL;
- return new RTSPServerWithREGISTERProxying(env, ourSocket, ourPort, authDatabase, authDatabaseForREGISTER, reclamationTestSeconds,
+ return new RTSPServerWithREGISTERProxying(env, ourSocket, ourPort, authDatabase, authDatabaseForREGISTER, reclamationSeconds,
streamRTPOverTCP, verbosityLevelForProxying);
}
RTSPServerWithREGISTERProxying
::RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER,
- unsigned reclamationTestSeconds,
+ unsigned reclamationSeconds,
Boolean streamRTPOverTCP, int verbosityLevelForProxying)
- : RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds),
+ : RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationSeconds),
fStreamRTPOverTCP(streamRTPOverTCP), fVerbosityLevelForProxying(verbosityLevelForProxying),
fRegisteredProxyCounter(0), fAllowedCommandNames(NULL), fAuthDBForREGISTER(authDatabaseForREGISTER) {
}
diff --git a/liveMedia/RTSPServerSupportingHTTPStreaming.cpp b/liveMedia/RTSPServerSupportingHTTPStreaming.cpp
index 275c64d..0849bb1 100644
--- a/liveMedia/RTSPServerSupportingHTTPStreaming.cpp
+++ b/liveMedia/RTSPServerSupportingHTTPStreaming.cpp
@@ -14,10 +14,11 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A server that supports both RTSP, and HTTP streaming (using Apple's "HTTP Live Streaming" protocol)
// Implementation
+#include "RTSPServer.hh"
#include "RTSPServerSupportingHTTPStreaming.hh"
#include "RTSPCommon.hh"
#ifndef _WIN32_WCE
@@ -43,7 +44,7 @@ RTSPServerSupportingHTTPStreaming
RTSPServerSupportingHTTPStreaming::~RTSPServerSupportingHTTPStreaming() {
}
-RTSPServer::RTSPClientConnection*
+GenericMediaServer::ClientConnection*
RTSPServerSupportingHTTPStreaming::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {
return new RTSPClientConnectionSupportingHTTPStreaming(*this, clientSocket, clientAddr);
}
@@ -51,11 +52,12 @@ RTSPServerSupportingHTTPStreaming::createNewClientConnection(int clientSocket, s
RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStreaming
::RTSPClientConnectionSupportingHTTPStreaming(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
: RTSPClientConnection(ourServer, clientSocket, clientAddr),
- fClientSessionId(0), fPlaylistSource(NULL), fTCPSink(NULL) {
+ fClientSessionId(0), fStreamSource(NULL), fPlaylistSource(NULL), fTCPSink(NULL) {
}
RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStreaming::~RTSPClientConnectionSupportingHTTPStreaming() {
Medium::close(fPlaylistSource);
+ Medium::close(fStreamSource);
Medium::close(fTCPSink);
}
@@ -112,7 +114,7 @@ void RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStream
u_int8_t destinationTTL = 0;
Boolean isMulticast = False;
void* streamToken;
- subsession->getStreamParameters(fClientSessionId, 0, clientRTPPort,clientRTCPPort, 0,0,0, destinationAddress,destinationTTL, isMulticast, serverRTPPort,serverRTCPPort, streamToken);
+ subsession->getStreamParameters(fClientSessionId, 0, clientRTPPort,clientRTCPPort, -1,0,0, destinationAddress,destinationTTL, isMulticast, serverRTPPort,serverRTCPPort, streamToken);
// Seek the stream source to the desired place, with the desired duration, and (as a side effect) get the number of bytes:
double dOffsetInSeconds = (double)offsetInSeconds;
@@ -144,10 +146,14 @@ void RTSPServerSupportingHTTPStreaming::RTSPClientConnectionSupportingHTTPStream
fResponseBuffer[0] = '\0'; // We've already sent the response. This tells the calling code not to send it again.
// Ask the media source to deliver - to the TCP sink - the desired data:
- FramedSource* mediaSource = subsession->getStreamSource(streamToken);
- if (mediaSource != NULL) {
+ if (fStreamSource != NULL) { // sanity check
+ if (fTCPSink != NULL) fTCPSink->stopPlaying();
+ Medium::close(fStreamSource);
+ }
+ fStreamSource = subsession->getStreamSource(streamToken);
+ if (fStreamSource != NULL) {
if (fTCPSink == NULL) fTCPSink = TCPStreamSink::createNew(envir(), fClientOutputSocket);
- fTCPSink->startPlaying(*mediaSource, afterStreaming, this);
+ fTCPSink->startPlaying(*fStreamSource, afterStreaming, this);
}
} while(0);
diff --git a/liveMedia/SIPClient.cpp b/liveMedia/SIPClient.cpp
index 131b2ff..452fdcf 100644
--- a/liveMedia/SIPClient.cpp
+++ b/liveMedia/SIPClient.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic SIP client
// Implementation
@@ -86,7 +86,7 @@ SIPClient::SIPClient(UsageEnvironment& env,
// Now, find out our source port number. Hack: Do this by first trying to
// send a 0-length packet, so that the "getSourcePort()" call will work.
- fOurSocket->output(envir(), 255, (unsigned char*)"", 0);
+ fOurSocket->output(envir(), (unsigned char*)"", 0);
Port srcPort(0);
getSourcePort(env, fOurSocket->socketNum(), srcPort);
if (srcPort.num() != 0) {
@@ -892,8 +892,7 @@ Boolean SIPClient::sendRequest(char const* requestString,
}
// NOTE: We should really check that "requestLength" is not #####
// too large for UDP (see RFC 3261, section 18.1.1) #####
- return fOurSocket->output(envir(), 255, (unsigned char*)requestString,
- requestLength);
+ return fOurSocket->output(envir(), (unsigned char*)requestString, requestLength);
}
unsigned SIPClient::getResponse(char*& responseBuffer,
diff --git a/liveMedia/ServerMediaSession.cpp b/liveMedia/ServerMediaSession.cpp
index 2093b29..c9c2a8d 100644
--- a/liveMedia/ServerMediaSession.cpp
+++ b/liveMedia/ServerMediaSession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A data structure that represents a session that consists of
// potentially multiple (audio and/or video) sub-sessions
// (This data structure is used for media *streamers* - i.e., servers.
@@ -24,6 +24,9 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "ServerMediaSession.hh"
#include <GroupsockHelper.hh>
#include <math.h>
+#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
+#define snprintf _snprintf
+#endif
////////// ServerMediaSession //////////
@@ -272,30 +275,35 @@ char* ServerMediaSession::generateSDPDescription() {
+ strlen(fDescriptionSDPString)
+ strlen(fInfoSDPString)
+ strlen(fMiscSDPLines);
+ sdpLength += 1000; // in case the length of the "subsession->sdpLines()" calls below change
sdp = new char[sdpLength];
if (sdp == NULL) break;
// Generate the SDP prefix (session-level lines):
- sprintf(sdp, sdpPrefixFmt,
- fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
- 1, // o= <version> // (needs to change if params are modified)
- ipAddressStr.val(), // o= <address>
- fDescriptionSDPString, // s= <description>
- fInfoSDPString, // i= <info>
- libNameStr, libVersionStr, // a=tool:
- sourceFilterLine, // a=source-filter: incl (if a SSM session)
- rangeLine, // a=range: line
- fDescriptionSDPString, // a=x-qt-text-nam: line
- fInfoSDPString, // a=x-qt-text-inf: line
- fMiscSDPLines); // miscellaneous session SDP lines (if any)
+ snprintf(sdp, sdpLength, sdpPrefixFmt,
+ fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
+ 1, // o= <version> // (needs to change if params are modified)
+ ipAddressStr.val(), // o= <address>
+ fDescriptionSDPString, // s= <description>
+ fInfoSDPString, // i= <info>
+ libNameStr, libVersionStr, // a=tool:
+ sourceFilterLine, // a=source-filter: incl (if a SSM session)
+ rangeLine, // a=range: line
+ fDescriptionSDPString, // a=x-qt-text-nam: line
+ fInfoSDPString, // a=x-qt-text-inf: line
+ fMiscSDPLines); // miscellaneous session SDP lines (if any)
// Then, add the (media-level) lines for each subsession:
char* mediaSDP = sdp;
for (subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
- mediaSDP += strlen(mediaSDP);
+ unsigned mediaSDPLength = strlen(mediaSDP);
+ mediaSDP += mediaSDPLength;
+ sdpLength -= mediaSDPLength;
+ if (sdpLength <= 1) break; // the SDP has somehow become too long
+
char const* sdpLines = subsession->sdpLines();
- if (sdpLines != NULL) sprintf(mediaSDP, "%s", sdpLines);
+ if (sdpLines != NULL) snprintf(mediaSDP, sdpLength, "%s", sdpLines);
}
} while (0);
@@ -304,7 +312,7 @@ char* ServerMediaSession::generateSDPDescription() {
}
-////////// ServerMediaSessionIterator //////////
+////////// ServerMediaSubsessionIterator //////////
ServerMediaSubsessionIterator
::ServerMediaSubsessionIterator(ServerMediaSession& session)
@@ -367,8 +375,10 @@ void ServerMediaSubsession::seekStream(unsigned /*clientSessionId*/,
delete[] absStart; absStart = NULL;
delete[] absEnd; absEnd = NULL;
}
-void ServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, void* /*streamToken*/) {
+void ServerMediaSubsession::nullSeekStream(unsigned /*clientSessionId*/, void* /*streamToken*/,
+ double streamEndTime, u_int64_t& numBytes) {
// default implementation: do nothing
+ numBytes = 0;
}
void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,
void* /*streamToken*/, float /*scale*/) {
diff --git a/liveMedia/SimpleRTPSink.cpp b/liveMedia/SimpleRTPSink.cpp
index c5fd2f5..f2e8d23 100644
--- a/liveMedia/SimpleRTPSink.cpp
+++ b/liveMedia/SimpleRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simple RTP sink that packs frames into each outgoing
// packet, without any fragmentation or special headers.
// Implementation
diff --git a/liveMedia/SimpleRTPSource.cpp b/liveMedia/SimpleRTPSource.cpp
index 976bb1a..bdb16cc 100644
--- a/liveMedia/SimpleRTPSource.cpp
+++ b/liveMedia/SimpleRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A RTP source for a simple RTP payload format that
// - doesn't have any special headers following the RTP header
// - doesn't have any special framing apart from the packet data itself
diff --git a/liveMedia/StreamParser.cpp b/liveMedia/StreamParser.cpp
index e1cd82e..1f38176 100644
--- a/liveMedia/StreamParser.cpp
+++ b/liveMedia/StreamParser.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Abstract class for parsing a byte stream
// Implementation
@@ -85,7 +85,7 @@ unsigned StreamParser::getBits(unsigned numBits) {
lastByte >>= (fRemainingUnparsedBits - numBits);
fRemainingUnparsedBits -= numBits;
- return (unsigned)lastByte &~ ((~0)<<numBits);
+ return (unsigned)lastByte &~ ((~0u)<<numBits);
} else {
unsigned char lastByte;
if (fRemainingUnparsedBits > 0) {
@@ -102,7 +102,7 @@ unsigned StreamParser::getBits(unsigned numBits) {
result >>= (32 - remainingBits);
result |= (lastByte << remainingBits);
- if (numBits < 32) result &=~ ((~0)<<numBits);
+ if (numBits < 32) result &=~ ((~0u)<<numBits);
unsigned const numRemainingBytes = (remainingBits+7)/8;
fCurParserIndex += numRemainingBytes;
diff --git a/liveMedia/StreamParser.hh b/liveMedia/StreamParser.hh
index 6c5ad06..e766f55 100644
--- a/liveMedia/StreamParser.hh
+++ b/liveMedia/StreamParser.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Abstract class for parsing a byte stream
// C++ header
@@ -74,7 +74,7 @@ protected: // we're a virtual base class
fRemainingUnparsedBits = 0;
return curBank()[fCurParserIndex++];
}
- u_int8_t test1Byte(unsigned numBytes) { // as above, but doesn't advance ptr
+ u_int8_t test1Byte() { // as above, but doesn't advance ptr
ensureValidBytes(1);
return nextToParse()[0];
}
diff --git a/liveMedia/StreamReplicator.cpp b/liveMedia/StreamReplicator.cpp
index 87618c5..a047635 100644
--- a/liveMedia/StreamReplicator.cpp
+++ b/liveMedia/StreamReplicator.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// An class that can be used to create (possibly multiple) 'replicas' of an incoming stream.
// Implementation.
@@ -38,7 +38,6 @@ private:
private:
StreamReplicator& fOurReplicator;
int fFrameIndex; // 0 or 1, depending upon which frame we're currently requesting; could also be -1 if we've stopped playing
- Boolean fDeliveryInProgress;
// Replicas that are currently awaiting data are kept in a (singly-linked) list:
StreamReplica* fNext;
@@ -69,7 +68,7 @@ FramedSource* StreamReplicator::createStreamReplica() {
void StreamReplicator::getNextFrame(StreamReplica* replica) {
if (fInputSourceHasClosed) { // handle closure instead
- FramedSource::handleClosure(replica);
+ replica->handleClosure();
return;
}
@@ -104,12 +103,15 @@ void StreamReplicator::getNextFrame(StreamReplica* replica) {
}
void StreamReplicator::deactivateStreamReplica(StreamReplica* replicaBeingDeactivated) {
+ if (replicaBeingDeactivated->fFrameIndex == -1) return; // this replica has already been deactivated (or was never activated at all)
+
// Assert: fNumActiveReplicas > 0
- if (fNumReplicas == 0) fprintf(stderr, "StreamReplicator::deactivateStreamReplica() Internal Error!\n"); // should not happen
+ if (fNumActiveReplicas == 0) fprintf(stderr, "StreamReplicator::deactivateStreamReplica() Internal Error!\n"); // should not happen
--fNumActiveReplicas;
+ replicaBeingDeactivated->fFrameIndex = -1;
- if (replicaBeingDeactivated->fDeliveryInProgress) --fNumDeliveriesMadeSoFar;
- // hack in case we're called while in the middle of a frame delivery to us
+ // Forget about any frame delivery that might have just been made to this replica:
+ if (replicaBeingDeactivated->fFrameIndex != fFrameIndex && fNumDeliveriesMadeSoFar > 0) --fNumDeliveriesMadeSoFar;
// Check whether the replica being deactivated is the 'master' replica, or is enqueued awaiting a frame:
if (replicaBeingDeactivated == fMasterReplica) {
@@ -177,26 +179,29 @@ void StreamReplicator::deactivateStreamReplica(StreamReplica* replicaBeingDeacti
}
}
}
+
+ // Check for the possibility that - now that a replica has been deactivated - all other
+ // replicas have received the current frame, and so now we need to complete delivery to
+ // the master replica:
+ if (fMasterReplica != NULL && fInputSource != NULL && !fInputSource->isCurrentlyAwaitingData()) deliverReceivedFrame();
}
if (fNumActiveReplicas == 0 && fInputSource != NULL) fInputSource->stopGettingFrames(); // tell our source to stop too
}
void StreamReplicator::removeStreamReplica(StreamReplica* replicaBeingRemoved) {
+ // First, handle the replica that's being removed the same way that we would if it were merely being deactivated:
+ deactivateStreamReplica(replicaBeingRemoved);
+
// Assert: fNumReplicas > 0
if (fNumReplicas == 0) fprintf(stderr, "StreamReplicator::removeStreamReplica() Internal Error!\n"); // should not happen
--fNumReplicas;
// If this was the last replica, then delete ourselves (if we were set up to do so):
if (fNumReplicas == 0 && fDeleteWhenLastReplicaDies) {
- delete this;
+ Medium::close(this);
return;
}
-
- // Now handle the replica that's being removed the same way that we would if it were merely being deactivated:
- if (replicaBeingRemoved->fFrameIndex != -1) { // i.e., we haven't already done this
- deactivateStreamReplica(replicaBeingRemoved);
- }
}
void StreamReplicator::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes,
@@ -228,16 +233,16 @@ void StreamReplicator::onSourceClosure() {
while ((replica = fReplicasAwaitingCurrentFrame) != NULL) {
fReplicasAwaitingCurrentFrame = replica->fNext;
replica->fNext = NULL;
- FramedSource::handleClosure(replica);
+ replica->handleClosure();
}
while ((replica = fReplicasAwaitingNextFrame) != NULL) {
fReplicasAwaitingNextFrame = replica->fNext;
replica->fNext = NULL;
- FramedSource::handleClosure(replica);
+ replica->handleClosure();
}
if ((replica = fMasterReplica) != NULL) {
fMasterReplica = NULL;
- FramedSource::handleClosure(replica);
+ replica->handleClosure();
}
}
@@ -250,8 +255,6 @@ void StreamReplicator::deliverReceivedFrame() {
fReplicasAwaitingCurrentFrame = replica->fNext;
replica->fNext = NULL;
- replica->fDeliveryInProgress = True;
-
// Assert: fMasterReplica != NULL
if (fMasterReplica == NULL) fprintf(stderr, "StreamReplicator::deliverReceivedFrame() Internal Error 1!\n"); // shouldn't happen
StreamReplica::copyReceivedFrame(replica, fMasterReplica);
@@ -263,8 +266,6 @@ void StreamReplicator::deliverReceivedFrame() {
// Complete delivery to this replica:
FramedSource::afterGetting(replica);
-
- replica->fDeliveryInProgress = False;
}
if (fNumDeliveriesMadeSoFar == fNumActiveReplicas - 1 && fMasterReplica != NULL) {
@@ -292,6 +293,7 @@ void StreamReplicator::deliverReceivedFrame() {
fReplicasAwaitingCurrentFrame = fReplicasAwaitingNextFrame;
fReplicasAwaitingNextFrame = NULL;
+ // Complete delivery to the 'master' replica (thereby completing all deliveries for this frame):
FramedSource::afterGetting(replica);
}
}
@@ -302,7 +304,7 @@ void StreamReplicator::deliverReceivedFrame() {
StreamReplica::StreamReplica(StreamReplicator& ourReplicator)
: FramedSource(ourReplicator.envir()),
fOurReplicator(ourReplicator),
- fFrameIndex(-1/*we haven't started playing yet*/), fDeliveryInProgress(False), fNext(NULL) {
+ fFrameIndex(-1/*we haven't started playing yet*/), fNext(NULL) {
}
StreamReplica::~StreamReplica() {
@@ -314,10 +316,7 @@ void StreamReplica::doGetNextFrame() {
}
void StreamReplica::doStopGettingFrames() {
- if (fFrameIndex != -1) { // we had been activated
- fFrameIndex = -1; // When we start reading again, this will tell the replicator that we were previously inactive.
- fOurReplicator.deactivateStreamReplica(this);
- }
+ fOurReplicator.deactivateStreamReplica(this);
}
void StreamReplica::copyReceivedFrame(StreamReplica* toReplica, StreamReplica* fromReplica) {
diff --git a/liveMedia/T140TextMatroskaFileServerMediaSubsession.cpp b/liveMedia/T140TextMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index ab89b2c..0000000
--- a/liveMedia/T140TextMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an text (subtitle) track within a Matroska file.
-// Implementation
-
-#include "T140TextMatroskaFileServerMediaSubsession.hh"
-#include "T140TextRTPSink.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-T140TextMatroskaFileServerMediaSubsession* T140TextMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new T140TextMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-T140TextMatroskaFileServerMediaSubsession
-::T140TextMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber) {
-}
-
-T140TextMatroskaFileServerMediaSubsession
-::~T140TextMatroskaFileServerMediaSubsession() {
-}
-
-float T140TextMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void T140TextMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- ((MatroskaDemuxedTrack*)inputSource)->seekToTime(seekNPT);
-}
-
-FramedSource* T140TextMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- estBitrate = 48; // kbps, estimate
-
- return fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
-}
-
-RTPSink* T140TextMatroskaFileServerMediaSubsession
-::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
- return T140TextRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
-}
diff --git a/liveMedia/T140TextMatroskaFileServerMediaSubsession.hh b/liveMedia/T140TextMatroskaFileServerMediaSubsession.hh
deleted file mode 100644
index e0e0edb..0000000
--- a/liveMedia/T140TextMatroskaFileServerMediaSubsession.hh
+++ /dev/null
@@ -1,54 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from an text (subtitle) track within a Matroska file.
-// C++ header
-
-#ifndef _T140_TEXT_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _T140_TEXT_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-
-#ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
-#include "FileServerMediaSubsession.hh"
-#endif
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#include "MatroskaFileServerDemux.hh"
-#endif
-
-class T140TextMatroskaFileServerMediaSubsession: public FileServerMediaSubsession {
-public:
- static T140TextMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
-
-private:
- T140TextMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~T140TextMatroskaFileServerMediaSubsession();
-
-private: // redefined virtual functions
- virtual float duration() const;
- virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
- virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
- unsigned& estBitrate);
- virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
-
-private:
- MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
-};
-
-#endif
diff --git a/liveMedia/T140TextRTPSink.cpp b/liveMedia/T140TextRTPSink.cpp
index 24c8a18..a88daec 100644
--- a/liveMedia/T140TextRTPSink.cpp
+++ b/liveMedia/T140TextRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for T.140 text (RFC 2793)
// Implementation
@@ -180,5 +180,5 @@ void T140IdleFilter::onSourceClosure() {
envir().taskScheduler().unscheduleDelayedTask(fIdleTimerTask);
fIdleTimerTask = NULL;
- FramedSource::handleClosure(this);
+ handleClosure();
}
diff --git a/liveMedia/TCPStreamSink.cpp b/liveMedia/TCPStreamSink.cpp
index 8f83dfa..3a6a1bf 100644
--- a/liveMedia/TCPStreamSink.cpp
+++ b/liveMedia/TCPStreamSink.cpp
@@ -14,11 +14,12 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A sink representing a TCP output stream
// Implementation
#include "TCPStreamSink.hh"
+#include <GroupsockHelper.hh> // for "ignoreSigPipeOnSocket()"
TCPStreamSink* TCPStreamSink::createNew(UsageEnvironment& env, int socketNum) {
return new TCPStreamSink(env, socketNum);
@@ -29,9 +30,12 @@ TCPStreamSink::TCPStreamSink(UsageEnvironment& env, int socketNum)
fUnwrittenBytesStart(0), fUnwrittenBytesEnd(0),
fInputSourceIsOpen(False), fOutputSocketIsWritable(True),
fOutputSocketNum(socketNum) {
+ ignoreSigPipeOnSocket(socketNum);
}
TCPStreamSink::~TCPStreamSink() {
+ // Turn off any pending background handling of our output socket:
+ envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum);
}
Boolean TCPStreamSink::continuePlaying() {
@@ -68,9 +72,7 @@ void TCPStreamSink::processBuffer() {
// Then, read from our input source, if we can (& we're not already reading from it):
if (fInputSourceIsOpen && freeBufferSpace() >= TCP_STREAM_SINK_MIN_READ_SIZE && !fSource->isCurrentlyAwaitingData()) {
fSource->getNextFrame(&fBuffer[fUnwrittenBytesEnd], freeBufferSpace(), afterGettingFrame, this, ourOnSourceClosure, this);
- }
-
- if (!fInputSourceIsOpen && numUnwrittenBytes() == 0) {
+ } else if (!fInputSourceIsOpen && numUnwrittenBytes() == 0) {
// We're now done:
onSourceClosure();
}
diff --git a/liveMedia/TextRTPSink.cpp b/liveMedia/TextRTPSink.cpp
index 48333d2..636b86a 100644
--- a/liveMedia/TextRTPSink.cpp
+++ b/liveMedia/TextRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTP sink for text codecs (abstract base class)
// Implementation
diff --git a/liveMedia/TheoraVideoRTPSink.cpp b/liveMedia/TheoraVideoRTPSink.cpp
index 9bef5c4..0964ad0 100644
--- a/liveMedia/TheoraVideoRTPSink.cpp
+++ b/liveMedia/TheoraVideoRTPSink.cpp
@@ -1,98 +1,105 @@
-/*
- * Theora Video RTP packetizer
- * Copied from live555's VorbisAudioRTPSink
- */
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// RTP sink for Theora video
+// Implementation
#include "TheoraVideoRTPSink.hh"
#include "Base64.hh"
+#include "VorbisAudioRTPSource.hh" // for parseVorbisOrTheoraConfigStr()
+#include "VorbisAudioRTPSink.hh" // for generateVorbisOrTheoraConfigStr()
+
+TheoraVideoRTPSink* TheoraVideoRTPSink
+::createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField) {
+ return new TheoraVideoRTPSink(env, RTPgs,
+ rtpPayloadFormat,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize, identField);
+}
+
+TheoraVideoRTPSink* TheoraVideoRTPSink
+::createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ char const* configStr) {
+ // Begin by decoding and unpacking the configuration string:
+ u_int8_t* identificationHeader; unsigned identificationHeaderSize;
+ u_int8_t* commentHeader; unsigned commentHeaderSize;
+ u_int8_t* setupHeader; unsigned setupHeaderSize;
+ u_int32_t identField;
-TheoraVideoRTPSink::TheoraVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat,
- u_int32_t rtpTimestampFrequency,
- unsigned width, unsigned height, enum PixFmt pf,
- u_int8_t* identificationHeader, unsigned identificationHeaderSize,
- u_int8_t* commentHeader, unsigned commentHeaderSize,
- u_int8_t* setupHeader, unsigned setupHeaderSize,
- u_int32_t identField)
- : VideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "theora"),
+ parseVorbisOrTheoraConfigStr(configStr,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+
+ TheoraVideoRTPSink* resultSink
+ = new TheoraVideoRTPSink(env, RTPgs, rtpPayloadFormat,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+ delete[] identificationHeader; delete[] commentHeader; delete[] setupHeader;
+
+ return resultSink;
+}
+
+TheoraVideoRTPSink
+::TheoraVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField)
+ : VideoRTPSink(env, RTPgs, rtpPayloadFormat, 90000, "THEORA"),
fIdent(identField), fFmtpSDPLine(NULL) {
static const char *pf_to_str[] = {
"YCbCr-4:2:0",
+ "Reserved",
"YCbCr-4:2:2",
"YCbCr-4:4:4",
};
- // Create packed configuration headers, and encode this data into a "a=fmtp:" SDP line that we'll use to describe it:
-
- // First, count how many headers (<=3) are included, and how many bytes will be used to encode these headers' sizes:
- unsigned numHeaders = 0;
- unsigned sizeSize[2]; // The number of bytes used to encode the lengths of the first two headers (but not the length of the 3rd)
- sizeSize[0] = sizeSize[1] = 0;
- if (identificationHeaderSize > 0) {
- sizeSize[numHeaders++] = identificationHeaderSize < 128 ? 1 : identificationHeaderSize < 16384 ? 2 : 3;
- }
- if (commentHeaderSize > 0) {
- sizeSize[numHeaders++] = commentHeaderSize < 128 ? 1 : commentHeaderSize < 16384 ? 2 : 3;
- }
- if (setupHeaderSize > 0) {
- ++numHeaders;
- } else {
- sizeSize[1] = 0; // We have at most two headers, so the second one's length isn't encoded
+ unsigned width = 1280; // default value
+ unsigned height = 720; // default value
+ unsigned pf = 0; // default value
+ if (identificationHeaderSize >= 42) {
+ // Parse this header to get the "width", "height", "pf" (pixel format), and
+ // 'nominal bitrate' parameters:
+ u_int8_t* p = identificationHeader; // alias
+ width = (p[14]<<16)|(p[15]<<8)|p[16];
+ height = (p[17]<<16)|(p[18]<<8)|p[19];
+ pf = (p[41]&0x18)>>3;
+ unsigned nominalBitrate = (p[37]<<16)|(p[38]<<8)|p[39];
+ if (nominalBitrate > 0) estimatedBitrate() = nominalBitrate/1000;
}
- if (numHeaders == 0) return; // With no headers, we can't set up a configuration
- if (numHeaders == 1) sizeSize[0] = 0; // With only one header, its length isn't encoded
-
- // Then figure out the size of the packed configuration headers, and allocate space for this:
- unsigned length = identificationHeaderSize + commentHeaderSize + setupHeaderSize; // The "length" field in the packed headers
- if (length > (unsigned)0xFFFF) return; // too big for a 16-bit field; we can't handle this
- unsigned packedHeadersSize
- = 4 // "Number of packed headers" field
- + 3 // "ident" field
- + 2 // "length" field
- + 1 // "n. of headers" field
- + sizeSize[0] + sizeSize[1] // "length1" and "length2" (if present) fields
- + length;
- u_int8_t* packedHeaders = new u_int8_t[packedHeadersSize];
- if (packedHeaders == NULL) return;
-
- // Fill in the 'packed headers':
- u_int8_t* p = packedHeaders;
- *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; // "Number of packed headers": 1
- *p++ = fIdent>>16; *p++ = fIdent>>8; *p++ = fIdent; // "Ident" (24 bits)
- *p++ = length>>8; *p++ = length; // "length" (16 bits)
- *p++ = numHeaders-1; // "n. of headers"
- if (numHeaders > 1) {
- // Fill in the "length1" header:
- unsigned length1 = identificationHeaderSize > 0 ? identificationHeaderSize : commentHeaderSize;
- if (length1 >= 16384) {
- *p++ = 0x80; // flag, but no more, because we know length1 <= 32767
- }
- if (length1 >= 128) {
- *p++ = 0x80|((length1&0x3F80)>>7); // flag + the second 7 bits
- }
- *p++ = length1&0x7F; // the low 7 bits
-
- if (numHeaders > 2) { // numHeaders == 3
- // Fill in the "length2" header (for the 'Comment' header):
- unsigned length2 = commentHeaderSize;
- if (length2 >= 16384) {
- *p++ = 0x80; // flag, but no more, because we know length2 <= 32767
- }
- if (length2 >= 128) {
- *p++ = 0x80|((length2&0x3F80)>>7); // flag + the second 7 bits
- }
- *p++ = length2&0x7F; // the low 7 bits
- }
- }
- // Copy each header:
- if (identificationHeader != NULL) memmove(p, identificationHeader, identificationHeaderSize); p += identificationHeaderSize;
- if (commentHeader != NULL) memmove(p, commentHeader, commentHeaderSize); p += commentHeaderSize;
- if (setupHeader != NULL) memmove(p, setupHeader, setupHeaderSize);
-
- // Having set up the 'packed configuration headers', Base-64-encode this, and put it in our "a=fmtp:" SDP line:
- char* base64PackedHeaders = base64Encode((char const*)packedHeaders, packedHeadersSize);
- delete[] packedHeaders;
-
+
+ // Generate a 'config' string from the supplied configuration headers:
+ char* base64PackedHeaders
+ = generateVorbisOrTheoraConfigStr(identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+ if (base64PackedHeaders == NULL) return;
+
+ // Then use this 'config' string to construct our "a=fmtp:" SDP line:
unsigned fmtpSDPLineMaxSize = 200 + strlen(base64PackedHeaders);// 200 => more than enough space
fFmtpSDPLine = new char[fmtpSDPLineMaxSize];
sprintf(fFmtpSDPLine, "a=fmtp:%d sampling=%s;width=%u;height=%u;delivery-method=out_band/rtsp;configuration=%s\r\n", rtpPayloadType(), pf_to_str[pf], width, height, base64PackedHeaders);
@@ -103,22 +110,6 @@ TheoraVideoRTPSink::~TheoraVideoRTPSink() {
delete[] fFmtpSDPLine;
}
-TheoraVideoRTPSink*
-TheoraVideoRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency,
- unsigned width, unsigned height, enum PixFmt pf,
- u_int8_t* identificationHeader, unsigned identificationHeaderSize,
- u_int8_t* commentHeader, unsigned commentHeaderSize,
- u_int8_t* setupHeader, unsigned setupHeaderSize,
- u_int32_t identField) {
- return new TheoraVideoRTPSink(env, RTPgs,
- rtpPayloadFormat, rtpTimestampFrequency,
- width, height, pf,
- identificationHeader, identificationHeaderSize,
- commentHeader, commentHeaderSize,
- setupHeader, setupHeaderSize, identField);
-}
-
char const* TheoraVideoRTPSink::auxSDPLine() {
return fFmtpSDPLine;
}
diff --git a/liveMedia/VorbisAudioRTPSource.cpp b/liveMedia/TheoraVideoRTPSource.cpp
similarity index 62%
copy from liveMedia/VorbisAudioRTPSource.cpp
copy to liveMedia/TheoraVideoRTPSource.cpp
index 7ba026a..759b65c 100644
--- a/liveMedia/VorbisAudioRTPSource.cpp
+++ b/liveMedia/TheoraVideoRTPSource.cpp
@@ -14,52 +14,50 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// Vorbis Audio RTP Sources
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// Theora Video RTP Sources
// Implementation
-#include "VorbisAudioRTPSource.hh"
+#include "TheoraVideoRTPSource.hh"
-////////// VorbisBufferedPacket and VorbisBufferedPacketFactory //////////
+////////// TheoraBufferedPacket and TheoraBufferedPacketFactory //////////
-class VorbisBufferedPacket: public BufferedPacket {
+class TheoraBufferedPacket: public BufferedPacket {
public:
- VorbisBufferedPacket();
- virtual ~VorbisBufferedPacket();
+ TheoraBufferedPacket();
+ virtual ~TheoraBufferedPacket();
private: // redefined virtual functions
virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
unsigned dataSize);
};
-class VorbisBufferedPacketFactory: public BufferedPacketFactory {
+class TheoraBufferedPacketFactory: public BufferedPacketFactory {
private: // redefined virtual functions
virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
};
-///////// MPEG4VorbisAudioRTPSource implementation ////////
+///////// TheoraVideoRTPSource implementation ////////
-VorbisAudioRTPSource*
-VorbisAudioRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
- unsigned char rtpPayloadFormat,
- unsigned rtpTimestampFrequency) {
- return new VorbisAudioRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency);
+TheoraVideoRTPSource*
+TheoraVideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat) {
+ return new TheoraVideoRTPSource(env, RTPgs, rtpPayloadFormat);
}
-VorbisAudioRTPSource
-::VorbisAudioRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
- unsigned char rtpPayloadFormat,
- unsigned rtpTimestampFrequency)
- : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency,
- new VorbisBufferedPacketFactory),
+TheoraVideoRTPSource
+::TheoraVideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat)
+ : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, 90000,
+ new TheoraBufferedPacketFactory),
fCurPacketIdent(0) {
}
-VorbisAudioRTPSource::~VorbisAudioRTPSource() {
+TheoraVideoRTPSource::~TheoraVideoRTPSource() {
}
-Boolean VorbisAudioRTPSource
+Boolean TheoraVideoRTPSource
::processSpecialHeader(BufferedPacket* packet,
unsigned& resultSpecialHeaderSize) {
unsigned char* headerStart = packet->data();
@@ -71,8 +69,8 @@ Boolean VorbisAudioRTPSource
// The first 3 bytes of the header are the "Ident" field:
fCurPacketIdent = (headerStart[0]<<16) | (headerStart[1]<<8) | headerStart[2];
- // The 4th byte is F|VDT|numPkts.
- // Reject any packet with VDT == 3:
+ // The 4th byte is F|TDT|numPkts.
+ // Reject any packet with TDT == 3:
if ((headerStart[3]&0x30) == 0x30) return False;
u_int8_t F = headerStart[3]>>6;
@@ -82,20 +80,20 @@ Boolean VorbisAudioRTPSource
return True;
}
-char const* VorbisAudioRTPSource::MIMEtype() const {
- return "audio/VORBIS";
+char const* TheoraVideoRTPSource::MIMEtype() const {
+ return "video/THEORA";
}
-////////// VorbisBufferedPacket and VorbisBufferedPacketFactory implementation //////////
+////////// TheoraBufferedPacket and TheoraBufferedPacketFactory implementation //////////
-VorbisBufferedPacket::VorbisBufferedPacket() {
+TheoraBufferedPacket::TheoraBufferedPacket() {
}
-VorbisBufferedPacket::~VorbisBufferedPacket() {
+TheoraBufferedPacket::~TheoraBufferedPacket() {
}
-unsigned VorbisBufferedPacket
+unsigned TheoraBufferedPacket
::nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
if (dataSize < 2) {
// There's not enough space for a 2-byte header. TARFU! Just return the data that's left:
@@ -109,7 +107,7 @@ unsigned VorbisBufferedPacket
return frameSize;
}
-BufferedPacket* VorbisBufferedPacketFactory
+BufferedPacket* TheoraBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource* /*ourSource*/) {
- return new VorbisBufferedPacket();
+ return new TheoraBufferedPacket();
}
diff --git a/liveMedia/VP8VideoMatroskaFileServerMediaSubsession.cpp b/liveMedia/VP8VideoMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index 124d069..0000000
--- a/liveMedia/VP8VideoMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from a VP8 Video track within a Matroska file.
-// Implementation
-
-#include "VP8VideoMatroskaFileServerMediaSubsession.hh"
-#include "VP8VideoRTPSink.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-VP8VideoMatroskaFileServerMediaSubsession* VP8VideoMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new VP8VideoMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-VP8VideoMatroskaFileServerMediaSubsession
-::VP8VideoMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber) {
-}
-
-VP8VideoMatroskaFileServerMediaSubsession
-::~VP8VideoMatroskaFileServerMediaSubsession() {
-}
-
-float VP8VideoMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void VP8VideoMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- ((MatroskaDemuxedTrack*)inputSource)->seekToTime(seekNPT);
-}
-
-FramedSource* VP8VideoMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- estBitrate = 500; // kbps, estimate
-
- return fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
-}
-
-RTPSink* VP8VideoMatroskaFileServerMediaSubsession
-::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
- return VP8VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
-}
diff --git a/liveMedia/VP8VideoRTPSink.cpp b/liveMedia/VP8VideoRTPSink.cpp
index 30d7bf0..fb32e08 100644
--- a/liveMedia/VP8VideoRTPSink.cpp
+++ b/liveMedia/VP8VideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for VP8 video
// Implementation
diff --git a/liveMedia/VP8VideoRTPSource.cpp b/liveMedia/VP8VideoRTPSource.cpp
index 6e31e67..19f2218 100644
--- a/liveMedia/VP8VideoRTPSource.cpp
+++ b/liveMedia/VP8VideoRTPSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// VP8 Video RTP Sources
// Implementation
@@ -38,6 +38,8 @@ VP8VideoRTPSource
VP8VideoRTPSource::~VP8VideoRTPSource() {
}
+#define incrHeader do { ++resultSpecialHeaderSize; ++headerStart; if (--packetSize == 0) return False; } while (0)
+
Boolean VP8VideoRTPSource
::processSpecialHeader(BufferedPacket* packet,
unsigned& resultSpecialHeaderSize) {
@@ -48,7 +50,7 @@ Boolean VP8VideoRTPSource
if (packetSize == 0) return False; // error
resultSpecialHeaderSize = 1; // unless we learn otherwise
- u_int8_t const byte1 = headerStart[0];
+ u_int8_t const byte1 = *headerStart;
Boolean const X = (byte1&0x80) != 0;
Boolean const S = (byte1&0x10) != 0;
u_int8_t const PartID = byte1&0x0F;
@@ -57,23 +59,23 @@ Boolean VP8VideoRTPSource
fCurrentPacketCompletesFrame = packet->rtpMarkerBit(); // RTP header's "M" bit
if (X) {
- ++resultSpecialHeaderSize;
+ incrHeader;
- u_int8_t const byte2 = headerStart[1];
+ u_int8_t const byte2 = *headerStart;
Boolean const I = (byte2&0x80) != 0;
Boolean const L = (byte2&0x40) != 0;
Boolean const T = (byte2&0x20) != 0;
Boolean const K = (byte2&0x10) != 0;
if (I) {
- ++resultSpecialHeaderSize;
- if (headerStart[2]&0x80) { // extension flag in the PictureID is set
- ++resultSpecialHeaderSize;
+ incrHeader;
+ if ((*headerStart)&0x80) { // extension flag in the PictureID is set
+ incrHeader;
}
}
- if (L) ++resultSpecialHeaderSize;
- if (T||K) ++resultSpecialHeaderSize;
+ if (L) incrHeader;
+ if (T||K) incrHeader;
}
return True;
diff --git a/liveMedia/VP8VideoRTPSink.cpp b/liveMedia/VP9VideoRTPSink.cpp
similarity index 60%
copy from liveMedia/VP8VideoRTPSink.cpp
copy to liveMedia/VP9VideoRTPSink.cpp
index 30d7bf0..7d37eee 100644
--- a/liveMedia/VP8VideoRTPSink.cpp
+++ b/liveMedia/VP9VideoRTPSink.cpp
@@ -14,55 +14,58 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// RTP sink for VP8 video
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// RTP sink for VP9 video
// Implementation
-#include "VP8VideoRTPSink.hh"
+#include "VP9VideoRTPSink.hh"
-VP8VideoRTPSink
-::VP8VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat)
- : VideoRTPSink(env, RTPgs, rtpPayloadFormat, 90000, "VP8") {
+VP9VideoRTPSink
+::VP9VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat)
+ : VideoRTPSink(env, RTPgs, rtpPayloadFormat, 90000, "VP9") {
}
-VP8VideoRTPSink::~VP8VideoRTPSink() {
+VP9VideoRTPSink::~VP9VideoRTPSink() {
}
-VP8VideoRTPSink*
-VP8VideoRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat) {
- return new VP8VideoRTPSink(env, RTPgs, rtpPayloadFormat);
+VP9VideoRTPSink*
+VP9VideoRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat) {
+ return new VP9VideoRTPSink(env, RTPgs, rtpPayloadFormat);
}
-Boolean VP8VideoRTPSink
+Boolean VP9VideoRTPSink
::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/,
unsigned /*numBytesInFrame*/) const {
// A packet can contain only one frame
return False;
}
-void VP8VideoRTPSink
+void VP9VideoRTPSink
::doSpecialFrameHandling(unsigned fragmentationOffset,
unsigned char* /*frameStart*/,
unsigned /*numBytesInFrame*/,
struct timeval framePresentationTime,
unsigned numRemainingBytes) {
- // Set the "VP8 Payload Descriptor" (just the minimal required 1-byte version):
- u_int8_t vp8PayloadDescriptor = fragmentationOffset == 0 ? 0x10 : 0x00;
- // X = R = N = 0; PartID = 0; S = 1 iff this is the first (or only) fragment of the frame
- setSpecialHeaderBytes(&vp8PayloadDescriptor, 1);
+ // Set the "VP9 Payload Descriptor" (just the minimal required 1-byte version):
+ u_int8_t vp9PayloadDescriptor = fragmentationOffset == 0 ? 0x10 : 0x00;
+ // I = L = F = V = U = 0; S = 1 iff this is the first (or only) fragment of the frame
if (numRemainingBytes == 0) {
// This packet contains the last (or only) fragment of the frame.
- // Set the RTP 'M' ('marker') bit:
+ // Set the E bit:
+ vp9PayloadDescriptor |= 0x08;
+ // Also set the RTP 'M' ('marker') bit:
setMarkerBit();
}
+ setSpecialHeaderBytes(&vp9PayloadDescriptor, 1);
+
// Also set the RTP timestamp:
setTimestamp(framePresentationTime);
}
-unsigned VP8VideoRTPSink::specialHeaderSize() const {
- // We include only the required 1-byte form of the "VP8 Payload Descriptor":
+unsigned VP9VideoRTPSink::specialHeaderSize() const {
+ // We include only the required 1-byte form of the "VP9 Payload Descriptor":
return 1;
}
diff --git a/liveMedia/VP9VideoRTPSource.cpp b/liveMedia/VP9VideoRTPSource.cpp
new file mode 100644
index 0000000..39bee29
--- /dev/null
+++ b/liveMedia/VP9VideoRTPSource.cpp
@@ -0,0 +1,108 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// VP9 Video RTP Sources
+// Implementation
+
+#include "VP9VideoRTPSource.hh"
+
+VP9VideoRTPSource*
+VP9VideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat,
+ unsigned rtpTimestampFrequency) {
+ return new VP9VideoRTPSource(env, RTPgs, rtpPayloadFormat,
+ rtpTimestampFrequency);
+}
+
+VP9VideoRTPSource
+::VP9VideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat,
+ unsigned rtpTimestampFrequency)
+ : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency) {
+}
+
+VP9VideoRTPSource::~VP9VideoRTPSource() {
+}
+
+#define incrHeader do { ++resultSpecialHeaderSize; ++headerStart; if (--packetSize == 0) return False; } while (0)
+
+Boolean VP9VideoRTPSource
+::processSpecialHeader(BufferedPacket* packet,
+ unsigned& resultSpecialHeaderSize) {
+ unsigned char* headerStart = packet->data();
+ unsigned packetSize = packet->dataSize();
+
+ // Figure out the size of the special header.
+ if (packetSize == 0) return False; // error
+ resultSpecialHeaderSize = 1; // unless we learn otherwise
+
+ u_int8_t const byte1 = *headerStart;
+ Boolean const I = (byte1&0x80) != 0;
+ Boolean const L = (byte1&0x40) != 0;
+ Boolean const F = (byte1&0x20) != 0;
+ Boolean const B = (byte1&0x10) != 0;
+ Boolean const E = (byte1&0x08) != 0;
+ Boolean const V = (byte1&0x04) != 0;
+ Boolean const U = (byte1&0x02) != 0;
+
+ fCurrentPacketBeginsFrame = B;
+ fCurrentPacketCompletesFrame = E;
+ // use this instead of the RTP header's 'M' bit (which might not be accurate)
+
+ if (I) { // PictureID present
+ incrHeader;
+ Boolean const M = ((*headerStart)&0x80) != 0;
+ if (M) incrHeader;
+ }
+
+ if (L) { // Layer indices present
+ incrHeader;
+ if (F) { // Reference indices present
+ incrHeader;
+ unsigned R = (*headerStart)&0x03;
+ while (R-- > 0) {
+ incrHeader;
+ Boolean const X = ((*headerStart)&0x10) != 0;
+ if (X) incrHeader;
+ }
+ }
+ }
+
+ if (V) { // Scalability Structure (SS) present
+ incrHeader;
+ unsigned patternLength = *headerStart;
+ while (patternLength-- > 0) {
+ incrHeader;
+ unsigned R = (*headerStart)&0x03;
+ while (R-- > 0) {
+ incrHeader;
+ Boolean const X = ((*headerStart)&0x10) != 0;
+ if (X) incrHeader;
+ }
+ }
+ }
+
+ if (U) { // Scalability Structure Update (SU) present
+ return False; // This structure isn't yet defined in the VP9 payload format I-D
+ }
+
+ return True;
+}
+
+char const* VP9VideoRTPSource::MIMEtype() const {
+ return "video/VP9";
+}
diff --git a/liveMedia/VideoRTPSink.cpp b/liveMedia/VideoRTPSink.cpp
index 82d5a10..818da19 100644
--- a/liveMedia/VideoRTPSink.cpp
+++ b/liveMedia/VideoRTPSink.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTP sink for video codecs (abstract base class)
// Implementation
diff --git a/liveMedia/VorbisAudioMatroskaFileServerMediaSubsession.cpp b/liveMedia/VorbisAudioMatroskaFileServerMediaSubsession.cpp
deleted file mode 100644
index 1913d5b..0000000
--- a/liveMedia/VorbisAudioMatroskaFileServerMediaSubsession.cpp
+++ /dev/null
@@ -1,177 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from a Vorbis audio track within a Matroska file.
-// Implementation
-
-#include "VorbisAudioMatroskaFileServerMediaSubsession.hh"
-#include "VorbisAudioRTPSink.hh"
-#include "MatroskaDemuxedTrack.hh"
-
-VorbisAudioMatroskaFileServerMediaSubsession* VorbisAudioMatroskaFileServerMediaSubsession
-::createNew(MatroskaFileServerDemux& demux, unsigned trackNumber) {
- return new VorbisAudioMatroskaFileServerMediaSubsession(demux, trackNumber);
-}
-
-#define getPrivByte(b) if (n == 0) break; else do {b = *p++; --n;} while (0)
-
-VorbisAudioMatroskaFileServerMediaSubsession
-::VorbisAudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber)
- : FileServerMediaSubsession(demux.envir(), demux.fileName(), False),
- fOurDemux(demux), fTrackNumber(trackNumber),
- fIdentificationHeader(NULL), fIdentificationHeaderSize(0),
- fCommentHeader(NULL), fCommentHeaderSize(0),
- fSetupHeader(NULL), fSetupHeaderSize(0),
- fEstBitrate(96/* kbps, default guess */) {
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
-
- // The Matroska file's 'Codec Private' data is assumed to be the Vorbis configuration information,
- // containing the "Identification", "Comment", and "Setup" headers. Extract these headers now:
- do {
- u_int8_t* p = track->codecPrivate;
- unsigned n = track->codecPrivateSize;
- if (n == 0 || p == NULL) break; // we have no 'Codec Private' data
-
- u_int8_t numHeaders;
- getPrivByte(numHeaders);
- unsigned headerSize[3]; // we don't handle any more than 2+1 headers
-
- // Extract the sizes of each of these headers:
- unsigned sizesSum = 0;
- Boolean success = True;
- unsigned i;
- for (i = 0; i < numHeaders && i < 3; ++i) {
- unsigned len = 0;
- u_int8_t c;
-
- do {
- success = False;
- getPrivByte(c);
- success = True;
-
- len += c;
- } while (c == 255);
- if (!success || len == 0) break;
-
- headerSize[i] = len;
- sizesSum += len;
- }
- if (!success) break;
-
- // Compute the implicit size of the final header:
- if (numHeaders < 3) {
- int finalHeaderSize = n - sizesSum;
- if (finalHeaderSize <= 0) break; // error in data; give up
-
- headerSize[numHeaders] = (unsigned)finalHeaderSize;
- ++numHeaders; // include the final header now
- } else {
- numHeaders = 3; // The maximum number of headers that we handle
- }
-
- // Then, extract and classify each header:
- for (i = 0; i < numHeaders; ++i) {
- success = False;
- unsigned newHeaderSize = headerSize[i];
- u_int8_t* newHeader = new u_int8_t[newHeaderSize];
- if (newHeader == NULL) break;
-
- u_int8_t* hdr = newHeader;
- while (newHeaderSize-- > 0) {
- success = False;
- getPrivByte(*hdr++);
- success = True;
- }
- if (!success) {
- delete[] newHeader;
- break;
- }
-
- u_int8_t headerType = newHeader[0];
- if (headerType == 1) {
- delete[] fIdentificationHeader; fIdentificationHeader = newHeader;
- fIdentificationHeaderSize = headerSize[i];
-
- if (fIdentificationHeaderSize >= 28) {
- // Get the 'bitrate' values from this header, and use them to set "fEstBitrate":
- u_int32_t val;
- u_int8_t* p;
-
- p = &fIdentificationHeader[16];
- val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian
- int bitrate_maximum = (int)val;
- if (bitrate_maximum < 0) bitrate_maximum = 0;
-
- p = &fIdentificationHeader[20];
- val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian
- int bitrate_nominal = (int)val;
- if (bitrate_nominal < 0) bitrate_nominal = 0;
-
- p = &fIdentificationHeader[24];
- val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian
- int bitrate_minimum = (int)val;
- if (bitrate_minimum < 0) bitrate_minimum = 0;
-
- int bitrate
- = bitrate_nominal>0 ? bitrate_nominal : bitrate_maximum>0 ? bitrate_maximum : bitrate_minimum>0 ? bitrate_minimum : 0;
- if (bitrate > 0) fEstBitrate = ((unsigned)bitrate)/1000;
- }
- } else if (headerType == 3) {
- delete[] fCommentHeader; fCommentHeader = newHeader;
- fCommentHeaderSize = headerSize[i];
- } else if (headerType == 5) {
- delete[] fSetupHeader; fSetupHeader = newHeader;
- fSetupHeaderSize = headerSize[i];
- } else {
- delete[] newHeader; // because it was a header type that we don't understand
- }
- }
- if (!success) break;
- } while (0);
-}
-
-VorbisAudioMatroskaFileServerMediaSubsession
-::~VorbisAudioMatroskaFileServerMediaSubsession() {
- delete[] fIdentificationHeader;
- delete[] fCommentHeader;
- delete[] fSetupHeader;
-}
-
-float VorbisAudioMatroskaFileServerMediaSubsession::duration() const { return fOurDemux.fileDuration(); }
-
-void VorbisAudioMatroskaFileServerMediaSubsession
-::seekStreamSource(FramedSource* inputSource, double& seekNPT, double /*streamDuration*/, u_int64_t& /*numBytes*/) {
- ((MatroskaDemuxedTrack*)inputSource)->seekToTime(seekNPT);
-}
-
-FramedSource* VorbisAudioMatroskaFileServerMediaSubsession
-::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate) {
- estBitrate = fEstBitrate; // kbps, estimate
-
- return fOurDemux.newDemuxedTrack(clientSessionId, fTrackNumber);
-}
-
-RTPSink* VorbisAudioMatroskaFileServerMediaSubsession
-::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) {
- MatroskaTrack* track = fOurDemux.lookup(fTrackNumber);
- return VorbisAudioRTPSink::createNew(envir(), rtpGroupsock,
- rtpPayloadTypeIfDynamic, track->samplingFrequency, track->numChannels,
- fIdentificationHeader, fIdentificationHeaderSize,
- fCommentHeader, fCommentHeaderSize,
- fSetupHeader, fSetupHeaderSize);
-}
diff --git a/liveMedia/VorbisAudioMatroskaFileServerMediaSubsession.hh b/liveMedia/VorbisAudioMatroskaFileServerMediaSubsession.hh
deleted file mode 100644
index b53e55e..0000000
--- a/liveMedia/VorbisAudioMatroskaFileServerMediaSubsession.hh
+++ /dev/null
@@ -1,60 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
-// on demand, from a Vorbis audio track within a Matroska file.
-// C++ header
-
-#ifndef _VORBIS_AUDIO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-#define _VORBIS_AUDIO_MATROSKA_FILE_SERVER_MEDIA_SUBSESSION_HH
-
-#ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
-#include "FileServerMediaSubsession.hh"
-#endif
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#include "MatroskaFileServerDemux.hh"
-#endif
-
-class VorbisAudioMatroskaFileServerMediaSubsession: public FileServerMediaSubsession {
-public:
- static VorbisAudioMatroskaFileServerMediaSubsession*
- createNew(MatroskaFileServerDemux& demux, unsigned trackNumber);
-
-private:
- VorbisAudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber);
- // called only by createNew();
- virtual ~VorbisAudioMatroskaFileServerMediaSubsession();
-
-private: // redefined virtual functions
- virtual float duration() const;
- virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
- virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
- unsigned& estBitrate);
- virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
-
-private:
- MatroskaFileServerDemux& fOurDemux;
- unsigned fTrackNumber;
-
- u_int8_t* fIdentificationHeader; unsigned fIdentificationHeaderSize;
- u_int8_t* fCommentHeader; unsigned fCommentHeaderSize;
- u_int8_t* fSetupHeader; unsigned fSetupHeaderSize;
-
- unsigned fEstBitrate; // in kbps
-};
-
-#endif
diff --git a/liveMedia/VorbisAudioRTPSink.cpp b/liveMedia/VorbisAudioRTPSink.cpp
index 8fd72b8..9ee5261 100644
--- a/liveMedia/VorbisAudioRTPSink.cpp
+++ b/liveMedia/VorbisAudioRTPSink.cpp
@@ -14,94 +14,101 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for Vorbis audio
// Implementation
#include "VorbisAudioRTPSink.hh"
#include "Base64.hh"
+#include "VorbisAudioRTPSource.hh" // for parseVorbisOrTheoraConfigStr()
+
+VorbisAudioRTPSink* VorbisAudioRTPSink
+::createNew(UsageEnvironment& env, Groupsock* RTPgs,
+ u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels,
+ u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField) {
+ return new VorbisAudioRTPSink(env, RTPgs,
+ rtpPayloadFormat, rtpTimestampFrequency, numChannels,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+}
-VorbisAudioRTPSink::VorbisAudioRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat,
- u_int32_t rtpTimestampFrequency,
- unsigned numChannels,
- u_int8_t* identificationHeader, unsigned identificationHeaderSize,
- u_int8_t* commentHeader, unsigned commentHeaderSize,
- u_int8_t* setupHeader, unsigned setupHeaderSize,
- u_int32_t identField)
- : AudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "VORBIS", numChannels),
- fIdent(identField), fFmtpSDPLine(NULL) {
- // Create packed configuration headers, and encode this data into a "a=fmtp:" SDP line that we'll use to describe it:
-
- // First, count how many headers (<=3) are included, and how many bytes will be used to encode these headers' sizes:
- unsigned numHeaders = 0;
- unsigned sizeSize[2]; // The number of bytes used to encode the lengths of the first two headers (but not the length of the 3rd)
- sizeSize[0] = sizeSize[1] = 0;
- if (identificationHeaderSize > 0) {
- sizeSize[numHeaders++] = identificationHeaderSize < 128 ? 1 : identificationHeaderSize < 16384 ? 2 : 3;
- }
- if (commentHeaderSize > 0) {
- sizeSize[numHeaders++] = commentHeaderSize < 128 ? 1 : commentHeaderSize < 16384 ? 2 : 3;
- }
- if (setupHeaderSize > 0) {
- ++numHeaders;
- } else {
- sizeSize[1] = 0; // We have at most two headers, so the second one's length isn't encoded
- }
- if (numHeaders == 0) return; // With no headers, we can't set up a configuration
- if (numHeaders == 1) sizeSize[0] = 0; // With only one header, its length isn't encoded
-
- // Then figure out the size of the packed configuration headers, and allocate space for this:
- unsigned length = identificationHeaderSize + commentHeaderSize + setupHeaderSize; // The "length" field in the packed headers
- if (length > (unsigned)0xFFFF) return; // too big for a 16-bit field; we can't handle this
- unsigned packedHeadersSize
- = 4 // "Number of packed headers" field
- + 3 // "ident" field
- + 2 // "length" field
- + 1 // "n. of headers" field
- + sizeSize[0] + sizeSize[1] // "length1" and "length2" (if present) fields
- + length;
- u_int8_t* packedHeaders = new u_int8_t[packedHeadersSize];
- if (packedHeaders == NULL) return;
+VorbisAudioRTPSink* VorbisAudioRTPSink
+::createNew(UsageEnvironment& env, Groupsock* RTPgs,u_int8_t rtpPayloadFormat,
+ u_int32_t rtpTimestampFrequency, unsigned numChannels,
+ char const* configStr) {
+ // Begin by decoding and unpacking the configuration string:
+ u_int8_t* identificationHeader; unsigned identificationHeaderSize;
+ u_int8_t* commentHeader; unsigned commentHeaderSize;
+ u_int8_t* setupHeader; unsigned setupHeaderSize;
+ u_int32_t identField;
+
+ parseVorbisOrTheoraConfigStr(configStr,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+
+ VorbisAudioRTPSink* resultSink
+ = new VorbisAudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, numChannels,
+ identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+ delete[] identificationHeader; delete[] commentHeader; delete[] setupHeader;
- // Fill in the 'packed headers':
- u_int8_t* p = packedHeaders;
- *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; // "Number of packed headers": 1
- *p++ = fIdent>>16; *p++ = fIdent>>8; *p++ = fIdent; // "Ident" (24 bits)
- *p++ = length>>8; *p++ = length; // "length" (16 bits)
- *p++ = numHeaders-1; // "n. of headers"
- if (numHeaders > 1) {
- // Fill in the "length1" header:
- unsigned length1 = identificationHeaderSize > 0 ? identificationHeaderSize : commentHeaderSize;
- if (length1 >= 16384) {
- *p++ = 0x80; // flag, but no more, because we know length1 <= 32767
- }
- if (length1 >= 128) {
- *p++ = 0x80|((length1&0x3F80)>>7); // flag + the second 7 bits
- }
- *p++ = length1&0x7F; // the low 7 bits
+ return resultSink;
+}
- if (numHeaders > 2) { // numHeaders == 3
- // Fill in the "length2" header (for the 'Comment' header):
- unsigned length2 = commentHeaderSize;
- if (length2 >= 16384) {
- *p++ = 0x80; // flag, but no more, because we know length2 <= 32767
- }
- if (length2 >= 128) {
- *p++ = 0x80|((length2&0x3F80)>>7); // flag + the second 7 bits
- }
- *p++ = length2&0x7F; // the low 7 bits
- }
+VorbisAudioRTPSink
+::VorbisAudioRTPSink(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ u_int32_t rtpTimestampFrequency, unsigned numChannels,
+ u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField)
+ : AudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "VORBIS", numChannels),
+ fIdent(identField), fFmtpSDPLine(NULL) {
+ if (identificationHeaderSize >= 28) {
+ // Get the 'bitrate' values from this header, and use them to set our estimated bitrate:
+ u_int32_t val;
+ u_int8_t* p;
+
+ p = &identificationHeader[16];
+ val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian
+ int bitrate_maximum = (int)val;
+ if (bitrate_maximum < 0) bitrate_maximum = 0;
+
+ p = &identificationHeader[20];
+ val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian
+ int bitrate_nominal = (int)val;
+ if (bitrate_nominal < 0) bitrate_nominal = 0;
+
+ p = &identificationHeader[24];
+ val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian
+ int bitrate_minimum = (int)val;
+ if (bitrate_minimum < 0) bitrate_minimum = 0;
+
+ int bitrate
+ = bitrate_nominal > 0 ? bitrate_nominal
+ : bitrate_maximum > 0 ? bitrate_maximum
+ : bitrate_minimum > 0 ? bitrate_minimum : 0;
+ if (bitrate > 0) estimatedBitrate() = ((unsigned)bitrate)/1000;
}
- // Copy each header:
- if (identificationHeader != NULL) memmove(p, identificationHeader, identificationHeaderSize); p += identificationHeaderSize;
- if (commentHeader != NULL) memmove(p, commentHeader, commentHeaderSize); p += commentHeaderSize;
- if (setupHeader != NULL) memmove(p, setupHeader, setupHeaderSize);
- // Having set up the 'packed configuration headers', Base-64-encode this, and put it in our "a=fmtp:" SDP line:
- char* base64PackedHeaders = base64Encode((char const*)packedHeaders, packedHeadersSize);
- delete[] packedHeaders;
+ // Generate a 'config' string from the supplied configuration headers:
+ char* base64PackedHeaders
+ = generateVorbisOrTheoraConfigStr(identificationHeader, identificationHeaderSize,
+ commentHeader, commentHeaderSize,
+ setupHeader, setupHeaderSize,
+ identField);
+ if (base64PackedHeaders == NULL) return;
+ // Then use this 'config' string to construct our "a=fmtp:" SDP line:
unsigned fmtpSDPLineMaxSize = 50 + strlen(base64PackedHeaders); // 50 => more than enough space
fFmtpSDPLine = new char[fmtpSDPLineMaxSize];
sprintf(fFmtpSDPLine, "a=fmtp:%d configuration=%s\r\n", rtpPayloadType(), base64PackedHeaders);
@@ -112,106 +119,6 @@ VorbisAudioRTPSink::~VorbisAudioRTPSink() {
delete[] fFmtpSDPLine;
}
-VorbisAudioRTPSink*
-VorbisAudioRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels,
- u_int8_t* identificationHeader, unsigned identificationHeaderSize,
- u_int8_t* commentHeader, unsigned commentHeaderSize,
- u_int8_t* setupHeader, unsigned setupHeaderSize) {
- return new VorbisAudioRTPSink(env, RTPgs,
- rtpPayloadFormat, rtpTimestampFrequency, numChannels,
- identificationHeader, identificationHeaderSize,
- commentHeader, commentHeaderSize,
- setupHeader, setupHeaderSize);
-}
-
-#define ADVANCE(n) do { p += (n); rem -= (n); } while (0)
-#define GET_ENCODED_VAL(n) do { u_int8_t byte; n = 0; do { if (rem == 0) break; byte = *p; n = (n*128) + (byte&0x7F); ADVANCE(1); } while (byte&0x80); } while (0); if (rem == 0) break
-
-VorbisAudioRTPSink*
-VorbisAudioRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels,
- char const* configStr) {
- u_int8_t* identificationHeader = NULL; unsigned identificationHeaderSize = 0;
- u_int8_t* commentHeader = NULL; unsigned commentHeaderSize = 0;
- u_int8_t* setupHeader = NULL; unsigned setupHeaderSize = 0;
- VorbisAudioRTPSink* resultSink = NULL;
-
- // Begin by Base64-decoding the configuration string:
- unsigned configDataSize;
- u_int8_t* configData = base64Decode(configStr, configDataSize);
- u_int8_t* p = configData;
- unsigned rem = configDataSize;
-
- do {
- if (rem < 4) break;
- u_int32_t numPackedHeaders = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; ADVANCE(4);
-
- if (numPackedHeaders == 0) break;
- // Use the first 'packed header' only.
-
- if (rem < 3) break;
- u_int32_t ident = (p[0]<<16)|(p[1]<<8)|p[2]; ADVANCE(3);
-
- if (rem < 2) break;
- u_int16_t length = (p[0]<<8)|p[1]; ADVANCE(2);
-
- unsigned numHeaders;
- GET_ENCODED_VAL(numHeaders);
-
- Boolean success = False;
- for (unsigned i = 0; i < numHeaders+1 && i < 3; ++i) {
- success = False;
- unsigned headerSize;
- if (i < numHeaders) {
- // The header size is encoded:
- GET_ENCODED_VAL(headerSize);
- if (headerSize > length) break;
- length -= headerSize;
- } else {
- // The last header is implicit:
- headerSize = length;
- }
-
- // Allocate space for the header bytes; we'll fill it in later
- if (i == 0) {
- identificationHeaderSize = headerSize;
- identificationHeader = new u_int8_t[identificationHeaderSize];
- } else if (i == 1) {
- commentHeaderSize = headerSize;
- commentHeader = new u_int8_t[commentHeaderSize];
- } else { // i == 2
- setupHeaderSize = headerSize;
- setupHeader = new u_int8_t[setupHeaderSize];
- }
-
- success = True;
- }
- if (!success) break;
-
- // Copy the remaining config bytes into the appropriate 'header' buffers:
- if (identificationHeader != NULL) {
- memmove(identificationHeader, p, identificationHeaderSize); ADVANCE(identificationHeaderSize);
- if (commentHeader != NULL) {
- memmove(commentHeader, p, commentHeaderSize); ADVANCE(commentHeaderSize);
- if (setupHeader != NULL) {
- memmove(setupHeader, p, setupHeaderSize); ADVANCE(setupHeaderSize);
- }
- }
- }
-
- resultSink = new VorbisAudioRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, numChannels,
- identificationHeader, identificationHeaderSize,
- commentHeader, commentHeaderSize,
- setupHeader, setupHeaderSize,
- ident);
- } while (0);
-
- delete[] configData;
- delete[] identificationHeader; delete[] commentHeader; delete[] setupHeader;
- return resultSink;
-}
-
char const* VorbisAudioRTPSink::auxSDPLine() {
return fFmtpSDPLine;
}
@@ -225,7 +132,7 @@ void VorbisAudioRTPSink
// Set the 4-byte "payload header", as defined in RFC 5215, section 2.2:
u_int8_t header[4];
- // The three bytes of the header are our "Ident":
+ // The first three bytes of the header are our "Ident":
header[0] = fIdent>>16; header[1] = fIdent>>8; header[2] = fIdent;
// The final byte contains the "F", "VDT", and "numPkts" fields:
@@ -276,3 +183,84 @@ unsigned VorbisAudioRTPSink::specialHeaderSize() const {
unsigned VorbisAudioRTPSink::frameSpecificHeaderSize() const {
return 2;
}
+
+
+////////// generateVorbisOrTheoraConfigStr() implementation //////////
+
+char* generateVorbisOrTheoraConfigStr(u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField) {
+ // First, count how many headers (<=3) are included, and how many bytes will be used
+ // to encode these headers' sizes:
+ unsigned numHeaders = 0;
+ unsigned sizeSize[2]; // The number of bytes used to encode the lengths of the first two headers (but not the length of the 3rd)
+ sizeSize[0] = sizeSize[1] = 0;
+ if (identificationHeaderSize > 0) {
+ sizeSize[numHeaders++] = identificationHeaderSize < 128 ? 1 : identificationHeaderSize < 16384 ? 2 : 3;
+ }
+ if (commentHeaderSize > 0) {
+ sizeSize[numHeaders++] = commentHeaderSize < 128 ? 1 : commentHeaderSize < 16384 ? 2 : 3;
+ }
+ if (setupHeaderSize > 0) {
+ ++numHeaders;
+ } else {
+ sizeSize[1] = 0; // We have at most two headers, so the second one's length isn't encoded
+ }
+ if (numHeaders == 0) return NULL; // With no headers, we can't set up a configuration
+ if (numHeaders == 1) sizeSize[0] = 0; // With only one header, its length isn't encoded
+
+ // Then figure out the size of the packed configuration headers, and allocate space for this:
+ unsigned length = identificationHeaderSize + commentHeaderSize + setupHeaderSize;
+ // The "length" field in the packed headers
+ if (length > (unsigned)0xFFFF) return NULL; // too big for a 16-bit field; we can't handle this
+ unsigned packedHeadersSize
+ = 4 // "Number of packed headers" field
+ + 3 // "ident" field
+ + 2 // "length" field
+ + 1 // "n. of headers" field
+ + sizeSize[0] + sizeSize[1] // "length1" and "length2" (if present) fields
+ + length;
+ u_int8_t* packedHeaders = new u_int8_t[packedHeadersSize];
+ if (packedHeaders == NULL) return NULL;
+
+ // Fill in the 'packed headers':
+ u_int8_t* p = packedHeaders;
+ *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; // "Number of packed headers": 1
+ *p++ = identField>>16; *p++ = identField>>8; *p++ = identField; // "Ident" (24 bits)
+ *p++ = length>>8; *p++ = length; // "length" (16 bits)
+ *p++ = numHeaders-1; // "n. of headers"
+ if (numHeaders > 1) {
+ // Fill in the "length1" header:
+ unsigned length1 = identificationHeaderSize > 0 ? identificationHeaderSize : commentHeaderSize;
+ if (length1 >= 16384) {
+ *p++ = 0x80; // flag, but no more, because we know length1 <= 32767
+ }
+ if (length1 >= 128) {
+ *p++ = 0x80|((length1&0x3F80)>>7); // flag + the second 7 bits
+ }
+ *p++ = length1&0x7F; // the low 7 bits
+
+ if (numHeaders > 2) { // numHeaders == 3
+ // Fill in the "length2" header (for the 'Comment' header):
+ unsigned length2 = commentHeaderSize;
+ if (length2 >= 16384) {
+ *p++ = 0x80; // flag, but no more, because we know length2 <= 32767
+ }
+ if (length2 >= 128) {
+ *p++ = 0x80|((length2&0x3F80)>>7); // flag + the second 7 bits
+ }
+ *p++ = length2&0x7F; // the low 7 bits
+ }
+ }
+ // Copy each header:
+ if (identificationHeader != NULL) memmove(p, identificationHeader, identificationHeaderSize); p += identificationHeaderSize;
+ if (commentHeader != NULL) memmove(p, commentHeader, commentHeaderSize); p += commentHeaderSize;
+ if (setupHeader != NULL) memmove(p, setupHeader, setupHeaderSize);
+
+ // Having set up the 'packed configuration headers', Base-64-encode this, for our result:
+ char* base64PackedHeaders = base64Encode((char const*)packedHeaders, packedHeadersSize);
+ delete[] packedHeaders;
+
+ return base64PackedHeaders;
+}
diff --git a/liveMedia/VorbisAudioRTPSource.cpp b/liveMedia/VorbisAudioRTPSource.cpp
index 7ba026a..af488f2 100644
--- a/liveMedia/VorbisAudioRTPSource.cpp
+++ b/liveMedia/VorbisAudioRTPSource.cpp
@@ -14,11 +14,12 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Vorbis Audio RTP Sources
// Implementation
#include "VorbisAudioRTPSource.hh"
+#include "Base64.hh"
////////// VorbisBufferedPacket and VorbisBufferedPacketFactory //////////
@@ -38,7 +39,7 @@ private: // redefined virtual functions
};
-///////// MPEG4VorbisAudioRTPSource implementation ////////
+///////// VorbisAudioRTPSource implementation ////////
VorbisAudioRTPSource*
VorbisAudioRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
@@ -113,3 +114,84 @@ BufferedPacket* VorbisBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource* /*ourSource*/) {
return new VorbisBufferedPacket();
}
+
+
+////////// parseVorbisOrTheoraConfigStr() implementation //////////
+
+#define ADVANCE(n) do { p += (n); rem -= (n); } while (0)
+#define GET_ENCODED_VAL(n) do { u_int8_t byte; n = 0; do { if (rem == 0) break; byte = *p; n = (n*128) + (byte&0x7F); ADVANCE(1); } while (byte&0x80); } while (0); if (rem == 0) break
+
+void parseVorbisOrTheoraConfigStr(char const* configStr,
+ u_int8_t*& identificationHdr, unsigned& identificationHdrSize,
+ u_int8_t*& commentHdr, unsigned& commentHdrSize,
+ u_int8_t*& setupHdr, unsigned& setupHdrSize,
+ u_int32_t& identField) {
+ identificationHdr = commentHdr = setupHdr = NULL; // default values, if an error occur
+ identificationHdrSize = commentHdrSize = setupHdrSize = 0; // ditto
+ identField = 0; // ditto
+
+ // Begin by Base64-decoding the configuration string:
+ unsigned configDataSize;
+ u_int8_t* configData = base64Decode(configStr, configDataSize);
+ u_int8_t* p = configData;
+ unsigned rem = configDataSize;
+
+ do {
+ if (rem < 4) break;
+ u_int32_t numPackedHeaders = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; ADVANCE(4);
+ if (numPackedHeaders == 0) break;
+
+ // Use the first 'packed header' only.
+ if (rem < 3) break;
+ identField = (p[0]<<16)|(p[1]<<8)|p[2]; ADVANCE(3);
+
+ if (rem < 2) break;
+ u_int16_t length = (p[0]<<8)|p[1]; ADVANCE(2);
+
+ unsigned numHeaders;
+ GET_ENCODED_VAL(numHeaders);
+
+ Boolean success = False;
+ for (unsigned i = 0; i < numHeaders+1 && i < 3; ++i) {
+ success = False;
+ unsigned headerSize;
+ if (i < numHeaders) {
+ // The header size is encoded:
+ GET_ENCODED_VAL(headerSize);
+ if (headerSize > length) break;
+ length -= headerSize;
+ } else {
+ // The last header is implicit:
+ headerSize = length;
+ }
+
+ // Allocate space for the header bytes; we'll fill it in later
+ if (i == 0) {
+ identificationHdrSize = headerSize;
+ identificationHdr = new u_int8_t[identificationHdrSize];
+ } else if (i == 1) {
+ commentHdrSize = headerSize;
+ commentHdr = new u_int8_t[commentHdrSize];
+ } else { // i == 2
+ setupHdrSize = headerSize;
+ setupHdr = new u_int8_t[setupHdrSize];
+ }
+
+ success = True;
+ }
+ if (!success) break;
+
+ // Copy the remaining config bytes into the appropriate 'header' buffers:
+ if (identificationHdr != NULL) {
+ memmove(identificationHdr, p, identificationHdrSize); ADVANCE(identificationHdrSize);
+ if (commentHdr != NULL) {
+ memmove(commentHdr, p, commentHdrSize); ADVANCE(commentHdrSize);
+ if (setupHdr != NULL) {
+ memmove(setupHdr, p, setupHdrSize); ADVANCE(setupHdrSize);
+ }
+ }
+ }
+ } while (0);
+
+ delete[] configData;
+}
diff --git a/liveMedia/WAVAudioFileServerMediaSubsession.cpp b/liveMedia/WAVAudioFileServerMediaSubsession.cpp
index 15bb457..314de94 100644
--- a/liveMedia/WAVAudioFileServerMediaSubsession.cpp
+++ b/liveMedia/WAVAudioFileServerMediaSubsession.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an WAV audio file.
// Implementation
@@ -56,11 +56,27 @@ void WAVAudioFileServerMediaSubsession
unsigned seekSampleNumber = (unsigned)(seekNPT*fSamplingFrequency);
unsigned seekByteNumber = seekSampleNumber*((fNumChannels*fBitsPerSample)/8);
+ wavSource->seekToPCMByte(seekByteNumber);
+
+ setStreamSourceDuration(inputSource, streamDuration, numBytes);
+}
+
+void WAVAudioFileServerMediaSubsession
+::setStreamSourceDuration(FramedSource* inputSource, double streamDuration, u_int64_t& numBytes) {
+ WAVAudioFileSource* wavSource;
+ if (fBitsPerSample > 8) {
+ // "inputSource" is a filter; its input source is the original WAV file source:
+ wavSource = (WAVAudioFileSource*)(((FramedFilter*)inputSource)->inputSource());
+ } else {
+ // "inputSource" is the original WAV file source:
+ wavSource = (WAVAudioFileSource*)inputSource;
+ }
+
unsigned numDurationSamples = (unsigned)(streamDuration*fSamplingFrequency);
unsigned numDurationBytes = numDurationSamples*((fNumChannels*fBitsPerSample)/8);
numBytes = (u_int64_t)numDurationBytes;
- wavSource->seekToPCMByte(seekByteNumber, numDurationBytes);
+ wavSource->limitNumBytesToStream(numDurationBytes);
}
void WAVAudioFileServerMediaSubsession
diff --git a/liveMedia/WAVAudioFileSource.cpp b/liveMedia/WAVAudioFileSource.cpp
index 671d7b4..b60a488 100644
--- a/liveMedia/WAVAudioFileSource.cpp
+++ b/liveMedia/WAVAudioFileSource.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A WAV audio file source
// Implementation
@@ -65,12 +65,14 @@ void WAVAudioFileSource::setScaleFactor(int scale) {
}
}
-void WAVAudioFileSource::seekToPCMByte(unsigned byteNumber, unsigned numBytesToStream) {
+void WAVAudioFileSource::seekToPCMByte(unsigned byteNumber) {
byteNumber += fWAVHeaderSize;
if (byteNumber > fFileSize) byteNumber = fFileSize;
SeekFile64(fFid, byteNumber, SEEK_SET);
+}
+void WAVAudioFileSource::limitNumBytesToStream(unsigned numBytesToStream) {
fNumBytesToStream = numBytesToStream;
fLimitNumBytesToStream = fNumBytesToStream > 0;
}
@@ -222,7 +224,7 @@ WAVAudioFileSource::~WAVAudioFileSource() {
void WAVAudioFileSource::doGetNextFrame() {
if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {
- handleClosure(this);
+ handleClosure();
return;
}
@@ -240,6 +242,7 @@ void WAVAudioFileSource::doGetNextFrame() {
}
void WAVAudioFileSource::doStopGettingFrames() {
+ envir().taskScheduler().unscheduleDelayedTask(nextTask());
#ifndef READ_FROM_FILES_SYNCHRONOUSLY
envir().taskScheduler().turnOffBackgroundReadHandling(fileno(fFid));
fHaveStartedReading = False;
@@ -280,7 +283,7 @@ void WAVAudioFileSource::doReadFromFile() {
}
#endif
if (numBytesRead == 0) {
- handleClosure(this);
+ handleClosure();
return;
}
fFrameSize += numBytesRead;
diff --git a/liveMedia/include/AC3AudioFileServerMediaSubsession.hh b/liveMedia/include/AC3AudioFileServerMediaSubsession.hh
index ecb0b0e..deab94c 100644
--- a/liveMedia/include/AC3AudioFileServerMediaSubsession.hh
+++ b/liveMedia/include/AC3AudioFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an AC3 audio file.
// C++ header
diff --git a/liveMedia/include/AC3AudioRTPSink.hh b/liveMedia/include/AC3AudioRTPSink.hh
index ba2ba5a..8e5cf92 100644
--- a/liveMedia/include/AC3AudioRTPSink.hh
+++ b/liveMedia/include/AC3AudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for AC3 audio
// C++ header
diff --git a/liveMedia/include/AC3AudioRTPSource.hh b/liveMedia/include/AC3AudioRTPSource.hh
index 4525094..c89bf5e 100644
--- a/liveMedia/include/AC3AudioRTPSource.hh
+++ b/liveMedia/include/AC3AudioRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// AC3 Audio RTP Sources
// C++ header
diff --git a/liveMedia/include/AC3AudioStreamFramer.hh b/liveMedia/include/AC3AudioStreamFramer.hh
index 9b87797..d8954d1 100644
--- a/liveMedia/include/AC3AudioStreamFramer.hh
+++ b/liveMedia/include/AC3AudioStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an AC3 audio elementary stream into frames
// C++ header
diff --git a/liveMedia/include/ADTSAudioFileServerMediaSubsession.hh b/liveMedia/include/ADTSAudioFileServerMediaSubsession.hh
index 68f136d..efaf5af 100644
--- a/liveMedia/include/ADTSAudioFileServerMediaSubsession.hh
+++ b/liveMedia/include/ADTSAudioFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an AAC audio file in ADTS format
// C++ header
diff --git a/liveMedia/include/ADTSAudioFileSource.hh b/liveMedia/include/ADTSAudioFileSource.hh
index c49876a..59c86d8 100644
--- a/liveMedia/include/ADTSAudioFileSource.hh
+++ b/liveMedia/include/ADTSAudioFileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source object for AAC audio files in ADTS format
// C++ header
diff --git a/liveMedia/include/AMRAudioFileServerMediaSubsession.hh b/liveMedia/include/AMRAudioFileServerMediaSubsession.hh
index 9a6fc5d..14ab235 100644
--- a/liveMedia/include/AMRAudioFileServerMediaSubsession.hh
+++ b/liveMedia/include/AMRAudioFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an AMR audio file.
// C++ header
diff --git a/liveMedia/include/AMRAudioFileSink.hh b/liveMedia/include/AMRAudioFileSink.hh
index 6d24417..4efffc9 100644
--- a/liveMedia/include/AMRAudioFileSink.hh
+++ b/liveMedia/include/AMRAudioFileSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// AMR Audio File Sinks
// C++ header
diff --git a/liveMedia/include/AMRAudioFileSource.hh b/liveMedia/include/AMRAudioFileSource.hh
index 468ace2..9dd1bf7 100644
--- a/liveMedia/include/AMRAudioFileSource.hh
+++ b/liveMedia/include/AMRAudioFileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source object for AMR audio files (as defined in RFC 4867, section 5)
// C++ header
diff --git a/liveMedia/include/AMRAudioRTPSink.hh b/liveMedia/include/AMRAudioRTPSink.hh
index 82ea9b3..c868d5b 100644
--- a/liveMedia/include/AMRAudioRTPSink.hh
+++ b/liveMedia/include/AMRAudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for AMR audio (RFC 4867)
// C++ header
diff --git a/liveMedia/include/AMRAudioRTPSource.hh b/liveMedia/include/AMRAudioRTPSource.hh
index dd3868e..327445e 100644
--- a/liveMedia/include/AMRAudioRTPSource.hh
+++ b/liveMedia/include/AMRAudioRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// AMR Audio RTP Sources (RFC 4867)
// C++ header
diff --git a/liveMedia/include/AMRAudioSource.hh b/liveMedia/include/AMRAudioSource.hh
index f7d7f87..3956af9 100644
--- a/liveMedia/include/AMRAudioSource.hh
+++ b/liveMedia/include/AMRAudioSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source object for AMR audio sources
// C++ header
diff --git a/liveMedia/include/AVIFileSink.hh b/liveMedia/include/AVIFileSink.hh
index 5c319c9..f0e2558 100644
--- a/liveMedia/include/AVIFileSink.hh
+++ b/liveMedia/include/AVIFileSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A sink that generates an AVI file from a composite media session
// C++ header
diff --git a/liveMedia/include/AudioInputDevice.hh b/liveMedia/include/AudioInputDevice.hh
index e0a1a3f..10f9df9 100644
--- a/liveMedia/include/AudioInputDevice.hh
+++ b/liveMedia/include/AudioInputDevice.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Generic audio input device (such as a microphone, or an input sound card)
// C++ header
diff --git a/liveMedia/include/AudioRTPSink.hh b/liveMedia/include/AudioRTPSink.hh
index f00f865..e0d4617 100644
--- a/liveMedia/include/AudioRTPSink.hh
+++ b/liveMedia/include/AudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTP sink for audio codecs (abstract base class)
// C++ header
diff --git a/liveMedia/include/Base64.hh b/liveMedia/include/Base64.hh
index feda65d..79b0a3a 100644
--- a/liveMedia/include/Base64.hh
+++ b/liveMedia/include/Base64.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Base64 encoding and decoding
// C++ header
diff --git a/liveMedia/include/BasicUDPSink.hh b/liveMedia/include/BasicUDPSink.hh
index 14f6e60..7adee01 100644
--- a/liveMedia/include/BasicUDPSink.hh
+++ b/liveMedia/include/BasicUDPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simple UDP sink (i.e., without RTP or other headers added); one frame per packet
// C++ header
diff --git a/liveMedia/include/BasicUDPSource.hh b/liveMedia/include/BasicUDPSource.hh
index f823122..8d47320 100644
--- a/liveMedia/include/BasicUDPSource.hh
+++ b/liveMedia/include/BasicUDPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simple UDP source, where every UDP payload is a complete frame
// C++ header
diff --git a/liveMedia/include/BitVector.hh b/liveMedia/include/BitVector.hh
index 477c3e4..68a4eb6 100644
--- a/liveMedia/include/BitVector.hh
+++ b/liveMedia/include/BitVector.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Bit Vector data structure
// C++ header
diff --git a/liveMedia/include/ByteStreamFileSource.hh b/liveMedia/include/ByteStreamFileSource.hh
index a594f73..c6edce2 100644
--- a/liveMedia/include/ByteStreamFileSource.hh
+++ b/liveMedia/include/ByteStreamFileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A file source that is a plain byte stream (rather than frames)
// C++ header
@@ -46,7 +46,7 @@ public:
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);
+ void seekToByteRelative(int64_t offset, u_int64_t numBytesToStream = 0);
void seekToEnd(); // to force EOF handling on the next read
protected:
diff --git a/liveMedia/include/ByteStreamMemoryBufferSource.hh b/liveMedia/include/ByteStreamMemoryBufferSource.hh
index 6906df0..8dd2fcc 100644
--- a/liveMedia/include/ByteStreamMemoryBufferSource.hh
+++ b/liveMedia/include/ByteStreamMemoryBufferSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class for streaming data from a (static) memory buffer, as if it were a file.
// C++ header
@@ -39,7 +39,7 @@ public:
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);
+ void seekToByteRelative(int64_t offset, u_int64_t numBytesToStream = 0);
protected:
ByteStreamMemoryBufferSource(UsageEnvironment& env,
diff --git a/liveMedia/include/ByteStreamMultiFileSource.hh b/liveMedia/include/ByteStreamMultiFileSource.hh
index 00bf85d..e41f380 100644
--- a/liveMedia/include/ByteStreamMultiFileSource.hh
+++ b/liveMedia/include/ByteStreamMultiFileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A source that consists of multiple byte-stream files, read sequentially
// C++ header
diff --git a/liveMedia/include/DVVideoFileServerMediaSubsession.hh b/liveMedia/include/DVVideoFileServerMediaSubsession.hh
index b35515c..34a44b5 100644
--- a/liveMedia/include/DVVideoFileServerMediaSubsession.hh
+++ b/liveMedia/include/DVVideoFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a DV video file.
// C++ header
@@ -39,6 +39,7 @@ private:
private: // redefined virtual functions
virtual char const* getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource);
virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
+ virtual void setStreamSourceDuration(FramedSource* inputSource, double streamDuration, u_int64_t& numBytes);
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);
virtual float duration() const;
diff --git a/liveMedia/include/DVVideoRTPSink.hh b/liveMedia/include/DVVideoRTPSink.hh
index f07b204..5b767d5 100644
--- a/liveMedia/include/DVVideoRTPSink.hh
+++ b/liveMedia/include/DVVideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for DV video (RFC 3189)
// (Thanks to Ben Hutchings for prototyping this.)
// C++ header
diff --git a/liveMedia/include/DVVideoRTPSource.hh b/liveMedia/include/DVVideoRTPSource.hh
index 66f4f3b..0983618 100644
--- a/liveMedia/include/DVVideoRTPSource.hh
+++ b/liveMedia/include/DVVideoRTPSource.hh
@@ -14,11 +14,11 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// DV Video RTP Sources
// C++ header
-#ifndef _DV_VIDEOO_RTP_SOURCE_HH
+#ifndef _DV_VIDEO_RTP_SOURCE_HH
#define _DV_VIDEO_RTP_SOURCE_HH
#ifndef _MULTI_FRAMED_RTP_SOURCE_HH
diff --git a/liveMedia/include/DVVideoStreamFramer.hh b/liveMedia/include/DVVideoStreamFramer.hh
index 3a0ccdb..73ccc00 100644
--- a/liveMedia/include/DVVideoStreamFramer.hh
+++ b/liveMedia/include/DVVideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that parses a DV input stream into DV frames to deliver to the downstream object
// C++ header
diff --git a/liveMedia/include/DarwinInjector.hh b/liveMedia/include/DarwinInjector.hh
deleted file mode 100644
index 56027fc..0000000
--- a/liveMedia/include/DarwinInjector.hh
+++ /dev/null
@@ -1,106 +0,0 @@
-/**********
-This library is free software; you can redistribute it and/or modify it under
-the terms of the GNU Lesser General Public License as published by the
-Free Software Foundation; either version 2.1 of the License, or (at your
-option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
-
-This library is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
-more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this library; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-**********/
-// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// An object that redirects one or more RTP/RTCP streams - forming a single
-// multimedia session - into a 'Darwin Streaming Server' (for subsequent
-// reflection to potentially arbitrarily many remote RTSP clients).
-// C++ header
-
-#ifndef _DARWIN_INJECTOR_HH
-#define _DARWIN_INJECTOR_HH
-
-#ifndef _RTSP_CLIENT_HH
-#include <RTSPClient.hh>
-#endif
-
-#ifndef _RTCP_HH
-#include <RTCP.hh>
-#endif
-
-/*
-To use a "DarwinInjector":
- 1/ Create RTP sinks and RTCP instances for each audio or video subsession.
- Note: These can use 0.0.0.0 for the address, and 0 for the port number,
- of each 'groupsock')
- 2/ Call "addStream()" for each.
- 3/ Call "setDestination()" to specify the remote Darwin Streaming Server.
- Note: You must have 'write' permission on the Darwin Streaming Server.
- This can be set up using a "qtaccess" file in the server's 'movies'
- directory. For example, the following "qtaccess" file allows anyone to
- play streams from the server, but allows only valid users to
- inject streams *into* the server:
- <Limit WRITE>
- require valid-user
- </Limit>
- require any-user
- Use the "remoteUserName" and "remotePassword" parameters to
- "setDestination()", as appropriate.
- 4/ Call "startPlaying" on each RTP sink (from the corresponding 'source').
-*/
-
-class SubstreamDescriptor; // forward
-
-class DarwinInjector: public Medium {
-public:
- static DarwinInjector* createNew(UsageEnvironment& env,
- char const* applicationName = "DarwinInjector",
- int verbosityLevel = 0);
-
- static Boolean lookupByName(UsageEnvironment& env, char const* name,
- DarwinInjector*& result);
-
- void addStream(RTPSink* rtpSink, RTCPInstance* rtcpInstance);
-
- Boolean setDestination(char const* remoteRTSPServerNameOrAddress,
- char const* remoteFileName,
- char const* sessionName = "",
- char const* sessionInfo = "",
- portNumBits remoteRTSPServerPortNumber = 554,
- char const* remoteUserName = "",
- char const* remotePassword = "",
- char const* sessionAuthor = "",
- char const* sessionCopyright = "",
- int timeout = -1);
-
-private: // redefined virtual functions
- virtual Boolean isDarwinInjector() const;
-
-private:
- DarwinInjector(UsageEnvironment& env,
- char const* applicationName, int verbosityLevel);
- // called only by createNew()
-
- virtual ~DarwinInjector();
-
- static void genericResponseHandler(RTSPClient* rtspClient, int responseCode, char* responseString);
- void genericResponseHandler1(int responseCode, char* responseString);
-
-private:
- char const* fApplicationName;
- int fVerbosityLevel;
- RTSPClient* fRTSPClient;
- unsigned fSubstreamSDPSizes;
- SubstreamDescriptor* fHeadSubstream;
- SubstreamDescriptor* fTailSubstream;
- MediaSession* fSession;
- unsigned fLastTrackId;
- char fWatchVariable;
- int fResultCode;
- char* fResultString;
-};
-
-#endif
diff --git a/liveMedia/include/DeviceSource.hh b/liveMedia/include/DeviceSource.hh
index adc937f..3d00c40 100644
--- a/liveMedia/include/DeviceSource.hh
+++ b/liveMedia/include/DeviceSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A template for a MediaSource encapsulating an audio/video input device
//
// NOTE: Sections of this code labeled "%%% TO BE WRITTEN %%%" are incomplete, and needto be written by the programmer
diff --git a/liveMedia/include/DigestAuthentication.hh b/liveMedia/include/DigestAuthentication.hh
index a3656c0..364d944 100644
--- a/liveMedia/include/DigestAuthentication.hh
+++ b/liveMedia/include/DigestAuthentication.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class used for digest authentication.
// C++ header
@@ -37,6 +37,7 @@ public:
// by md5(<username>:<realm>:<actual-password>)
Authenticator(const Authenticator& orig);
Authenticator& operator=(const Authenticator& rightSide);
+ Boolean operator<(const Authenticator* rightSide);
virtual ~Authenticator();
void reset();
diff --git a/liveMedia/include/FileServerMediaSubsession.hh b/liveMedia/include/FileServerMediaSubsession.hh
index 42d1756..4485772 100644
--- a/liveMedia/include/FileServerMediaSubsession.hh
+++ b/liveMedia/include/FileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a file.
// C++ header
@@ -22,6 +22,9 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#ifndef _FILE_SERVER_MEDIA_SUBSESSION_HH
#define _FILE_SERVER_MEDIA_SUBSESSION_HH
+#ifndef _SERVER_MEDIA_SESSION_HH
+#include "ServerMediaSession.hh"
+#endif
#ifndef _ON_DEMAND_SERVER_MEDIA_SUBSESSION_HH
#include "OnDemandServerMediaSubsession.hh"
#endif
diff --git a/liveMedia/include/FileSink.hh b/liveMedia/include/FileSink.hh
index 2f3bc6d..c8bbc07 100644
--- a/liveMedia/include/FileSink.hh
+++ b/liveMedia/include/FileSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// File Sinks
// C++ header
@@ -37,8 +37,8 @@ public:
// file name suffix). The default behavior ("oneFilePerFrame" == False)
// is to output all incoming data into a single file.
- void addData(unsigned char const* data, unsigned dataSize,
- struct timeval presentationTime);
+ virtual void addData(unsigned char const* data, unsigned dataSize,
+ struct timeval presentationTime);
// (Available in case a client wants to add extra data to the output file)
protected:
diff --git a/liveMedia/include/FramedFileSource.hh b/liveMedia/include/FramedFileSource.hh
index 8ad76d8..dc97d15 100644
--- a/liveMedia/include/FramedFileSource.hh
+++ b/liveMedia/include/FramedFileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Framed File Sources
// C++ header
diff --git a/liveMedia/include/FramedFilter.hh b/liveMedia/include/FramedFilter.hh
index fe74fa7..b18cd9f 100644
--- a/liveMedia/include/FramedFilter.hh
+++ b/liveMedia/include/FramedFilter.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Framed Filters
// C++ header
diff --git a/liveMedia/include/FramedSource.hh b/liveMedia/include/FramedSource.hh
index 1ca3fea..e7a69a3 100644
--- a/liveMedia/include/FramedSource.hh
+++ b/liveMedia/include/FramedSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Framed Sources
// C++ header
@@ -45,6 +45,7 @@ public:
void* onCloseClientData);
static void handleClosure(void* clientData);
+ void handleClosure();
// This should be called (on ourself) if the source is discovered
// to be closed (i.e., no longer readable)
diff --git a/liveMedia/include/GSMAudioRTPSink.hh b/liveMedia/include/GSMAudioRTPSink.hh
index 9267a8c..3e58786 100644
--- a/liveMedia/include/GSMAudioRTPSink.hh
+++ b/liveMedia/include/GSMAudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for GSM audio
// C++ header
diff --git a/liveMedia/include/GenericMediaServer.hh b/liveMedia/include/GenericMediaServer.hh
new file mode 100644
index 0000000..a5329ad
--- /dev/null
+++ b/liveMedia/include/GenericMediaServer.hh
@@ -0,0 +1,189 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A generic media server class, used to implement a RTSP server, and any other server that uses
+// "ServerMediaSession" objects to describe media to be served.
+// C++ header
+
+#ifndef _GENERIC_MEDIA_SERVER_HH
+#define _GENERIC_MEDIA_SERVER_HH
+
+#ifndef _SERVER_MEDIA_SESSION_HH
+#include "ServerMediaSession.hh"
+#endif
+
+#ifndef REQUEST_BUFFER_SIZE
+#define REQUEST_BUFFER_SIZE 20000 // for incoming requests
+#endif
+#ifndef RESPONSE_BUFFER_SIZE
+#define RESPONSE_BUFFER_SIZE 20000
+#endif
+
+class GenericMediaServer: public Medium {
+public:
+ void addServerMediaSession(ServerMediaSession* serverMediaSession);
+
+ virtual ServerMediaSession*
+ lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession = True);
+
+ void removeServerMediaSession(ServerMediaSession* serverMediaSession);
+ // Removes the "ServerMediaSession" object from our lookup table, so it will no longer be accessible by new clients.
+ // (However, any *existing* client sessions that use this "ServerMediaSession" object will continue streaming.
+ // The "ServerMediaSession" object will not get deleted until all of these client sessions have closed.)
+ // (To both delete the "ServerMediaSession" object *and* close all client sessions that use it,
+ // call "deleteServerMediaSession(serverMediaSession)" instead.)
+ void removeServerMediaSession(char const* streamName);
+ // ditto
+
+ void closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession);
+ // Closes (from the server) all client sessions that are currently using this "ServerMediaSession" object.
+ // Note, however, that the "ServerMediaSession" object remains accessible by new clients.
+ void closeAllClientSessionsForServerMediaSession(char const* streamName);
+ // ditto
+
+ void deleteServerMediaSession(ServerMediaSession* serverMediaSession);
+ // Equivalent to:
+ // "closeAllClientSessionsForServerMediaSession(serverMediaSession); removeServerMediaSession(serverMediaSession);"
+ void deleteServerMediaSession(char const* streamName);
+ // Equivalent to:
+ // "closeAllClientSessionsForServerMediaSession(streamName); removeServerMediaSession(streamName);
+
+protected:
+ GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort,
+ unsigned reclamationSeconds);
+ // If "reclamationSeconds" > 0, then the "ClientSession" state for each client will get
+ // reclaimed if no activity from the client is detected in at least "reclamationSeconds".
+ // we're an abstract base class
+ virtual ~GenericMediaServer();
+ void cleanup(); // MUST be called in the destructor of any subclass of us
+
+ static int setUpOurSocket(UsageEnvironment& env, Port& ourPort);
+
+ static void incomingConnectionHandler(void*, int /*mask*/);
+ void incomingConnectionHandler();
+ void incomingConnectionHandlerOnSocket(int serverSocket);
+
+public: // should be protected, but some old compilers complain otherwise
+ // The state of a TCP connection used by a client:
+ class ClientConnection {
+ protected:
+ ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr);
+ virtual ~ClientConnection();
+
+ UsageEnvironment& envir() { return fOurServer.envir(); }
+ void closeSockets();
+
+ static void incomingRequestHandler(void*, int /*mask*/);
+ void incomingRequestHandler();
+ virtual void handleRequestBytes(int newBytesRead) = 0;
+ void resetRequestBuffer();
+
+ protected:
+ friend class GenericMediaServer;
+ friend class ClientSession;
+ friend class RTSPServer; // needed to make some broken Windows compilers work; remove this in the future when we end support for Windows
+ GenericMediaServer& fOurServer;
+ int fOurSocket;
+ struct sockaddr_in fClientAddr;
+ unsigned char fRequestBuffer[REQUEST_BUFFER_SIZE];
+ unsigned char fResponseBuffer[RESPONSE_BUFFER_SIZE];
+ unsigned fRequestBytesAlreadySeen, fRequestBufferBytesLeft;
+ };
+
+ // The state of an individual client session (using one or more sequential TCP connections) handled by a server:
+ class ClientSession {
+ protected:
+ ClientSession(GenericMediaServer& ourServer, u_int32_t sessionId);
+ virtual ~ClientSession();
+
+ UsageEnvironment& envir() { return fOurServer.envir(); }
+ void noteLiveness();
+ static void noteClientLiveness(ClientSession* clientSession);
+ static void livenessTimeoutTask(ClientSession* clientSession);
+
+ protected:
+ friend class GenericMediaServer;
+ friend class ClientConnection;
+ GenericMediaServer& fOurServer;
+ u_int32_t fOurSessionId;
+ ServerMediaSession* fOurServerMediaSession;
+ TaskToken fLivenessCheckTask;
+ };
+
+protected:
+ virtual ClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) = 0;
+ virtual ClientSession* createNewClientSession(u_int32_t sessionId) = 0;
+
+ ClientSession* createNewClientSessionWithId();
+ // Generates a new (unused) random session id, and calls the "createNewClientSession()"
+ // virtual function with this session id as parameter.
+
+ // Lookup a "ClientSession" object by sessionId (integer, and string):
+ ClientSession* lookupClientSession(u_int32_t sessionId);
+ ClientSession* lookupClientSession(char const* sessionIdStr);
+
+ // An iterator over our "ServerMediaSession" objects:
+ class ServerMediaSessionIterator {
+ public:
+ ServerMediaSessionIterator(GenericMediaServer& server);
+ virtual ~ServerMediaSessionIterator();
+ ServerMediaSession* next();
+ private:
+ HashTable::Iterator* fOurIterator;
+ };
+
+protected:
+ friend class ClientConnection;
+ friend class ClientSession;
+ friend class ServerMediaSessionIterator;
+ int fServerSocket;
+ Port fServerPort;
+ unsigned fReclamationSeconds;
+
+private:
+ HashTable* fServerMediaSessions; // maps 'stream name' strings to "ServerMediaSession" objects
+ HashTable* fClientConnections; // the "ClientConnection" objects that we're using
+ HashTable* fClientSessions; // maps 'session id' strings to "ClientSession" objects
+};
+
+// A data structure used for optional user/password authentication:
+
+class UserAuthenticationDatabase {
+public:
+ UserAuthenticationDatabase(char const* realm = NULL,
+ Boolean passwordsAreMD5 = False);
+ // If "passwordsAreMD5" is True, then each password stored into, or removed from,
+ // the database is actually the value computed
+ // by md5(<username>:<realm>:<actual-password>)
+ virtual ~UserAuthenticationDatabase();
+
+ virtual void addUserRecord(char const* username, char const* password);
+ virtual void removeUserRecord(char const* username);
+
+ virtual char const* lookupPassword(char const* username);
+ // returns NULL if the user name was not present
+
+ char const* realm() { return fRealm; }
+ Boolean passwordsAreMD5() { return fPasswordsAreMD5; }
+
+protected:
+ HashTable* fTable;
+ char* fRealm;
+ Boolean fPasswordsAreMD5;
+};
+
+#endif
diff --git a/liveMedia/include/H261VideoRTPSource.hh b/liveMedia/include/H261VideoRTPSource.hh
index f66151c..881f029 100644
--- a/liveMedia/include/H261VideoRTPSource.hh
+++ b/liveMedia/include/H261VideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.261 Video RTP Sources
// C++ header
diff --git a/liveMedia/include/H263plusVideoFileServerMediaSubsession.hh b/liveMedia/include/H263plusVideoFileServerMediaSubsession.hh
index d1df495..d0b52bc 100644
--- a/liveMedia/include/H263plusVideoFileServerMediaSubsession.hh
+++ b/liveMedia/include/H263plusVideoFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a H.263 video file.
// C++ header
diff --git a/liveMedia/include/H263plusVideoRTPSink.hh b/liveMedia/include/H263plusVideoRTPSink.hh
index 3b49b65..7b4d854 100644
--- a/liveMedia/include/H263plusVideoRTPSink.hh
+++ b/liveMedia/include/H263plusVideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.263+ video (RFC 4629)
// C++ header
diff --git a/liveMedia/include/H263plusVideoRTPSource.hh b/liveMedia/include/H263plusVideoRTPSource.hh
index d49ad00..135e0b4 100644
--- a/liveMedia/include/H263plusVideoRTPSource.hh
+++ b/liveMedia/include/H263plusVideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.263+ Video RTP Sources
// C++ header
diff --git a/liveMedia/include/H263plusVideoStreamFramer.hh b/liveMedia/include/H263plusVideoStreamFramer.hh
index 05fb3d3..c64f914 100644
--- a/liveMedia/include/H263plusVideoStreamFramer.hh
+++ b/liveMedia/include/H263plusVideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an H263 video elementary stream into frames.
// Author Benhard Feiten
diff --git a/liveMedia/include/H264VideoFileServerMediaSubsession.hh b/liveMedia/include/H264VideoFileServerMediaSubsession.hh
index 465ae96..2d8f409 100644
--- a/liveMedia/include/H264VideoFileServerMediaSubsession.hh
+++ b/liveMedia/include/H264VideoFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a H264 Elementary Stream video file.
// C++ header
diff --git a/liveMedia/include/H264VideoFileSink.hh b/liveMedia/include/H264VideoFileSink.hh
index 73cd921..00c4c31 100644
--- a/liveMedia/include/H264VideoFileSink.hh
+++ b/liveMedia/include/H264VideoFileSink.hh
@@ -14,25 +14,27 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.264 Video File Sinks
// C++ header
#ifndef _H264_VIDEO_FILE_SINK_HH
#define _H264_VIDEO_FILE_SINK_HH
-#ifndef _FILE_SINK_HH
-#include "FileSink.hh"
+#ifndef _H264_OR_5_VIDEO_FILE_SINK_HH
+#include "H264or5VideoFileSink.hh"
#endif
-class H264VideoFileSink: public FileSink {
+class H264VideoFileSink: public H264or5VideoFileSink {
public:
static H264VideoFileSink* createNew(UsageEnvironment& env, char const* fileName,
char const* sPropParameterSetsStr = NULL,
- // An optional 'SDP format' string (comma-separated Base64-encoded) representing SPS and/or PPS NAL-units to prepend to the output
+ // "sPropParameterSetsStr" is an optional 'SDP format' string
+ // (comma-separated Base64-encoded) representing SPS and/or PPS NAL-units
+ // to prepend to the output
unsigned bufferSize = 100000,
Boolean oneFilePerFrame = False);
- // See "FileSink.hh" for a description of these parameters.
+ // See "FileSink.hh" for a description of these parameters.
protected:
H264VideoFileSink(UsageEnvironment& env, FILE* fid,
@@ -40,13 +42,6 @@ protected:
unsigned bufferSize, char const* perFrameFileNamePrefix);
// called only by createNew()
virtual ~H264VideoFileSink();
-
-protected: // redefined virtual functions:
- virtual void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime);
-
-private:
- char const* fSPropParameterSetsStr;
- Boolean fHaveWrittenFirstFrame;
};
#endif
diff --git a/liveMedia/include/H264VideoRTPSink.hh b/liveMedia/include/H264VideoRTPSink.hh
index 13021d8..9b4681c 100644
--- a/liveMedia/include/H264VideoRTPSink.hh
+++ b/liveMedia/include/H264VideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.264 video (RFC 3984)
// C++ header
@@ -34,11 +34,13 @@ public:
u_int8_t const* sps, unsigned spsSize, u_int8_t const* pps, unsigned ppsSize);
// an optional variant of "createNew()", useful if we know, in advance,
// the stream's SPS and PPS NAL units.
+ // This avoids us having to 'pre-read' from the input source in order to get these values.
static H264VideoRTPSink*
createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
char const* sPropParameterSetsStr);
// an optional variant of "createNew()", useful if we know, in advance,
// the stream's SPS and PPS NAL units.
+ // This avoids us having to 'pre-read' from the input source in order to get these values.
protected:
H264VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
diff --git a/liveMedia/include/H264VideoRTPSource.hh b/liveMedia/include/H264VideoRTPSource.hh
index b7a6212..530d4d2 100644
--- a/liveMedia/include/H264VideoRTPSource.hh
+++ b/liveMedia/include/H264VideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// H.264 Video RTP Sources
// C++ header
diff --git a/liveMedia/include/H264VideoStreamDiscreteFramer.hh b/liveMedia/include/H264VideoStreamDiscreteFramer.hh
index ffbac92..716b464 100644
--- a/liveMedia/include/H264VideoStreamDiscreteFramer.hh
+++ b/liveMedia/include/H264VideoStreamDiscreteFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "H264VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/include/H264VideoStreamFramer.hh b/liveMedia/include/H264VideoStreamFramer.hh
index deca92a..f629b91 100644
--- a/liveMedia/include/H264VideoStreamFramer.hh
+++ b/liveMedia/include/H264VideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up a H.264 Video Elementary Stream into NAL units.
// C++ header
diff --git a/liveMedia/include/H264VideoFileSink.hh b/liveMedia/include/H264or5VideoFileSink.hh
similarity index 55%
copy from liveMedia/include/H264VideoFileSink.hh
copy to liveMedia/include/H264or5VideoFileSink.hh
index 73cd921..fd83a08 100644
--- a/liveMedia/include/H264VideoFileSink.hh
+++ b/liveMedia/include/H264or5VideoFileSink.hh
@@ -14,38 +14,32 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// H.264 Video File Sinks
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// H.264 or H.265 Video File Sinks
// C++ header
-#ifndef _H264_VIDEO_FILE_SINK_HH
-#define _H264_VIDEO_FILE_SINK_HH
+#ifndef _H264_OR_5_VIDEO_FILE_SINK_HH
+#define _H264_OR_5_VIDEO_FILE_SINK_HH
#ifndef _FILE_SINK_HH
#include "FileSink.hh"
#endif
-class H264VideoFileSink: public FileSink {
-public:
- static H264VideoFileSink* createNew(UsageEnvironment& env, char const* fileName,
- char const* sPropParameterSetsStr = NULL,
- // An optional 'SDP format' string (comma-separated Base64-encoded) representing SPS and/or PPS NAL-units to prepend to the output
- unsigned bufferSize = 100000,
- Boolean oneFilePerFrame = False);
- // See "FileSink.hh" for a description of these parameters.
-
+class H264or5VideoFileSink: public FileSink {
protected:
- H264VideoFileSink(UsageEnvironment& env, FILE* fid,
- char const* sPropParameterSetsStr,
- unsigned bufferSize, char const* perFrameFileNamePrefix);
- // called only by createNew()
- virtual ~H264VideoFileSink();
+ H264or5VideoFileSink(UsageEnvironment& env, FILE* fid,
+ unsigned bufferSize, char const* perFrameFileNamePrefix,
+ char const* sPropParameterSetsStr1,
+ char const* sPropParameterSetsStr2 = NULL,
+ char const* sPropParameterSetsStr3 = NULL);
+ // we're an abstract base class
+ virtual ~H264or5VideoFileSink();
protected: // redefined virtual functions:
virtual void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime);
private:
- char const* fSPropParameterSetsStr;
+ char const* fSPropParameterSetsStr[3];
Boolean fHaveWrittenFirstFrame;
};
diff --git a/liveMedia/include/H264or5VideoRTPSink.hh b/liveMedia/include/H264or5VideoRTPSink.hh
index 00ca921..c7752cc 100644
--- a/liveMedia/include/H264or5VideoRTPSink.hh
+++ b/liveMedia/include/H264or5VideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.264 or H.265 video
// C++ header
diff --git a/liveMedia/include/H264or5VideoStreamDiscreteFramer.hh b/liveMedia/include/H264or5VideoStreamDiscreteFramer.hh
index dc83567..dd88ed7 100644
--- a/liveMedia/include/H264or5VideoStreamDiscreteFramer.hh
+++ b/liveMedia/include/H264or5VideoStreamDiscreteFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "H264or5VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
@@ -47,6 +47,8 @@ protected:
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned durationInMicroseconds);
+
+ virtual Boolean nalUnitEndsAccessUnit(u_int8_t nal_unit_type);
};
#endif
diff --git a/liveMedia/include/H264or5VideoStreamFramer.hh b/liveMedia/include/H264or5VideoStreamFramer.hh
index 0c631ed..9ffc541 100644
--- a/liveMedia/include/H264or5VideoStreamFramer.hh
+++ b/liveMedia/include/H264or5VideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up a H.264 or H.265 Video Elementary Stream into NAL units.
// C++ header
@@ -48,11 +48,6 @@ public:
saveCopyOfPPS(pps, ppsSize);
}
- u_int32_t profileLevelId() const { return fProfileLevelId; }
- // used for H.264 only
- u_int8_t const* profileTierLevelHeaderBytes() const { return fProfileTierLevelHeaderBytes; }
- // used for H.265 only
-
protected:
H264or5VideoStreamFramer(int hNumber, // 264 or 265
UsageEnvironment& env, FramedSource* inputSource,
@@ -79,8 +74,6 @@ protected:
unsigned fLastSeenSPSSize;
u_int8_t* fLastSeenPPS;
unsigned fLastSeenPPSSize;
- u_int32_t fProfileLevelId; // set/used for H.264 only
- u_int8_t fProfileTierLevelHeaderBytes[12]; // set/used for H.265 only
struct timeval fNextPresentationTime; // the presentation time to be used for the next NAL unit to be parsed/delivered after this
friend class H264or5VideoStreamParser; // hack
};
diff --git a/liveMedia/include/H265VideoFileServerMediaSubsession.hh b/liveMedia/include/H265VideoFileServerMediaSubsession.hh
index f34a1eb..e3314c4 100644
--- a/liveMedia/include/H265VideoFileServerMediaSubsession.hh
+++ b/liveMedia/include/H265VideoFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a H265 Elementary Stream video file.
// C++ header
diff --git a/liveMedia/include/H264VideoFileSink.hh b/liveMedia/include/H265VideoFileSink.hh
similarity index 53%
copy from liveMedia/include/H264VideoFileSink.hh
copy to liveMedia/include/H265VideoFileSink.hh
index 73cd921..f52dede 100644
--- a/liveMedia/include/H264VideoFileSink.hh
+++ b/liveMedia/include/H265VideoFileSink.hh
@@ -14,39 +14,38 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// H.264 Video File Sinks
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// H.265 Video File Sinks
// C++ header
-#ifndef _H264_VIDEO_FILE_SINK_HH
-#define _H264_VIDEO_FILE_SINK_HH
+#ifndef _H265_VIDEO_FILE_SINK_HH
+#define _H265_VIDEO_FILE_SINK_HH
-#ifndef _FILE_SINK_HH
-#include "FileSink.hh"
+#ifndef _H264_OR_5_VIDEO_FILE_SINK_HH
+#include "H264or5VideoFileSink.hh"
#endif
-class H264VideoFileSink: public FileSink {
+class H265VideoFileSink: public H264or5VideoFileSink {
public:
- static H264VideoFileSink* createNew(UsageEnvironment& env, char const* fileName,
- char const* sPropParameterSetsStr = NULL,
- // An optional 'SDP format' string (comma-separated Base64-encoded) representing SPS and/or PPS NAL-units to prepend to the output
+ static H265VideoFileSink* createNew(UsageEnvironment& env, char const* fileName,
+ char const* sPropVPSStr = NULL,
+ char const* sPropSPSStr = NULL,
+ char const* sPropPPSStr = NULL,
+ // The "sProp*Str" parameters are optional 'SDP format' strings
+ // (comma-separated Base64-encoded) representing VPS, SPS, and/or PPS NAL-units
+ // to prepend to the output
unsigned bufferSize = 100000,
Boolean oneFilePerFrame = False);
- // See "FileSink.hh" for a description of these parameters.
+ // See "FileSink.hh" for a description of these parameters.
protected:
- H264VideoFileSink(UsageEnvironment& env, FILE* fid,
- char const* sPropParameterSetsStr,
+ H265VideoFileSink(UsageEnvironment& env, FILE* fid,
+ char const* sPropVPSStr,
+ char const* sPropSPSStr,
+ char const* sPropPPSStr,
unsigned bufferSize, char const* perFrameFileNamePrefix);
// called only by createNew()
- virtual ~H264VideoFileSink();
-
-protected: // redefined virtual functions:
- virtual void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime);
-
-private:
- char const* fSPropParameterSetsStr;
- Boolean fHaveWrittenFirstFrame;
+ virtual ~H265VideoFileSink();
};
#endif
diff --git a/liveMedia/include/H265VideoRTPSink.hh b/liveMedia/include/H265VideoRTPSink.hh
index a90df4a..4c4c42b 100644
--- a/liveMedia/include/H265VideoRTPSink.hh
+++ b/liveMedia/include/H265VideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for H.265 video
// C++ header
@@ -36,11 +36,13 @@ public:
u_int8_t const* pps, unsigned ppsSize);
// an optional variant of "createNew()", useful if we know, in advance,
// the stream's VPS, SPS and PPS NAL units.
+ // This avoids us having to 'pre-read' from the input source in order to get these values.
static H265VideoRTPSink*
createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
- char const* sPropVPSStr, char const* sPropSPSStr=NULL, char const* sPropPPSStr=NULL);
+ char const* sPropVPSStr, char const* sPropSPSStr, char const* sPropPPSStr);
// an optional variant of "createNew()", useful if we know, in advance,
// the stream's VPS, SPS and PPS NAL units.
+ // This avoids us having to 'pre-read' from the input source in order to get these values.
protected:
H265VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
diff --git a/liveMedia/include/MPEG1or2AudioRTPSource.hh b/liveMedia/include/H265VideoRTPSource.hh
similarity index 57%
copy from liveMedia/include/MPEG1or2AudioRTPSource.hh
copy to liveMedia/include/H265VideoRTPSource.hh
index e0c8d3b..1bb5c59 100644
--- a/liveMedia/include/MPEG1or2AudioRTPSource.hh
+++ b/liveMedia/include/H265VideoRTPSource.hh
@@ -14,38 +14,54 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// MPEG-1 or MPEG-2 Audio RTP Sources
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// H.265 Video RTP Sources
// C++ header
-#ifndef _MPEG_1OR2_AUDIO_RTP_SOURCE_HH
-#define _MPEG_1OR2_AUDIO_RTP_SOURCE_HH
+#ifndef _H265_VIDEO_RTP_SOURCE_HH
+#define _H265_VIDEO_RTP_SOURCE_HH
#ifndef _MULTI_FRAMED_RTP_SOURCE_HH
#include "MultiFramedRTPSource.hh"
#endif
-class MPEG1or2AudioRTPSource: public MultiFramedRTPSource {
+class H265VideoRTPSource: public MultiFramedRTPSource {
public:
- static MPEG1or2AudioRTPSource*
+ static H265VideoRTPSource*
createNew(UsageEnvironment& env, Groupsock* RTPgs,
- unsigned char rtpPayloadFormat = 14,
+ unsigned char rtpPayloadFormat,
+ Boolean expectDONFields = False,
unsigned rtpTimestampFrequency = 90000);
+ // "expectDONFields" is True iff we expect incoming H.265/RTP packets to contain
+ // DONL and DOND fields. I.e., if "tx-mode == "MST" or sprop-depack-buf-nalus > 0".
-protected:
- virtual ~MPEG1or2AudioRTPSource();
+ u_int64_t currentNALUnitAbsDon() const { return fCurrentNALUnitAbsDon; }
+ // the 'absolute decoding order number (AbsDon)' for the most-recently delivered NAL unit
-private:
- MPEG1or2AudioRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
+protected:
+ H265VideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
unsigned char rtpPayloadFormat,
+ Boolean expectDONFields,
unsigned rtpTimestampFrequency);
// called only by createNew()
-private:
+ virtual ~H265VideoRTPSource();
+
+protected:
// redefined virtual functions:
virtual Boolean processSpecialHeader(BufferedPacket* packet,
unsigned& resultSpecialHeaderSize);
virtual char const* MIMEtype() const;
+
+private:
+ void computeAbsDonFromDON(u_int16_t DON);
+
+private:
+ friend class H265BufferedPacket;
+ Boolean fExpectDONFields;
+ unsigned char fCurPacketNALUnitType;
+ u_int16_t fPreviousNALUnitDON;
+ u_int64_t fCurrentNALUnitAbsDon;
};
#endif
diff --git a/liveMedia/include/H265VideoStreamDiscreteFramer.hh b/liveMedia/include/H265VideoStreamDiscreteFramer.hh
index 4995c7f..b4230cb 100644
--- a/liveMedia/include/H265VideoStreamDiscreteFramer.hh
+++ b/liveMedia/include/H265VideoStreamDiscreteFramer.hh
@@ -14,15 +14,15 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "H265VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
// "H265VideoStreamFramer".
// C++ header
-#ifndef _H264_VIDEO_STREAM_DISCRETE_FRAMER_HH
-#define _H264_VIDEO_STREAM_DISCRETE_FRAMER_HH
+#ifndef _H265_VIDEO_STREAM_DISCRETE_FRAMER_HH
+#define _H265_VIDEO_STREAM_DISCRETE_FRAMER_HH
#ifndef _H264_OR_5_VIDEO_STREAM_DISCRETE_FRAMER_HH
#include "H264or5VideoStreamDiscreteFramer.hh"
diff --git a/liveMedia/include/H265VideoStreamFramer.hh b/liveMedia/include/H265VideoStreamFramer.hh
index 5d6940b..0e7348a 100644
--- a/liveMedia/include/H265VideoStreamFramer.hh
+++ b/liveMedia/include/H265VideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up a H.265 Video Elementary Stream into NAL units.
// C++ header
diff --git a/liveMedia/include/InputFile.hh b/liveMedia/include/InputFile.hh
index 0b5d9ee..4e83598 100644
--- a/liveMedia/include/InputFile.hh
+++ b/liveMedia/include/InputFile.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Common routines for opening/closing named input files
// C++ header
diff --git a/liveMedia/include/JPEGVideoRTPSink.hh b/liveMedia/include/JPEGVideoRTPSink.hh
index ae2579e..50dd30b 100644
--- a/liveMedia/include/JPEGVideoRTPSink.hh
+++ b/liveMedia/include/JPEGVideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for JPEG video (RFC 2435)
// C++ header
diff --git a/liveMedia/include/JPEGVideoRTPSource.hh b/liveMedia/include/JPEGVideoRTPSource.hh
index 7703560..3eb85ab 100644
--- a/liveMedia/include/JPEGVideoRTPSource.hh
+++ b/liveMedia/include/JPEGVideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// JPEG Video (RFC 2435) RTP Sources
// C++ header
diff --git a/liveMedia/include/JPEGVideoSource.hh b/liveMedia/include/JPEGVideoSource.hh
index 46b5e10..246f4b6 100644
--- a/liveMedia/include/JPEGVideoSource.hh
+++ b/liveMedia/include/JPEGVideoSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// JPEG video sources
// C++ header
diff --git a/liveMedia/include/Locale.hh b/liveMedia/include/Locale.hh
index 392d696..b327948 100644
--- a/liveMedia/include/Locale.hh
+++ b/liveMedia/include/Locale.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Support for temporarily setting the locale (e.g., to "C" or "POSIX") for (e.g.) parsing or printing
// floating-point numbers in protocol headers, or calling toupper()/tolower() on human-input strings.
// C++ header
diff --git a/liveMedia/include/MP3ADU.hh b/liveMedia/include/MP3ADU.hh
index 3223481..a0f1133 100644
--- a/liveMedia/include/MP3ADU.hh
+++ b/liveMedia/include/MP3ADU.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// 'ADU' MP3 streams (for improved loss-tolerance)
// C++ header
diff --git a/liveMedia/include/MP3ADURTPSink.hh b/liveMedia/include/MP3ADURTPSink.hh
index aa04f11..f8a7bbb 100644
--- a/liveMedia/include/MP3ADURTPSink.hh
+++ b/liveMedia/include/MP3ADURTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for 'ADUized' MP3 frames ("mpa-robust")
// C++ header
diff --git a/liveMedia/include/MP3ADURTPSource.hh b/liveMedia/include/MP3ADURTPSource.hh
index 348b23e..479b01d 100644
--- a/liveMedia/include/MP3ADURTPSource.hh
+++ b/liveMedia/include/MP3ADURTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP source for 'ADUized' MP3 frames ("mpa-robust")
// C++ header
diff --git a/liveMedia/include/MP3ADUTranscoder.hh b/liveMedia/include/MP3ADUTranscoder.hh
index 74784ed..4be2a82 100644
--- a/liveMedia/include/MP3ADUTranscoder.hh
+++ b/liveMedia/include/MP3ADUTranscoder.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Transcoder for ADUized MP3 frames
// C++ header
diff --git a/liveMedia/include/MP3ADUinterleaving.hh b/liveMedia/include/MP3ADUinterleaving.hh
index b1ed288..5225cea 100644
--- a/liveMedia/include/MP3ADUinterleaving.hh
+++ b/liveMedia/include/MP3ADUinterleaving.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Interleaving of MP3 ADUs
// C++ header
diff --git a/liveMedia/include/MP3AudioFileServerMediaSubsession.hh b/liveMedia/include/MP3AudioFileServerMediaSubsession.hh
index 4cf914d..6781532 100644
--- a/liveMedia/include/MP3AudioFileServerMediaSubsession.hh
+++ b/liveMedia/include/MP3AudioFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an MP3 audio file.
// (Actually, any MPEG-1 or MPEG-2 audio file should work.)
diff --git a/liveMedia/include/MP3FileSource.hh b/liveMedia/include/MP3FileSource.hh
index 5148842..32bf3d4 100644
--- a/liveMedia/include/MP3FileSource.hh
+++ b/liveMedia/include/MP3FileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 File Sources
// C++ header
diff --git a/liveMedia/include/MP3Transcoder.hh b/liveMedia/include/MP3Transcoder.hh
index 4c04596..6d4ff5e 100644
--- a/liveMedia/include/MP3Transcoder.hh
+++ b/liveMedia/include/MP3Transcoder.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP3 Transcoder
// C++ header
diff --git a/liveMedia/include/MPEG1or2AudioRTPSink.hh b/liveMedia/include/MPEG1or2AudioRTPSink.hh
index 16d0675..af2b44f 100644
--- a/liveMedia/include/MPEG1or2AudioRTPSink.hh
+++ b/liveMedia/include/MPEG1or2AudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG audio (RFC 2250)
// C++ header
diff --git a/liveMedia/include/MPEG1or2AudioRTPSource.hh b/liveMedia/include/MPEG1or2AudioRTPSource.hh
index e0c8d3b..613d033 100644
--- a/liveMedia/include/MPEG1or2AudioRTPSource.hh
+++ b/liveMedia/include/MPEG1or2AudioRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG-1 or MPEG-2 Audio RTP Sources
// C++ header
diff --git a/liveMedia/include/MPEG1or2AudioStreamFramer.hh b/liveMedia/include/MPEG1or2AudioStreamFramer.hh
index 85b4923..e884d95 100644
--- a/liveMedia/include/MPEG1or2AudioStreamFramer.hh
+++ b/liveMedia/include/MPEG1or2AudioStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG (1,2) audio elementary stream into frames
// C++ header
diff --git a/liveMedia/include/MPEG1or2Demux.hh b/liveMedia/include/MPEG1or2Demux.hh
index 53a76c5..5940cbc 100644
--- a/liveMedia/include/MPEG1or2Demux.hh
+++ b/liveMedia/include/MPEG1or2Demux.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Demultiplexer for a MPEG 1 or 2 Program Stream
// C++ header
diff --git a/liveMedia/include/MPEG1or2DemuxedElementaryStream.hh b/liveMedia/include/MPEG1or2DemuxedElementaryStream.hh
index 523b8b2..6e928eb 100644
--- a/liveMedia/include/MPEG1or2DemuxedElementaryStream.hh
+++ b/liveMedia/include/MPEG1or2DemuxedElementaryStream.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A MPEG 1 or 2 Elementary Stream, demultiplexed from a Program Stream
// C++ header
diff --git a/liveMedia/include/MPEG1or2DemuxedServerMediaSubsession.hh b/liveMedia/include/MPEG1or2DemuxedServerMediaSubsession.hh
index 7117ff6..b2851da 100644
--- a/liveMedia/include/MPEG1or2DemuxedServerMediaSubsession.hh
+++ b/liveMedia/include/MPEG1or2DemuxedServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-1 or 2 demuxer.
// C++ header
diff --git a/liveMedia/include/MPEG1or2FileServerDemux.hh b/liveMedia/include/MPEG1or2FileServerDemux.hh
index 359ba06..02e7bfd 100644
--- a/liveMedia/include/MPEG1or2FileServerDemux.hh
+++ b/liveMedia/include/MPEG1or2FileServerDemux.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A server demultiplexer for a MPEG 1 or 2 Program Stream
// C++ header
diff --git a/liveMedia/include/MPEG1or2VideoFileServerMediaSubsession.hh b/liveMedia/include/MPEG1or2VideoFileServerMediaSubsession.hh
index 93a46a9..cd9772c 100644
--- a/liveMedia/include/MPEG1or2VideoFileServerMediaSubsession.hh
+++ b/liveMedia/include/MPEG1or2VideoFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-1 or 2 Elementary Stream video file.
// C++ header
diff --git a/liveMedia/include/MPEG1or2VideoRTPSink.hh b/liveMedia/include/MPEG1or2VideoRTPSink.hh
index 2e91e90..f31cdf3 100644
--- a/liveMedia/include/MPEG1or2VideoRTPSink.hh
+++ b/liveMedia/include/MPEG1or2VideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG video (RFC 2250)
// C++ header
diff --git a/liveMedia/include/MPEG1or2VideoRTPSource.hh b/liveMedia/include/MPEG1or2VideoRTPSource.hh
index 20d58c9..7e57fdf 100644
--- a/liveMedia/include/MPEG1or2VideoRTPSource.hh
+++ b/liveMedia/include/MPEG1or2VideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG-1 or MPEG-2 Video RTP Sources
// C++ header
diff --git a/liveMedia/include/MPEG1or2VideoStreamDiscreteFramer.hh b/liveMedia/include/MPEG1or2VideoStreamDiscreteFramer.hh
index ac77367..b395b47 100644
--- a/liveMedia/include/MPEG1or2VideoStreamDiscreteFramer.hh
+++ b/liveMedia/include/MPEG1or2VideoStreamDiscreteFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "MPEG1or2VideoStreamFramer" that takes only
// complete, discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/include/MPEG1or2VideoStreamFramer.hh b/liveMedia/include/MPEG1or2VideoStreamFramer.hh
index 7d8516d..4a89a6c 100644
--- a/liveMedia/include/MPEG1or2VideoStreamFramer.hh
+++ b/liveMedia/include/MPEG1or2VideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG 1 or 2 video elementary stream into
// frames for: Video_Sequence_Header, GOP_Header, Picture_Header
// C++ header
diff --git a/liveMedia/include/MPEG2IndexFromTransportStream.hh b/liveMedia/include/MPEG2IndexFromTransportStream.hh
index d3ef8e8..cc3af93 100644
--- a/liveMedia/include/MPEG2IndexFromTransportStream.hh
+++ b/liveMedia/include/MPEG2IndexFromTransportStream.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that produces a sequence of I-frame indices from a MPEG-2 Transport Stream
// C++ header
diff --git a/liveMedia/include/MPEG2TransportFileServerMediaSubsession.hh b/liveMedia/include/MPEG2TransportFileServerMediaSubsession.hh
index 71596ec..72408fd 100644
--- a/liveMedia/include/MPEG2TransportFileServerMediaSubsession.hh
+++ b/liveMedia/include/MPEG2TransportFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-2 Transport Stream file.
// C++ header
@@ -40,7 +40,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
class ClientTrickPlayState; // forward
-class MPEG2TransportFileServerMediaSubsession: public FileServerMediaSubsession{
+class MPEG2TransportFileServerMediaSubsession: public FileServerMediaSubsession {
public:
static MPEG2TransportFileServerMediaSubsession*
createNew(UsageEnvironment& env,
diff --git a/liveMedia/include/MPEG2TransportStreamFramer.hh b/liveMedia/include/MPEG2TransportStreamFramer.hh
index a9e2dff..61fed50 100644
--- a/liveMedia/include/MPEG2TransportStreamFramer.hh
+++ b/liveMedia/include/MPEG2TransportStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that passes through (unchanged) chunks that contain an integral number
// of MPEG-2 Transport Stream packets, but returning (in "fDurationInMicroseconds")
// an updated estimate of the time gap between chunks.
diff --git a/liveMedia/include/MPEG2TransportStreamFromESSource.hh b/liveMedia/include/MPEG2TransportStreamFromESSource.hh
index bc0a44d..4887437 100644
--- a/liveMedia/include/MPEG2TransportStreamFromESSource.hh
+++ b/liveMedia/include/MPEG2TransportStreamFromESSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter for converting one or more MPEG Elementary Streams
// to a MPEG-2 Transport Stream
// C++ header
@@ -30,9 +30,12 @@ class MPEG2TransportStreamFromESSource: public MPEG2TransportStreamMultiplexor {
public:
static MPEG2TransportStreamFromESSource* createNew(UsageEnvironment& env);
- void addNewVideoSource(FramedSource* inputSource, int mpegVersion);
+ void addNewVideoSource(FramedSource* inputSource, int mpegVersion, int16_t PID = -1);
// Note: For MPEG-4 video, set "mpegVersion" to 4; for H.264 video, set "mpegVersion" to 5.
- void addNewAudioSource(FramedSource* inputSource, int mpegVersion);
+ void addNewAudioSource(FramedSource* inputSource, int mpegVersion, int16_t PID = -1);
+ // Note: In these functions, if "PID" is not -1, then it (currently, just the low 8 bits)
+ // is used as the stream's PID. Otherwise (if "PID" is -1) the 'stream_id' is used as
+ // the PID.
protected:
MPEG2TransportStreamFromESSource(UsageEnvironment& env);
@@ -40,7 +43,7 @@ protected:
virtual ~MPEG2TransportStreamFromESSource();
void addNewInputSource(FramedSource* inputSource,
- u_int8_t streamId, int mpegVersion);
+ u_int8_t streamId, int mpegVersion, int16_t PID = -1);
// used to implement addNew*Source() above
private:
@@ -52,6 +55,7 @@ private:
friend class InputESSourceRecord;
class InputESSourceRecord* fInputSources;
unsigned fVideoSourceCounter, fAudioSourceCounter;
+ Boolean fAwaitingBackgroundDelivery;
};
#endif
diff --git a/liveMedia/include/MPEG2TransportStreamFromPESSource.hh b/liveMedia/include/MPEG2TransportStreamFromPESSource.hh
index 769dcf0..7a6868e 100644
--- a/liveMedia/include/MPEG2TransportStreamFromPESSource.hh
+++ b/liveMedia/include/MPEG2TransportStreamFromPESSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter for converting a stream of MPEG PES packets to a MPEG-2 Transport Stream
// C++ header
diff --git a/liveMedia/include/MPEG2TransportStreamIndexFile.hh b/liveMedia/include/MPEG2TransportStreamIndexFile.hh
index c483575..93e04b7 100644
--- a/liveMedia/include/MPEG2TransportStreamIndexFile.hh
+++ b/liveMedia/include/MPEG2TransportStreamIndexFile.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class that encapsulates MPEG-2 Transport Stream 'index files'/
// These index files are used to implement 'trick play' operations
// (seek-by-time, fast forward, reverse play) on Transport Stream files.
diff --git a/liveMedia/include/MPEG2TransportStreamMultiplexor.hh b/liveMedia/include/MPEG2TransportStreamMultiplexor.hh
index aac6709..321b303 100644
--- a/liveMedia/include/MPEG2TransportStreamMultiplexor.hh
+++ b/liveMedia/include/MPEG2TransportStreamMultiplexor.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class for generating MPEG-2 Transport Stream from one or more input
// Elementary Stream data sources
// C++ header
@@ -40,9 +40,12 @@ protected:
// implemented by subclasses
void handleNewBuffer(unsigned char* buffer, unsigned bufferSize,
- int mpegVersion, MPEG1or2Demux::SCR scr);
- // called by "awaitNewBuffer()"
- // Note: For MPEG-4 video, set "mpegVersion" to 4; for H.264 video, set "mpegVersion" to 5.
+ int mpegVersion, MPEG1or2Demux::SCR scr, int16_t PID = -1);
+ // called by "awaitNewBuffer()"
+ // Note: For MPEG-4 video, set "mpegVersion" to 4; for H.264 video, set "mpegVersion" to 5.
+ // The buffer is assumed to be a PES packet, with a proper PES header.
+ // If "PID" is not -1, then it (currently, only the low 8 bits) is used as the stream's PID,
+ // otherwise the "stream_id" in the PES header is reused to be the stream's PID.
private:
// Redefined virtual functions:
@@ -77,4 +80,9 @@ private:
Boolean fIsFirstAdaptationField;
};
+
+// The CRC calculation function that Transport Streams use. We make this function public
+// here in case it's useful elsewhere:
+u_int32_t calculateCRC(u_int8_t const* data, unsigned dataLength, u_int32_t initialValue = 0xFFFFFFFF);
+
#endif
diff --git a/liveMedia/include/MPEG2TransportStreamTrickModeFilter.hh b/liveMedia/include/MPEG2TransportStreamTrickModeFilter.hh
index 46b55e8..da76b58 100644
--- a/liveMedia/include/MPEG2TransportStreamTrickModeFilter.hh
+++ b/liveMedia/include/MPEG2TransportStreamTrickModeFilter.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.// A filter that converts a MPEG Transport Stream file - with corresponding index file
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.// A filter that converts a MPEG Transport Stream file - with corresponding index file
// - to a corresponding Video Elementary Stream. It also uses a "scale" parameter
// to implement 'trick mode' (fast forward or reverse play, using I-frames) on
// the video stream.
diff --git a/liveMedia/include/MPEG2TransportUDPServerMediaSubsession.hh b/liveMedia/include/MPEG2TransportUDPServerMediaSubsession.hh
index c2e3c02..3d435cc 100644
--- a/liveMedia/include/MPEG2TransportUDPServerMediaSubsession.hh
+++ b/liveMedia/include/MPEG2TransportUDPServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an incoming UDP (or RTP/UDP) MPEG-2 Transport Stream
// C++ header
diff --git a/liveMedia/include/MPEG4ESVideoRTPSink.hh b/liveMedia/include/MPEG4ESVideoRTPSink.hh
index 76b0254..b423553 100644
--- a/liveMedia/include/MPEG4ESVideoRTPSink.hh
+++ b/liveMedia/include/MPEG4ESVideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG-4 Elementary Stream video (RFC 3016)
// C++ header
diff --git a/liveMedia/include/MPEG4ESVideoRTPSource.hh b/liveMedia/include/MPEG4ESVideoRTPSource.hh
index 2420acc..b78ec7d 100644
--- a/liveMedia/include/MPEG4ESVideoRTPSource.hh
+++ b/liveMedia/include/MPEG4ESVideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MP4V-ES video RTP stream sources
// C++ header
diff --git a/liveMedia/include/MPEG4GenericRTPSink.hh b/liveMedia/include/MPEG4GenericRTPSink.hh
index 1b9e0ed..28bd08e 100644
--- a/liveMedia/include/MPEG4GenericRTPSink.hh
+++ b/liveMedia/include/MPEG4GenericRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG4-GENERIC ("audio", "video", or "application") RTP stream sinks
// C++ header
diff --git a/liveMedia/include/MPEG4GenericRTPSource.hh b/liveMedia/include/MPEG4GenericRTPSource.hh
index f38cef1..932bccf 100644
--- a/liveMedia/include/MPEG4GenericRTPSource.hh
+++ b/liveMedia/include/MPEG4GenericRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG4-GENERIC ("audio", "video", or "application") RTP stream sources
// C++ header
diff --git a/liveMedia/include/MPEG4LATMAudioRTPSink.hh b/liveMedia/include/MPEG4LATMAudioRTPSink.hh
index 54bedbd..2d283e3 100644
--- a/liveMedia/include/MPEG4LATMAudioRTPSink.hh
+++ b/liveMedia/include/MPEG4LATMAudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for MPEG-4 audio, using LATM multiplexing (RFC 3016)
// (Note that the initial 'size' field is assumed to be present at the start of
// each frame.)
diff --git a/liveMedia/include/MPEG4LATMAudioRTPSource.hh b/liveMedia/include/MPEG4LATMAudioRTPSource.hh
index 9048c0e..f9f6e1d 100644
--- a/liveMedia/include/MPEG4LATMAudioRTPSource.hh
+++ b/liveMedia/include/MPEG4LATMAudioRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// MPEG-4 audio, using LATM multiplexing
// C++ header
diff --git a/liveMedia/include/MPEG4VideoFileServerMediaSubsession.hh b/liveMedia/include/MPEG4VideoFileServerMediaSubsession.hh
index b44c202..f9b4ca2 100644
--- a/liveMedia/include/MPEG4VideoFileServerMediaSubsession.hh
+++ b/liveMedia/include/MPEG4VideoFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from a MPEG-4 video file.
// C++ header
diff --git a/liveMedia/include/MPEG4VideoStreamDiscreteFramer.hh b/liveMedia/include/MPEG4VideoStreamDiscreteFramer.hh
index d8cad73..feff0f3 100644
--- a/liveMedia/include/MPEG4VideoStreamDiscreteFramer.hh
+++ b/liveMedia/include/MPEG4VideoStreamDiscreteFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simplified version of "MPEG4VideoStreamFramer" that takes only complete,
// discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
diff --git a/liveMedia/include/MPEG4VideoStreamFramer.hh b/liveMedia/include/MPEG4VideoStreamFramer.hh
index 3078b3d..75c59a1 100644
--- a/liveMedia/include/MPEG4VideoStreamFramer.hh
+++ b/liveMedia/include/MPEG4VideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG-4 video elementary stream into
// frames for:
// - Visual Object Sequence (VS) Header + Visual Object (VO) Header
diff --git a/liveMedia/include/MPEGVideoStreamFramer.hh b/liveMedia/include/MPEGVideoStreamFramer.hh
index b624396..85aa7be 100644
--- a/liveMedia/include/MPEGVideoStreamFramer.hh
+++ b/liveMedia/include/MPEGVideoStreamFramer.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A filter that breaks up an MPEG video elementary stream into
// headers and frames
// C++ header
@@ -54,6 +54,7 @@ protected:
private: // redefined virtual functions
virtual void doGetNextFrame();
+ virtual void doStopGettingFrames();
private:
void reset();
diff --git a/liveMedia/include/MatroskaFile.hh b/liveMedia/include/MatroskaFile.hh
index a0b2e5f..df174b7 100644
--- a/liveMedia/include/MatroskaFile.hh
+++ b/liveMedia/include/MatroskaFile.hh
@@ -14,15 +14,15 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A class that encapsulates a Matroska file.
// C++ header
#ifndef _MATROSKA_FILE_HH
#define _MATROSKA_FILE_HH
-#ifndef _MEDIA_HH
-#include "Media.hh"
+#ifndef _RTP_SINK_HH
+#include "RTPSink.hh"
#endif
#ifndef _HASH_TABLE_HH
#include "HashTable.hh"
@@ -56,6 +56,17 @@ public:
unsigned chosenAudioTrackNumber() { return fChosenAudioTrackNumber; }
unsigned chosenSubtitleTrackNumber() { return fChosenSubtitleTrackNumber; }
+ FramedSource*
+ createSourceForStreaming(FramedSource* baseSource, unsigned trackNumber,
+ unsigned& estBitrate, unsigned& numFiltersInFrontOfTrack);
+ // Takes a data source (which must be a demultiplexed track from this file) and returns
+ // a (possibly modified) data source that can be used for streaming.
+
+ RTPSink* createRTPSinkForTrackNumber(unsigned trackNumber, Groupsock* rtpGroupsock,
+ unsigned char rtpPayloadTypeIfDynamic);
+ // Creates a "RTPSink" object that would be appropriate for streaming the specified track,
+ // or NULL if no appropriate "RTPSink" exists
+
private:
MatroskaFile(UsageEnvironment& env, char const* fileName, onCreationFunc* onCreation, void* onCreationClientData,
char const* preferredLanguage);
@@ -117,6 +128,7 @@ public:
unsigned codecPrivateSize;
u_int8_t* codecPrivate;
Boolean codecPrivateUsesH264FormatForH265; // a hack specifically for H.265 video tracks
+ Boolean codecIsOpus; // a hack for Opus audio
unsigned headerStrippedBytesSize;
u_int8_t* headerStrippedBytes;
unsigned subframeSizeSize; // 0 means: frames do not have subframes (the default behavior)
diff --git a/liveMedia/include/MatroskaFileServerDemux.hh b/liveMedia/include/MatroskaFileServerDemux.hh
index e7d75bf..53c2802 100644
--- a/liveMedia/include/MatroskaFileServerDemux.hh
+++ b/liveMedia/include/MatroskaFileServerDemux.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A server demultiplexor for a Matroska file
// C++ header
@@ -54,7 +54,6 @@ public:
MatroskaFile* ourMatroskaFile() { return fOurMatroskaFile; }
char const* fileName() const { return fFileName; }
float fileDuration() const { return fOurMatroskaFile->fileDuration(); }
- MatroskaTrack* lookup(unsigned trackNumber) { return fOurMatroskaFile->lookup(trackNumber); } // shortcut
FramedSource* newDemuxedTrack(unsigned clientSessionId, unsigned trackNumber);
// Used by the "ServerMediaSubsession" objects to implement their "createNewStreamSource()" virtual function.
diff --git a/liveMedia/include/Media.hh b/liveMedia/include/Media.hh
index 7705a14..f20029b 100644
--- a/liveMedia/include/Media.hh
+++ b/liveMedia/include/Media.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Medium
// C++ header
@@ -68,7 +68,6 @@ public:
virtual Boolean isRTSPServer() const;
virtual Boolean isMediaSession() const;
virtual Boolean isServerMediaSession() const;
- virtual Boolean isDarwinInjector() const;
protected:
friend class MediaLookupTable;
@@ -120,7 +119,7 @@ private:
class _Tables {
public:
static _Tables* getOurTables(UsageEnvironment& env, Boolean createIfNotPresent = True);
- // returns a pointer to an "ourTables" structure (creating it if necessary)
+ // returns a pointer to a "_Tables" structure (creating it if necessary)
void reclaimIfPossible();
// used to delete ourselves when we're no longer used
diff --git a/liveMedia/include/MediaSession.hh b/liveMedia/include/MediaSession.hh
index 3552756..048dde1 100644
--- a/liveMedia/include/MediaSession.hh
+++ b/liveMedia/include/MediaSession.hh
@@ -14,19 +14,21 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A data structure that represents a session that consists of
// potentially multiple (audio and/or video) sub-sessions
// (This data structure is used for media *receivers* - i.e., clients.
// For media streamers, use "ServerMediaSession" instead.)
// C++ header
-/* NOTE: To support receiving your own custom RTP payload format, you must first define a new subclass of "MultiFramedRTPSource"
- (or "BasicUDPSource") that implements it. Then define your own subclass of "MediaSession" and "MediaSubsession", as follows:
+/* NOTE: To support receiving your own custom RTP payload format, you must first define a new
+ subclass of "MultiFramedRTPSource" (or "BasicUDPSource") that implements it.
+ Then define your own subclass of "MediaSession" and "MediaSubsession", as follows:
- In your subclass of "MediaSession" (named, for example, "myMediaSession"):
- Define and implement your own static member function
static myMediaSession* createNew(UsageEnvironment& env, char const* sdpDescription);
- and call this - instead of "MediaSession::createNew()" - in your application, when you create a new "MediaSession" object.
+ and call this - instead of "MediaSession::createNew()" - in your application,
+ when you create a new "MediaSession" object.
- Reimplement the "createNewMediaSubsession()" virtual function, as follows:
MediaSubsession* myMediaSession::createNewMediaSubsession() { return new myMediaSubsession(*this); }
- In your subclass of "MediaSubsession" (named, for example, "myMediaSubsession"):
@@ -69,6 +71,7 @@ public:
char const* CNAME() const { return fCNAME; }
struct in_addr const& sourceFilterAddr() const { return fSourceFilterAddr; }
float& scale() { return fScale; }
+ float& speed() { return fSpeed; }
char* mediaSessionType() const { return fMediaSessionType; }
char* sessionName() const { return fSessionName; }
char* sessionDescription() const { return fSessionDescription; }
@@ -130,6 +133,7 @@ protected:
char* fAbsEndTime;
struct in_addr fSourceFilterAddr; // used for SSM
float fScale; // set from a RTSP "Scale:" header
+ float fSpeed;
char* fMediaSessionType; // holds a=type value
char* fSessionName; // holds s=<session name> value
char* fSessionDescription; // holds i=<session description> value
@@ -170,10 +174,12 @@ public:
unsigned videoFPS() const { return fVideoFPS; }
unsigned numChannels() const { return fNumChannels; }
float& scale() { return fScale; }
+ float& speed() { return fSpeed; }
RTPSource* rtpSource() { return fRTPSource; }
RTCPInstance* rtcpInstance() { return fRTCPInstance; }
unsigned rtpTimestampFrequency() const { return fRTPTimestampFrequency; }
+ Boolean rtcpIsMuxed() const { return fMultiplexRTCPWithRTP; }
FramedSource* readSource() { return fReadSource; }
// This is the source that client sinks read from. It is usually
// (but not necessarily) the same as "rtpSource()"
@@ -201,8 +207,8 @@ public:
// description does not specfy a client port number - an ephemeral
// (even) port number is chosen.) This routine must *not* be
// called after initiate().
- void receiveRawMP3ADUs() { fReceiveRawMP3ADUs = True; } // optional hack for audio/MPA-ROBUST; must not be called after Initiate()
- void receiveRawJPEGFrames() { fReceiveRawJPEGFrames = True; } // optional hack for video/JPEG; must not be called after Initiate()
+ void receiveRawMP3ADUs() { fReceiveRawMP3ADUs = True; } // optional hack for audio/MPA-ROBUST; must not be called after initiate()
+ void receiveRawJPEGFrames() { fReceiveRawJPEGFrames = True; } // optional hack for video/JPEG; must not be called after initiate()
char*& connectionEndpointName() { return fConnectionEndpointName; }
char const* connectionEndpointName() const {
return fConnectionEndpointName;
@@ -211,33 +217,23 @@ public:
// 'Bandwidth' parameter, set in the "b=" SDP line:
unsigned bandwidth() const { return fBandwidth; }
- // Various parameters set in "a=fmtp:" SDP lines:
- unsigned fmtp_auxiliarydatasizelength() const { return fAuxiliarydatasizelength; }
- unsigned fmtp_constantduration() const { return fConstantduration; }
- unsigned fmtp_constantsize() const { return fConstantsize; }
- unsigned fmtp_crc() const { return fCRC; }
- unsigned fmtp_ctsdeltalength() const { return fCtsdeltalength; }
- unsigned fmtp_de_interleavebuffersize() const { return fDe_interleavebuffersize; }
- unsigned fmtp_dtsdeltalength() const { return fDtsdeltalength; }
- unsigned fmtp_indexdeltalength() const { return fIndexdeltalength; }
- unsigned fmtp_indexlength() const { return fIndexlength; }
- unsigned fmtp_interleaving() const { return fInterleaving; }
- unsigned fmtp_maxdisplacement() const { return fMaxdisplacement; }
- unsigned fmtp_objecttype() const { return fObjecttype; }
- unsigned fmtp_octetalign() const { return fOctetalign; }
- unsigned fmtp_profile_level_id() const { return fProfile_level_id; }
- unsigned fmtp_robustsorting() const { return fRobustsorting; }
- unsigned fmtp_sizelength() const { return fSizelength; }
- unsigned fmtp_streamstateindication() const { return fStreamstateindication; }
- unsigned fmtp_streamtype() const { return fStreamtype; }
- Boolean fmtp_cpresent() const { return fCpresent; }
- Boolean fmtp_randomaccessindication() const { return fRandomaccessindication; }
- char const* fmtp_config() const { return fConfig; }
+ // General SDP attribute accessor functions:
+ char const* attrVal_str(char const* attrName) const;
+ // returns "" if attribute doesn't exist (and has no default value), or is not a string
+ char const* attrVal_strToLower(char const* attrName) const;
+ // returns "" if attribute doesn't exist (and has no default value), or is not a string
+ unsigned attrVal_int(char const* attrName) const;
+ // also returns 0 if attribute doesn't exist (and has no default value)
+ unsigned attrVal_unsigned(char const* attrName) const { return (unsigned)attrVal_int(attrName); }
+ Boolean attrVal_bool(char const* attrName) const { return attrVal_int(attrName) != 0; }
+
+ // Old, now-deprecated SDP attribute accessor functions, kept here for backwards-compatibility:
+ char const* fmtp_config() const;
char const* fmtp_configuration() const { return fmtp_config(); }
- char const* fmtp_mode() const { return fMode; }
- char const* fmtp_spropparametersets() const { return fSpropParameterSets; }
- char const* fmtp_emphasis() const { return fEmphasis; }
- char const* fmtp_channelorder() const { return fChannelOrder; }
+ char const* fmtp_spropparametersets() const { return attrVal_str("sprop-parameter-sets"); }
+ char const* fmtp_spropvps() const { return attrVal_str("sprop-vps"); }
+ char const* fmtp_spropsps() const { return attrVal_str("sprop-sps"); }
+ char const* fmtp_sproppps() const { return attrVal_str("sprop-pps"); }
netAddressBits connectionEndpointAddress() const;
// Converts "fConnectionEndpointName" to an address (or 0 if unknown)
@@ -281,9 +277,12 @@ protected:
UsageEnvironment& env() { return fParent.envir(); }
void setNext(MediaSubsession* next) { fNext = next; }
+ void setAttribute(char const* name, char const* value = NULL, Boolean valueIsHexadecimal = False);
+
Boolean parseSDPLine_c(char const* sdpLine);
Boolean parseSDPLine_b(char const* sdpLine);
Boolean parseSDPAttribute_rtpmap(char const* sdpLine);
+ Boolean parseSDPAttribute_rtcpmux(char const* sdpLine);
Boolean parseSDPAttribute_control(char const* sdpLine);
Boolean parseSDPAttribute_range(char const* sdpLine);
Boolean parseSDPAttribute_fmtp(char const* sdpLine);
@@ -309,20 +308,11 @@ protected:
char* fCodecName;
char* fProtocolName;
unsigned fRTPTimestampFrequency;
+ Boolean fMultiplexRTCPWithRTP;
char* fControlPath; // holds optional a=control: string
struct in_addr fSourceFilterAddr; // used for SSM
unsigned fBandwidth; // in kilobits-per-second, from b= line
- // Parameters set by "a=fmtp:" SDP lines:
- unsigned fAuxiliarydatasizelength, fConstantduration, fConstantsize;
- unsigned fCRC, fCtsdeltalength, fDe_interleavebuffersize, fDtsdeltalength;
- unsigned fIndexdeltalength, fIndexlength, fInterleaving;
- unsigned fMaxdisplacement, fObjecttype;
- unsigned fOctetalign, fProfile_level_id, fRobustsorting;
- unsigned fSizelength, fStreamstateindication, fStreamtype;
- Boolean fCpresent, fRandomaccessindication;
- char *fConfig, *fMode, *fSpropParameterSets, *fEmphasis, *fChannelOrder;
-
double fPlayStartTime;
double fPlayEndTime;
char* fAbsStartTime;
@@ -334,7 +324,9 @@ protected:
unsigned fNumChannels;
// optionally set by "a=rtpmap:" lines for audio sessions. Default: 1
float fScale; // set from a RTSP "Scale:" header
+ float fSpeed;
double fNPT_PTS_Offset; // set by "getNormalPlayTime()"; add this to a PTS to get NPT
+ HashTable* fAttributeTable; // for "a=fmtp:" attributes. (Later an array by payload type #####)
// Fields set or used by initiate():
Groupsock* fRTPSocket; Groupsock* fRTCPSocket; // works even for unicast
diff --git a/liveMedia/include/MediaSink.hh b/liveMedia/include/MediaSink.hh
index e7430c1..367083a 100644
--- a/liveMedia/include/MediaSink.hh
+++ b/liveMedia/include/MediaSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Media Sinks
// C++ header
@@ -70,10 +70,13 @@ private:
// A data structure that a sink may use for an output packet:
class OutPacketBuffer {
public:
- OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize);
+ OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize,
+ unsigned maxBufferSize = 0);
+ // if "maxBufferSize" is >0, use it - instead of "maxSize" to compute the buffer size
~OutPacketBuffer();
static unsigned maxSize;
+ static void increaseMaxSizeTo(unsigned newMaxSize) { if (newMaxSize > OutPacketBuffer::maxSize) OutPacketBuffer::maxSize = newMaxSize; }
unsigned char* curPtr() const {return &fBuf[fPacketStart + fCurOffset];}
unsigned totalBytesAvailable() const {
diff --git a/liveMedia/include/MediaSource.hh b/liveMedia/include/MediaSource.hh
index d608a40..a8ad5df 100644
--- a/liveMedia/include/MediaSource.hh
+++ b/liveMedia/include/MediaSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Media Sources
// C++ header
diff --git a/liveMedia/include/MediaTranscodingTable.hh b/liveMedia/include/MediaTranscodingTable.hh
new file mode 100644
index 0000000..694a0c6
--- /dev/null
+++ b/liveMedia/include/MediaTranscodingTable.hh
@@ -0,0 +1,57 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A class that implements a database that can be accessed to create
+// "FramedFilter" (subclass) objects that transcode one codec into another.
+// The implementation of this class just returns NULL for each codec lookup;
+// To actually implement transcoding, you would subclass it.
+// C++ header
+
+#ifndef _MEDIA_TRANSCODING_TABLE_HH
+#define _MEDIA_TRANSCODING_TABLE_HH
+
+#ifndef _FRAMED_FILTER_HH
+#include "FramedFilter.hh"
+#endif
+#ifndef _MEDIA_SESSION_HH
+#include "MediaSession.hh"
+#endif
+
+class MediaTranscodingTable: public Medium {
+public:
+ virtual FramedFilter*
+ lookupTranscoder(MediaSubsession& /*inputCodecDescription*/, // in
+ char*& outputCodecName/* out; must be delete[]d later */) {
+ // Default implementation: Return NULL (indicating: no transcoding).
+ // You would reimplement this virtual function in a subclass to return a new 'transcoding'
+ // "FramedFilter" (subclass) object for each ("mediumName","codecName") that you wish to
+ // transcode (or return NULL for no transcoding).
+ // (Note that "inputCodecDescription" must have a non-NULL "readSource()"; this is used
+ // as the input to the new "FramedFilter" (subclass) object.)
+ outputCodecName = NULL;
+ return NULL;
+ }
+
+protected: // we are to be subclassed only
+ MediaTranscodingTable(UsageEnvironment& env)
+ : Medium(env) {
+ }
+ virtual ~MediaTranscodingTable() {
+ }
+};
+
+#endif
diff --git a/liveMedia/include/MultiFramedRTPSink.hh b/liveMedia/include/MultiFramedRTPSink.hh
index 594f339..7215818 100644
--- a/liveMedia/include/MultiFramedRTPSink.hh
+++ b/liveMedia/include/MultiFramedRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for a common kind of payload format: Those which pack multiple,
// complete codec frames (as many as possible) into each RTP packet.
// C++ header
diff --git a/liveMedia/include/MultiFramedRTPSource.hh b/liveMedia/include/MultiFramedRTPSource.hh
index 97c0dee..424fc3f 100644
--- a/liveMedia/include/MultiFramedRTPSource.hh
+++ b/liveMedia/include/MultiFramedRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP source for a common kind of payload format: Those which pack multiple,
// complete codec frames (as many as possible) into each RTP packet.
// C++ header
@@ -91,7 +91,7 @@ public:
Boolean hasUsableData() const { return fTail > fHead; }
unsigned useCount() const { return fUseCount; }
- Boolean fillInData(RTPInterface& rtpInterface, Boolean& packetReadWasIncomplete);
+ Boolean fillInData(RTPInterface& rtpInterface, struct sockaddr_in& fromAddress, Boolean& packetReadWasIncomplete);
void assignMiscParams(unsigned short rtpSeqNo, unsigned rtpTimestamp,
struct timeval presentationTime,
Boolean hasBeenSyncedUsingRTCP,
diff --git a/liveMedia/include/OggFile.hh b/liveMedia/include/OggFile.hh
new file mode 100644
index 0000000..ee9bcc8
--- /dev/null
+++ b/liveMedia/include/OggFile.hh
@@ -0,0 +1,177 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A class that encapsulates an Ogg file
+// C++ header
+
+#ifndef _OGG_FILE_HH
+#define _OGG_FILE_HH
+
+#ifndef _RTP_SINK_HH
+#include "RTPSink.hh"
+#endif
+#ifndef _HASH_TABLE_HH
+#include "HashTable.hh"
+#endif
+
+class OggTrack; // forward
+class OggDemux; // forward
+
+class OggFile: public Medium {
+public:
+ typedef void (onCreationFunc)(OggFile* newFile, void* clientData);
+ static void createNew(UsageEnvironment& env, char const* fileName,
+ onCreationFunc* onCreation, void* onCreationClientData);
+ // Note: Unlike most "createNew()" functions, this one doesn't return a new object
+ // immediately. Instead, because this class requires file reading (to parse the
+ // Ogg track headers) before a new object can be initialized, the creation of a new object
+ // is signalled by calling - from the event loop - an 'onCreationFunc' that is passed as
+ // a parameter to "createNew()".
+
+ OggTrack* lookup(u_int32_t trackNumber);
+
+ OggDemux* newDemux();
+ // Creates a demultiplexor for extracting tracks from this file.
+ // (Separate clients will typically have separate demultiplexors.)
+
+ char const* fileName() const { return fFileName; }
+ unsigned numTracks() const;
+
+ FramedSource*
+ createSourceForStreaming(FramedSource* baseSource, u_int32_t trackNumber,
+ unsigned& estBitrate, unsigned& numFiltersInFrontOfTrack);
+ // Takes a data source (which must be a demultiplexed track from this file) and returns
+ // a (possibly modified) data source that can be used for streaming.
+
+ RTPSink* createRTPSinkForTrackNumber(u_int32_t trackNumber, Groupsock* rtpGroupsock,
+ unsigned char rtpPayloadTypeIfDynamic);
+ // Creates a "RTPSink" object that would be appropriate for streaming the specified track,
+ // or NULL if no appropriate "RTPSink" exists
+
+ class OggTrackTable& trackTable() { return *fTrackTable; }
+
+private:
+ OggFile(UsageEnvironment& env, char const* fileName, onCreationFunc* onCreation, void* onCreationClientData);
+ // called only by createNew()
+ virtual ~OggFile();
+
+ static void handleEndOfBosPageParsing(void* clientData);
+ void handleEndOfBosPageParsing();
+
+ void addTrack(OggTrack* newTrack);
+ void removeDemux(OggDemux* demux);
+
+private:
+ friend class OggFileParser;
+ friend class OggDemux;
+ char const* fFileName;
+ onCreationFunc* fOnCreation;
+ void* fOnCreationClientData;
+
+ class OggTrackTable* fTrackTable;
+ HashTable* fDemuxesTable;
+ class OggFileParser* fParserForInitialization;
+};
+
+class OggTrack {
+public:
+ OggTrack();
+ virtual ~OggTrack();
+
+ // track parameters
+ u_int32_t trackNumber; // bitstream serial number
+ char const* mimeType; // NULL if not known
+
+ unsigned samplingFrequency, numChannels; // for audio tracks
+ unsigned estBitrate; // estimate, in kbps (for RTCP)
+
+ // Special headers for Vorbis audio, Theora video, and Opus audio tracks:
+ struct _vtoHdrs {
+ u_int8_t* header[3]; // "identification", "comment", "setup"
+ unsigned headerSize[3];
+
+ // Fields specific to Vorbis audio:
+ unsigned blocksize[2]; // samples per frame (packet)
+ unsigned uSecsPerPacket[2]; // computed as (blocksize[i]*1000000)/samplingFrequency
+ unsigned vorbis_mode_count;
+ unsigned ilog_vorbis_mode_count_minus_1;
+ u_int8_t* vorbis_mode_blockflag;
+ // an array (of size "vorbis_mode_count") of indexes into the (2-entry) "blocksize" array
+
+ // Fields specific to Theora video:
+ u_int8_t KFGSHIFT;
+ unsigned uSecsPerFrame;
+
+ } vtoHdrs;
+
+ Boolean weNeedHeaders() const {
+ return
+ vtoHdrs.header[0] == NULL ||
+ vtoHdrs.header[1] == NULL ||
+ (vtoHdrs.header[2] == NULL && strcmp(mimeType, "audio/OPUS") != 0);
+ }
+};
+
+class OggTrackTableIterator {
+public:
+ OggTrackTableIterator(class OggTrackTable& ourTable);
+ virtual ~OggTrackTableIterator();
+
+ OggTrack* next();
+
+private:
+ HashTable::Iterator* fIter;
+};
+
+class OggDemux: public Medium {
+public:
+ FramedSource* newDemuxedTrack(u_int32_t& resultTrackNumber);
+ // Returns a new stream ("FramedSource" subclass) that represents the next media track
+ // from the file. This function returns NULL when no more media tracks exist.
+
+ FramedSource* newDemuxedTrackByTrackNumber(unsigned trackNumber);
+ // As above, but creates a new stream for a specific track number within the Matroska file.
+ // (You should not call this function more than once with the same track number.)
+
+ // Note: We assume that:
+ // - Every track created by "newDemuxedTrack()" is later read
+ // - All calls to "newDemuxedTrack()" are made before any track is read
+
+protected:
+ friend class OggFile;
+ friend class OggFileParser;
+ class OggDemuxedTrack* lookupDemuxedTrack(u_int32_t trackNumber);
+
+ OggDemux(OggFile& ourFile);
+ virtual ~OggDemux();
+
+private:
+ friend class OggDemuxedTrack;
+ void removeTrack(u_int32_t trackNumber);
+ void continueReading(); // called by a demuxed track to tell us that it has a pending read ("doGetNextFrame()")
+
+ static void handleEndOfFile(void* clientData);
+ void handleEndOfFile();
+
+private:
+ OggFile& fOurFile;
+ class OggFileParser* fOurParser;
+ HashTable* fDemuxedTracksTable;
+ OggTrackTableIterator* fIter;
+};
+
+#endif
diff --git a/liveMedia/include/MatroskaFileServerDemux.hh b/liveMedia/include/OggFileServerDemux.hh
similarity index 50%
copy from liveMedia/include/MatroskaFileServerDemux.hh
copy to liveMedia/include/OggFileServerDemux.hh
index e7d75bf..51dd0ad 100644
--- a/liveMedia/include/MatroskaFileServerDemux.hh
+++ b/liveMedia/include/OggFileServerDemux.hh
@@ -14,72 +14,68 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// A server demultiplexor for a Matroska file
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// A server demultiplexor for an Ogg file
// C++ header
-#ifndef _MATROSKA_FILE_SERVER_DEMUX_HH
-#define _MATROSKA_FILE_SERVER_DEMUX_HH
+#ifndef _OGG_FILE_SERVER_DEMUX_HH
+#define _OGG_FILE_SERVER_DEMUX_HH
#ifndef _SERVER_MEDIA_SESSION_HH
#include "ServerMediaSession.hh"
#endif
-#ifndef _MATROSKA_FILE_HH
-#include "MatroskaFile.hh"
+#ifndef _OGG_FILE_HH
+#include "OggFile.hh"
#endif
-class MatroskaFileServerDemux: public Medium {
+class OggFileServerDemux: public Medium {
public:
- typedef void (onCreationFunc)(MatroskaFileServerDemux* newDemux, void* clientData);
+ typedef void (onCreationFunc)(OggFileServerDemux* newDemux, void* clientData);
static void createNew(UsageEnvironment& env, char const* fileName,
- onCreationFunc* onCreation, void* onCreationClientData,
- char const* preferredLanguage = "eng");
+ onCreationFunc* onCreation, void* onCreationClientData);
// Note: Unlike most "createNew()" functions, this one doesn't return a new object immediately. Instead, because this class
- // requires file reading (to parse the Matroska 'Track' headers) before a new object can be initialized, the creation of a new
+ // requires file reading (to parse the Ogg 'Track' headers) before a new object can be initialized, the creation of a new
// object is signalled by calling - from the event loop - an 'onCreationFunc' that is passed as a parameter to "createNew()".
ServerMediaSubsession* newServerMediaSubsession();
- ServerMediaSubsession* newServerMediaSubsession(unsigned& resultTrackNumber);
- // Returns a new "ServerMediaSubsession" object that represents the next preferred media track
- // (video, audio, subtitle - in that order) from the file. (Preferred media tracks are based on the file's language preference.)
- // This function returns NULL when no more media tracks exist.
+ ServerMediaSubsession* newServerMediaSubsession(u_int32_t& resultTrackNumber);
+ // Returns a new "ServerMediaSubsession" object that represents the next media track
+ // from the file. This function returns NULL when no more media tracks exist.
- ServerMediaSubsession* newServerMediaSubsessionByTrackNumber(unsigned trackNumber);
- // As above, but creates a new "ServerMediaSubsession" object for a specific track number within the Matroska file.
- // (You should not call this function more than once with the same track number.)
+ ServerMediaSubsession* newServerMediaSubsessionByTrackNumber(u_int32_t trackNumber);
+ // As above, but creates a new "ServerMediaSubsession" object for a specific track number
+ // within the Ogg file.
+ // (You should not call this function more than once with the same track number.)
// The following public: member functions are called only by the "ServerMediaSubsession" objects:
- MatroskaFile* ourMatroskaFile() { return fOurMatroskaFile; }
+ OggFile* ourOggFile() { return fOurOggFile; }
char const* fileName() const { return fFileName; }
- float fileDuration() const { return fOurMatroskaFile->fileDuration(); }
- MatroskaTrack* lookup(unsigned trackNumber) { return fOurMatroskaFile->lookup(trackNumber); } // shortcut
- FramedSource* newDemuxedTrack(unsigned clientSessionId, unsigned trackNumber);
+ FramedSource* newDemuxedTrack(unsigned clientSessionId, u_int32_t trackNumber);
// Used by the "ServerMediaSubsession" objects to implement their "createNewStreamSource()" virtual function.
private:
- MatroskaFileServerDemux(UsageEnvironment& env, char const* fileName,
- onCreationFunc* onCreation, void* onCreationClientData,
- char const* preferredLanguage);
+ OggFileServerDemux(UsageEnvironment& env, char const* fileName,
+ onCreationFunc* onCreation, void* onCreationClientData);
// called only by createNew()
- virtual ~MatroskaFileServerDemux();
+ virtual ~OggFileServerDemux();
- static void onMatroskaFileCreation(MatroskaFile* newFile, void* clientData);
- void onMatroskaFileCreation(MatroskaFile* newFile);
+ static void onOggFileCreation(OggFile* newFile, void* clientData);
+ void onOggFileCreation(OggFile* newFile);
private:
char const* fFileName;
onCreationFunc* fOnCreation;
void* fOnCreationClientData;
- MatroskaFile* fOurMatroskaFile;
+ OggFile* fOurOggFile;
// Used to implement "newServerMediaSubsession()":
- u_int8_t fNextTrackTypeToCheck;
+ OggTrackTableIterator* fIter;
// Used to set up demuxing, to implement "newDemuxedTrack()":
unsigned fLastClientSessionId;
- MatroskaDemux* fLastCreatedDemux;
+ OggDemux* fLastCreatedDemux;
};
#endif
diff --git a/liveMedia/include/OggFileSink.hh b/liveMedia/include/OggFileSink.hh
new file mode 100644
index 0000000..bbb2a9a
--- /dev/null
+++ b/liveMedia/include/OggFileSink.hh
@@ -0,0 +1,79 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// 'Ogg' File Sink (recording a single media track only)
+// C++ header
+
+#ifndef _OGG_FILE_SINK_HH
+#define _OGG_FILE_SINK_HH
+
+#ifndef _FILE_SINK_HH
+#include "FileSink.hh"
+#endif
+
+class OggFileSink: public FileSink {
+public:
+ static OggFileSink* createNew(UsageEnvironment& env, char const* fileName,
+ unsigned samplingFrequency = 0, // used for granule_position
+ char const* configStr = NULL,
+ // "configStr" is an optional 'SDP format' string (Base64-encoded)
+ // representing 'packed configuration headers' ("identification", "comment", "setup")
+ // to prepend to the output. (For 'Vorbis" audio and 'Theora' video.)
+ unsigned bufferSize = 100000,
+ Boolean oneFilePerFrame = False);
+ // See "FileSink.hh" for a description of these parameters.
+
+protected:
+ OggFileSink(UsageEnvironment& env, FILE* fid, unsigned samplingFrequency, char const* configStr,
+ unsigned bufferSize, char const* perFrameFileNamePrefix);
+ // called only by createNew()
+ virtual ~OggFileSink();
+
+protected: // redefined virtual functions:
+ virtual Boolean continuePlaying();
+ virtual void addData(unsigned char const* data, unsigned dataSize,
+ struct timeval presentationTime);
+ virtual void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
+ struct timeval presentationTime);
+
+private:
+ static void ourOnSourceClosure(void* clientData);
+ void ourOnSourceClosure();
+
+private:
+ unsigned fSamplingFrequency;
+ char const* fConfigStr;
+ Boolean fHaveWrittenFirstFrame, fHaveSeenEOF;
+ struct timeval fFirstPresentationTime;
+ int64_t fGranulePosition;
+ int64_t fGranulePositionAdjustment; // used to ensure that "fGranulePosition" stays monotonic
+ u_int32_t fPageSequenceNumber;
+ u_int8_t fPageHeaderBytes[27];
+ // the header of each Ogg page, through the "number_page_segments" byte
+
+ // Special fields used for Theora video:
+ Boolean fIsTheora;
+ u_int64_t fGranuleIncrementPerFrame; // == 1 << KFGSHIFT
+
+ // Because the last Ogg page before EOF needs to have a special 'eos' bit set in the header,
+ // we need to defer the writing of each incoming frame. To do this, we maintain a 2nd buffer:
+ unsigned char* fAltBuffer;
+ unsigned fAltFrameSize, fAltNumTruncatedBytes;
+ struct timeval fAltPresentationTime;
+};
+
+#endif
diff --git a/liveMedia/include/OnDemandServerMediaSubsession.hh b/liveMedia/include/OnDemandServerMediaSubsession.hh
index d916d8c..8a8affd 100644
--- a/liveMedia/include/OnDemandServerMediaSubsession.hh
+++ b/liveMedia/include/OnDemandServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand.
// C++ header
@@ -38,7 +38,8 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
class OnDemandServerMediaSubsession: public ServerMediaSubsession {
protected: // we're a virtual base class
OnDemandServerMediaSubsession(UsageEnvironment& env, Boolean reuseFirstSource,
- portNumBits initialPortNum = 6970);
+ portNumBits initialPortNum = 6970,
+ Boolean multiplexRTCPWithRTP = False);
virtual ~OnDemandServerMediaSubsession();
protected: // redefined virtual functions
@@ -66,10 +67,13 @@ protected: // redefined virtual functions
virtual void pauseStream(unsigned clientSessionId, void* streamToken);
virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes);
virtual void seekStream(unsigned clientSessionId, void* streamToken, char*& absStart, char*& absEnd);
- virtual void nullSeekStream(unsigned clientSessionId, void* streamToken);
+ virtual void nullSeekStream(unsigned clientSessionId, void* streamToken,
+ double streamEndTime, u_int64_t& numBytes);
virtual void setStreamScale(unsigned clientSessionId, void* streamToken, float scale);
virtual float getCurrentNPT(void* streamToken);
virtual FramedSource* getStreamSource(void* streamToken);
+ virtual void getRTPSinkandRTCP(void* streamToken,
+ RTPSink const*& rtpSink, RTCPInstance const*& rtcp);
virtual void deleteStream(unsigned clientSessionId, void*& streamToken);
protected: // new virtual functions, possibly redefined by subclasses
@@ -85,6 +89,7 @@ protected: // new virtual functions, possibly redefined by subclasses
// "absEnd" should be either NULL (for no end time), or a string of the same form as "absStart".
// These strings may be modified in-place, or can be reassigned to a newly-allocated value (after delete[]ing the original).
virtual void setStreamSourceScale(FramedSource* inputSource, float scale);
+ virtual void setStreamSourceDuration(FramedSource* inputSource, double streamDuration, u_int64_t& numBytes);
virtual void closeStreamSource(FramedSource* inputSource);
protected: // new virtual functions, defined by all subclasses
@@ -95,6 +100,31 @@ protected: // new virtual functions, defined by all subclasses
unsigned char rtpPayloadTypeIfDynamic,
FramedSource* inputSource) = 0;
+protected: // new virtual functions, may be redefined by a subclass:
+ virtual Groupsock* createGroupsock(struct in_addr const& addr, Port port);
+ virtual RTCPInstance* createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
+ unsigned char const* cname, RTPSink* sink);
+
+public:
+ void multiplexRTCPWithRTP() { fMultiplexRTCPWithRTP = True; }
+ // An alternative to passing the "multiplexRTCPWithRTP" parameter as True in the constructor
+
+ void setRTCPAppPacketHandler(RTCPAppHandlerFunc* handler, void* clientData);
+ // Sets a handler to be called if a RTCP "APP" packet arrives from any future client.
+ // (Any current clients are not affected; any "APP" packets from them will continue to be
+ // handled by whatever handler existed when the client sent its first RTSP "PLAY" command.)
+ // (Call with (NULL, NULL) to remove an existing handler - for future clients only)
+
+ void sendRTCPAppPacket(u_int8_t subtype, char const* name,
+ u_int8_t* appDependentData, unsigned appDependentDataSize);
+ // Sends a custom RTCP "APP" packet to the most recent client (if "reuseFirstSource" was False),
+ // or to all current clients (if "reuseFirstSource" was True).
+ // The parameters correspond to their
+ // respective fields as described in the RTP/RTCP definition (RFC 3550).
+ // Note that only the low-order 5 bits of "subtype" are used, and only the first 4 bytes
+ // of "name" are used. (If "name" has fewer than 4 bytes, or is NULL,
+ // then the remaining bytes are '\0'.)
+
private:
void setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource,
unsigned estBitrate);
@@ -107,8 +137,11 @@ protected:
private:
Boolean fReuseFirstSource;
portNumBits fInitialPortNum;
+ Boolean fMultiplexRTCPWithRTP;
void* fLastStreamToken;
char fCNAME[100]; // for RTCP
+ RTCPAppHandlerFunc* fAppHandlerTask;
+ void* fAppHandlerClientData;
friend class StreamState;
};
@@ -147,12 +180,14 @@ public:
Groupsock* rtpGS, Groupsock* rtcpGS);
virtual ~StreamState();
- void startPlaying(Destinations* destinations,
+ void startPlaying(Destinations* destinations, unsigned clientSessionId,
TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,
ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
void* serverRequestAlternativeByteHandlerClientData);
void pause();
- void endPlaying(Destinations* destinations);
+ void sendRTCPAppPacket(u_int8_t subtype, char const* name,
+ u_int8_t* appDependentData, unsigned appDependentDataSize);
+ void endPlaying(Destinations* destinations, unsigned clientSessionId);
void reclaim();
unsigned& referenceCount() { return fReferenceCount; }
@@ -161,6 +196,7 @@ public:
Port const& serverRTCPPort() const { return fServerRTCPPort; }
RTPSink* rtpSink() const { return fRTPSink; }
+ RTCPInstance* rtcpInstance() const { return fRTCPInstance; }
float streamDuration() const { return fStreamDuration; }
diff --git a/liveMedia/include/OutputFile.hh b/liveMedia/include/OutputFile.hh
index caca04a..da1bd06 100644
--- a/liveMedia/include/OutputFile.hh
+++ b/liveMedia/include/OutputFile.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Common routines for opening/closing named output files
// C++ header
diff --git a/liveMedia/include/PassiveServerMediaSubsession.hh b/liveMedia/include/PassiveServerMediaSubsession.hh
index 76062ff..809ca5b 100644
--- a/liveMedia/include/PassiveServerMediaSubsession.hh
+++ b/liveMedia/include/PassiveServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that represents an existing
// 'RTPSink', rather than one that creates new 'RTPSink's on demand.
// C++ header
@@ -43,6 +43,8 @@ protected:
// called only by createNew();
virtual ~PassiveServerMediaSubsession();
+ virtual Boolean rtcpIsMuxed();
+
protected: // redefined virtual functions
virtual char const* sdpLines();
virtual void getStreamParameters(unsigned clientSessionId,
@@ -66,6 +68,8 @@ protected: // redefined virtual functions
ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
void* serverRequestAlternativeByteHandlerClientData);
virtual float getCurrentNPT(void* streamToken);
+ virtual void getRTPSinkandRTCP(void* streamToken,
+ RTPSink const*& rtpSink, RTCPInstance const*& rtcp);
virtual void deleteStream(unsigned clientSessionId, void*& streamToken);
protected:
diff --git a/liveMedia/include/ProxyServerMediaSession.hh b/liveMedia/include/ProxyServerMediaSession.hh
index 9069957..07c3002 100644
--- a/liveMedia/include/ProxyServerMediaSession.hh
+++ b/liveMedia/include/ProxyServerMediaSession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A subclass of "ServerMediaSession" that can be used to create a (unicast) RTSP servers that acts as a 'proxy' for
// another (unicast or multicast) RTSP/RTP stream.
// C++ header
@@ -31,6 +31,9 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#ifndef _RTSP_CLIENT_HH
#include "RTSPClient.hh"
#endif
+#ifndef _MEDIA_TRANSCODING_TABLE_HH
+#include "MediaTranscodingTable.hh"
+#endif
// A subclass of "RTSPClient", used to refer to the particular "ProxyServerMediaSession" object being used.
// It is used only within the implementation of "ProxyServerMediaSession", but is defined here, in case developers wish to
@@ -45,7 +48,8 @@ public:
void continueAfterDESCRIBE(char const* sdpDescription);
void continueAfterLivenessCommand(int resultCode, Boolean serverSupportsGetParameter);
- void continueAfterSETUP();
+ void continueAfterSETUP(int resultCode);
+ void continueAfterPLAY(int resultCode);
private:
void reset();
@@ -71,7 +75,7 @@ private:
class ProxyServerMediaSubsession *fSetupQueueHead, *fSetupQueueTail;
unsigned fNumSetupsDone;
unsigned fNextDESCRIBEDelay; // in seconds
- Boolean fServerSupportsGetParameter, fLastCommandWasPLAY;
+ Boolean fServerSupportsGetParameter, fLastCommandWasPLAY, fResetOnNextLivenessTest;
TaskToken fLivenessCommandTask, fDESCRIBECommandTask, fSubsessionTimerTask;
};
@@ -92,14 +96,15 @@ defaultCreateNewProxyRTSPClientFunc(ProxyServerMediaSession& ourServerMediaSessi
class ProxyServerMediaSession: public ServerMediaSession {
public:
static ProxyServerMediaSession* createNew(UsageEnvironment& env,
- RTSPServer* ourRTSPServer, // Note: We can be used by just one "RTSPServer"
+ GenericMediaServer* ourMediaServer, // Note: We can be used by just one server
char const* inputStreamURL, // the "rtsp://" URL of the stream we'll be proxying
char const* streamName = NULL,
char const* username = NULL, char const* password = NULL,
portNumBits tunnelOverHTTPPortNum = 0,
// for streaming the *proxied* (i.e., back-end) stream
int verbosityLevel = 0,
- int socketNumToServer = -1);
+ int socketNumToServer = -1,
+ MediaTranscodingTable* transcodingTable = NULL);
// Hack: "tunnelOverHTTPPortNum" == 0xFFFF (i.e., all-ones) means: Stream RTP/RTCP-over-TCP, but *not* using HTTP
// "verbosityLevel" == 1 means display basic proxy setup info; "verbosityLevel" == 2 means display RTSP client protocol also.
// If "socketNumToServer" is >= 0, then it is the socket number of an already-existing TCP connection to the server.
@@ -116,13 +121,16 @@ public:
// This can be used - along with "describeCompletdFlag" - to check whether the back-end "DESCRIBE" completed *successfully*.
protected:
- ProxyServerMediaSession(UsageEnvironment& env, RTSPServer* ourRTSPServer,
+ ProxyServerMediaSession(UsageEnvironment& env, GenericMediaServer* ourMediaServer,
char const* inputStreamURL, char const* streamName,
char const* username, char const* password,
portNumBits tunnelOverHTTPPortNum, int verbosityLevel,
int socketNumToServer,
+ MediaTranscodingTable* transcodingTable,
createNewProxyRTSPClientFunc* ourCreateNewProxyRTSPClientFunc
- = defaultCreateNewProxyRTSPClientFunc);
+ = defaultCreateNewProxyRTSPClientFunc,
+ portNumBits initialPortNum = 6970,
+ Boolean multiplexRTCPWithRTP = False);
// If you subclass "ProxyRTSPClient", then you will also need to define your own function
// - with signature "createNewProxyRTSPClientFunc" (see above) - that creates a new object
@@ -131,8 +139,14 @@ protected:
// constructor by passing your new function as the "ourCreateNewProxyRTSPClientFunc"
// parameter.
+ // Subclasses may redefine the following functions, if they want "ProxyServerSubsession"s
+ // to create subclassed "Groupsock" and/or "RTCPInstance" objects:
+ virtual Groupsock* createGroupsock(struct in_addr const& addr, Port port);
+ virtual RTCPInstance* createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */
+ unsigned char const* cname, RTPSink* sink);
+
protected:
- RTSPServer* fOurRTSPServer;
+ GenericMediaServer* fOurMediaServer;
ProxyRTSPClient* fProxyRTSPClient;
MediaSession* fClientMediaSession;
@@ -146,6 +160,9 @@ private:
int fVerbosityLevel;
class PresentationTimeSessionNormalizer* fPresentationTimeSessionNormalizer;
createNewProxyRTSPClientFunc* fCreateNewProxyRTSPClientFunc;
+ MediaTranscodingTable* fTranscodingTable;
+ portNumBits fInitialPortNum;
+ Boolean fMultiplexRTCPWithRTP;
};
diff --git a/liveMedia/include/QCELPAudioRTPSource.hh b/liveMedia/include/QCELPAudioRTPSource.hh
index 9cd000a..e72ae6e 100644
--- a/liveMedia/include/QCELPAudioRTPSource.hh
+++ b/liveMedia/include/QCELPAudioRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Qualcomm "PureVoice" (aka. "QCELP") Audio RTP Sources
// C++ header
diff --git a/liveMedia/include/QuickTimeFileSink.hh b/liveMedia/include/QuickTimeFileSink.hh
index 95c672e..0bea22f 100644
--- a/liveMedia/include/QuickTimeFileSink.hh
+++ b/liveMedia/include/QuickTimeFileSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A sink that generates a QuickTime file from a composite media session
// C++ header
@@ -45,7 +45,7 @@ public:
unsigned numActiveSubsessions() const { return fNumSubsessions; }
-private:
+protected:
QuickTimeFileSink(UsageEnvironment& env, MediaSession& inputSession,
char const* outputFileName, unsigned bufferSize,
unsigned short movieWidth, unsigned short movieHeight,
@@ -55,6 +55,10 @@ private:
// called only by createNew()
virtual ~QuickTimeFileSink();
+ virtual void noteRecordedFrame(MediaSubsession& inputSubsession,
+ unsigned packetDataSize, struct timeval const& presentationTime);
+
+private:
Boolean continuePlaying();
static void afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
diff --git a/liveMedia/include/QuickTimeGenericRTPSource.hh b/liveMedia/include/QuickTimeGenericRTPSource.hh
index 1d140e0..12d3b82 100644
--- a/liveMedia/include/QuickTimeGenericRTPSource.hh
+++ b/liveMedia/include/QuickTimeGenericRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP Sources containing generic QuickTime stream data, as defined in
// <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
// C++ header
diff --git a/liveMedia/include/RTCP.hh b/liveMedia/include/RTCP.hh
index 34bc323..a1069ed 100644
--- a/liveMedia/include/RTCP.hh
+++ b/liveMedia/include/RTCP.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTCP
// C++ header
@@ -39,6 +39,10 @@ private:
unsigned char fData[2 + 0xFF]; // first 2 bytes are tag and length
};
+typedef void RTCPAppHandlerFunc(void* clientData,
+ u_int8_t subtype, u_int32_t nameBytes/*big-endian order*/,
+ u_int8_t* appDependentData, unsigned appDependentDataSize);
+
class RTCPMemberDatabase; // forward
class RTCPInstance: public Medium {
@@ -47,7 +51,7 @@ public:
unsigned totSessionBW, /* in kbps */
unsigned char const* cname,
RTPSink* sink,
- RTPSource const* source,
+ RTPSource* source,
Boolean isSSMSource = False);
static Boolean lookupByName(UsageEnvironment& env, char const* instanceName,
@@ -71,16 +75,26 @@ public:
// (To remove an existing "BYE" handler, call "setByeHandler()" again, with a "handlerTask" of NULL.)
void setSRHandler(TaskFunc* handlerTask, void* clientData);
void setRRHandler(TaskFunc* handlerTask, void* clientData);
- // Assigns a handler routine to be called if a "SR" or "RR"
+ // Assigns a handler routine to be called if a "SR" or "RR" packet
// (respectively) arrives. Unlike "setByeHandler()", the handler will
// be called once for each incoming "SR" or "RR". (To turn off handling,
- // call the function again with "handlerTask" (and "clientData") as NULL.
+ // call the function again with "handlerTask" (and "clientData") as NULL.)
void setSpecificRRHandler(netAddressBits fromAddress, Port fromPort,
TaskFunc* handlerTask, void* clientData);
// Like "setRRHandler()", but applies only to "RR" packets that come from
// a specific source address and port. (Note that if both a specific
// and a general "RR" handler function is set, then both will be called.)
void unsetSpecificRRHandler(netAddressBits fromAddress, Port fromPort); // equivalent to setSpecificRRHandler(..., NULL, NULL);
+ void setAppHandler(RTCPAppHandlerFunc* handlerTask, void* clientData);
+ // Assigns a handler routine to be called whenever an "APP" packet arrives. (To turn off
+ // handling, call the function again with "handlerTask" (and "clientData") as NULL.)
+ void sendAppPacket(u_int8_t subtype, char const* name,
+ u_int8_t* appDependentData, unsigned appDependentDataSize);
+ // Sends a custom RTCP "APP" packet to the peer(s). The parameters correspond to their
+ // respective fields as described in the RTP/RTCP definition (RFC 3550).
+ // Note that only the low-order 5 bits of "subtype" are used, and only the first 4 bytes
+ // of "name" are used. (If "name" has fewer than 4 bytes, or is NULL,
+ // then the remaining bytes are '\0'.)
Groupsock* RTCPgs() const { return fRTCPInterface.gs(); }
@@ -97,14 +111,22 @@ public:
handlerClientData);
}
+ void injectReport(u_int8_t const* packet, unsigned packetSize, struct sockaddr_in const& fromAddress);
+ // Allows an outside party to inject an RTCP report (from other than the network interface)
+
protected:
RTCPInstance(UsageEnvironment& env, Groupsock* RTPgs, unsigned totSessionBW,
unsigned char const* cname,
- RTPSink* sink, RTPSource const* source,
+ RTPSink* sink, RTPSource* source,
Boolean isSSMSource);
// called only by createNew()
virtual ~RTCPInstance();
+ virtual void noteArrivingRR(struct sockaddr_in const& fromAddressAndPort,
+ int tcpSocketNum, unsigned char tcpStreamChannelId);
+
+ void incomingReportHandler1();
+
private:
// redefined virtual functions:
virtual Boolean isRTCPInstance() const;
@@ -126,17 +148,18 @@ private:
void onExpire1();
static void incomingReportHandler(RTCPInstance* instance, int /*mask*/);
- void incomingReportHandler1();
+ void processIncomingReport(unsigned packetSize, struct sockaddr_in const& fromAddressAndPort,
+ int tcpSocketNum, unsigned char tcpStreamChannelId);
void onReceive(int typeOfPacket, int totPacketSize, u_int32_t ssrc);
private:
- unsigned char* fInBuf;
+ u_int8_t* fInBuf;
unsigned fNumBytesAlreadyRead;
OutPacketBuffer* fOutBuf;
RTPInterface fRTCPInterface;
unsigned fTotSessionBW;
RTPSink* fSink;
- RTPSource const* fSource;
+ RTPSource* fSource;
Boolean fIsSSMSource;
SDESItem fCNAME;
@@ -165,6 +188,8 @@ private:
TaskFunc* fRRHandlerTask;
void* fRRHandlerClientData;
AddressPortLookupTable* fSpecificRRHandlerTable;
+ RTCPAppHandlerFunc* fAppHandlerTask;
+ void* fAppHandlerClientData;
public: // because this stuff is used by an external "C" function
void schedule(double nextTime);
@@ -186,6 +211,13 @@ const unsigned char RTCP_PT_RR = 201;
const unsigned char RTCP_PT_SDES = 202;
const unsigned char RTCP_PT_BYE = 203;
const unsigned char RTCP_PT_APP = 204;
+const unsigned char RTCP_PT_RTPFB = 205; // Generic RTP Feedback [RFC4585]
+const unsigned char RTCP_PT_PSFB = 206; // Payload-specific [RFC4585]
+const unsigned char RTCP_PT_XR = 207; // extended report [RFC3611]
+const unsigned char RTCP_PT_AVB = 208; // AVB RTCP packet ["Standard for Layer 3 Transport Protocol for Time Sensitive Applications in Local Area Networks." Work in progress.]
+const unsigned char RTCP_PT_RSI = 209; // Receiver Summary Information [RFC5760]
+const unsigned char RTCP_PT_TOKEN = 210; // Port Mapping [RFC6284]
+const unsigned char RTCP_PT_IDMS = 211; // IDMS Settings [RFC7272]
// SDES tags:
const unsigned char RTCP_SDES_END = 0;
diff --git a/liveMedia/include/RTPInterface.hh b/liveMedia/include/RTPInterface.hh
index 4df41e7..f50ac82 100644
--- a/liveMedia/include/RTPInterface.hh
+++ b/liveMedia/include/RTPInterface.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// An abstraction of a network interface used for RTP (or RTCP).
// (This allows the RTP-over-TCP hack (RFC 2326, section 10.12) to
// be implemented transparently.)
@@ -70,7 +70,17 @@ public:
void startNetworkReading(TaskScheduler::BackgroundHandlerProc*
handlerProc);
Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize,
- unsigned& bytesRead, struct sockaddr_in& fromAddress, Boolean& packetReadWasIncomplete);
+ // out parameters:
+ unsigned& bytesRead, struct sockaddr_in& fromAddress,
+ int& tcpSocketNum, unsigned char& tcpStreamChannelId,
+ Boolean& packetReadWasIncomplete);
+ // Note: If "tcpSocketNum" < 0, then the packet was received over UDP, and "tcpStreamChannelId"
+ // is undefined (and irrelevant).
+
+
+ // Otherwise (if "tcpSocketNum" >= 0), the packet was received (interleaved) over TCP, and
+ // "tcpStreamChannelId" will return the channel id.
+
void stopNetworkReading();
UsageEnvironment& envir() const { return fOwner->envir(); }
@@ -81,9 +91,10 @@ public:
fAuxReadHandlerClientData = handlerClientData;
}
- // A hack for supporting handlers for RTCP packets arriving interleaved over TCP:
- int nextTCPReadStreamSocketNum() const { return fNextTCPReadStreamSocketNum; }
- unsigned char nextTCPReadStreamChannelId() const { return fNextTCPReadStreamChannelId; }
+ void forgetOurGroupsock() { fGS = NULL; }
+ // This may be called - *only immediately prior* to deleting this - to prevent our destructor
+ // from turning off background reading on the 'groupsock'. (This is in case the 'groupsock'
+ // is also being read from elsewhere.)
private:
// Helper functions for sending a RTP or RTCP packet over a TCP connection:
diff --git a/liveMedia/include/RTPSink.hh b/liveMedia/include/RTPSink.hh
index ca27436..63fc2c5 100644
--- a/liveMedia/include/RTPSink.hh
+++ b/liveMedia/include/RTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP Sinks
// C++ header
@@ -84,6 +84,7 @@ public:
void removeStreamSocket(int sockNum, unsigned char streamChannelId) {
fRTPInterface.removeStreamSocket(sockNum, streamChannelId);
}
+ unsigned& estimatedBitrate() { return fEstimatedBitrate; } // kbps; usually 0 (i.e., unset)
protected:
RTPSink(UsageEnvironment& env,
@@ -124,6 +125,7 @@ private:
char const* fRTPPayloadFormatName;
unsigned fNumChannels;
struct timeval fCreationTime;
+ unsigned fEstimatedBitrate; // set on creation if known; otherwise 0
RTPTransmissionStatsDB* fTransmissionStatsDB;
};
@@ -168,7 +170,7 @@ private:
private:
friend class Iterator;
unsigned fNumReceivers;
- RTPSink& fOurRTPSink;
+ RTPSink& fOurRTPSink;
HashTable* fTable;
};
@@ -185,8 +187,8 @@ public:
unsigned roundTripDelay() const;
// The round-trip delay (in units of 1/65536 seconds) computed from
// the most recently-received RTCP RR packet.
- struct timeval timeCreated() const {return fTimeCreated;}
- struct timeval lastTimeReceived() const {return fTimeReceived;}
+ struct timeval const& timeCreated() const {return fTimeCreated;}
+ struct timeval const& lastTimeReceived() const {return fTimeReceived;}
void getTotalOctetCount(u_int32_t& hi, u_int32_t& lo);
void getTotalPacketCount(u_int32_t& hi, u_int32_t& lo);
diff --git a/liveMedia/include/RTPSource.hh b/liveMedia/include/RTPSource.hh
index 98820a3..f433cf6 100644
--- a/liveMedia/include/RTPSource.hh
+++ b/liveMedia/include/RTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP Sources
// C++ header
@@ -49,6 +49,10 @@ public:
u_int32_t SSRC() const { return fSSRC; }
// Note: This is *our* SSRC, not the SSRC in incoming RTP packets.
// later need a means of changing the SSRC if there's a collision #####
+ void registerForMultiplexedRTCPPackets(class RTCPInstance* rtcpInstance) {
+ fRTCPInstanceForMultiplexedRTCPPackets = rtcpInstance;
+ }
+ void deregisterForMultiplexedRTCPPackets() { registerForMultiplexedRTCPPackets(NULL); }
unsigned timestampFrequency() const {return fTimestampFrequency;}
@@ -93,6 +97,7 @@ protected:
Boolean fCurPacketMarkerBit;
Boolean fCurPacketHasBeenSynchronizedUsingRTCP;
u_int32_t fLastReceivedSSRC;
+ class RTCPInstance* fRTCPInstanceForMultiplexedRTCPPackets;
private:
// redefined virtual functions:
diff --git a/liveMedia/include/RTSPClient.hh b/liveMedia/include/RTSPClient.hh
index 52931ba..725f30c 100644
--- a/liveMedia/include/RTSPClient.hh
+++ b/liveMedia/include/RTSPClient.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTSP client - for a single "rtsp://" URL
// C++ header
@@ -153,6 +153,10 @@ public:
// Our implementation automatically does this just prior to sending each "PLAY" command;
// You should not call these functions yourself unless you know what you're doing.
+ void setSpeed(MediaSession& session, float speed = 1.0f);
+ // Set (recorded) media download speed to given value to support faster download using 'Speed:'
+ // option on 'PLAY' command.
+
Boolean changeResponseHandler(unsigned cseq, responseHandler* newResponseHandler);
// Changes the response handler for the previously-performed command (whose operation returned "cseq").
// (To turn off any response handling for the command, use a "newResponseHandler" value of NULL. This might be done as part
@@ -173,6 +177,10 @@ public:
void setUserAgentString(char const* userAgentName);
// sets an alternative string to be used in RTSP "User-Agent:" headers
+ void disallowBasicAuthentication() { fAllowBasicAuthentication = False; }
+ // call this if you don't want the server to request 'Basic' authentication
+ // (which would cause the client to send usernames and passwords over the net).
+
unsigned sessionTimeoutParameter() const { return fSessionTimeoutParameter; }
char const* url() const { return fBaseURL; }
@@ -262,6 +270,7 @@ private:
int openConnection(); // -1: failure; 0: pending; 1: success
int connectToServer(int socketNum, portNumBits remotePortNum); // used to implement "openConnection()"; result values are the same
char* createAuthenticatorString(char const* cmd, char const* url);
+ char* createBlocksizeString(Boolean streamUsingTCP);
void handleRequestError(RequestRecord* request);
Boolean parseResponseCode(char const* line, unsigned& responseCode, char const*& responseString);
void handleIncomingRequest();
@@ -270,13 +279,15 @@ private:
char*& serverAddressStr, portNumBits& serverPortNum,
unsigned char& rtpChannelId, unsigned char& rtcpChannelId);
Boolean parseScaleParam(char const* paramStr, float& scale);
+ Boolean parseSpeedParam(char const* paramStr, float& speed);
Boolean parseRTPInfoParams(char const*& paramStr, u_int16_t& seqNum, u_int32_t& timestamp);
Boolean handleSETUPResponse(MediaSubsession& subsession, char const* sessionParamsStr, char const* transportParamsStr,
Boolean streamUsingTCP);
Boolean handlePLAYResponse(MediaSession& session, MediaSubsession& subsession,
- char const* scaleParamsStr, char const* rangeParamsStr, char const* rtpInfoParamsStr);
+ char const* scaleParamsStr, const char* speedParamsStr,
+ char const* rangeParamsStr, char const* rtpInfoParamsStr);
Boolean handleTEARDOWNResponse(MediaSession& session, MediaSubsession& subsession);
- Boolean handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString);
+ Boolean handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString, char* resultValueStringEnd);
Boolean handleAuthenticationFailure(char const* wwwAuthenticateParamsStr);
Boolean resendCommand(RequestRecord* request);
char const* sessionURL(MediaSession const& session) const;
@@ -302,10 +313,16 @@ private:
void incomingDataHandler1();
void handleResponseBytes(int newBytesRead);
+public:
+ u_int16_t desiredMaxIncomingPacketSize;
+ // If set to a value >0, then a "Blocksize:" header with this value (minus an allowance for
+ // IP, UDP, and RTP headers) will be sent with each "SETUP" request.
+
protected:
int fVerbosityLevel;
unsigned fCSeq; // sequence number, used in consecutive requests
Authenticator fCurrentAuthenticator;
+ Boolean fAllowBasicAuthentication;
netAddressBits fServerAddress;
private:
@@ -341,7 +358,7 @@ public:
Port ourPort = 0, UserAuthenticationDatabase* authDatabase = NULL,
int verbosityLevel = 0, char const* applicationName = NULL);
// If ourPort.num() == 0, we'll choose the port number ourself. (Use the following function to get it.)
- portNumBits serverPortNum() const { return ntohs(fRTSPServerPort.num()); }
+ portNumBits serverPortNum() const { return ntohs(fServerPort.num()); }
protected:
HandlerServerForREGISTERCommand(UsageEnvironment& env, onRTSPClientCreationFunc* creationFunc, int ourSocket, Port ourPort,
diff --git a/liveMedia/include/RTSPCommon.hh b/liveMedia/include/RTSPCommon.hh
index 1a3fc85..4979043 100644
--- a/liveMedia/include/RTSPCommon.hh
+++ b/liveMedia/include/RTSPCommon.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Common routines used by both RTSP clients and servers
// C++ header
@@ -51,8 +51,8 @@ Boolean parseRTSPRequestString(char const *reqStr, unsigned reqStrSize,
unsigned resultSessionIdMaxSize,
unsigned& contentLength);
-Boolean parseRangeParam(char const* paramStr, double& rangeStart, double& rangeEnd, char*& absStartTime, char*& absEndTime);
-Boolean parseRangeHeader(char const* buf, double& rangeStart, double& rangeEnd, char*& absStartTime, char*& absEndTime);
+Boolean parseRangeParam(char const* paramStr, double& rangeStart, double& rangeEnd, char*& absStartTime, char*& absEndTime, Boolean& startTimeIsNow);
+Boolean parseRangeHeader(char const* buf, double& rangeStart, double& rangeEnd, char*& absStartTime, char*& absEndTime, Boolean& startTimeIsNow);
Boolean parseScaleHeader(char const* buf, float& scale);
@@ -62,6 +62,4 @@ Boolean RTSPOptionIsSupported(char const* commandName, char const* optionsRespon
char const* dateHeader(); // A "Date:" header that can be used in a RTSP (or HTTP) response
-void ignoreSigPipeOnSocket(int socketNum);
-
#endif
diff --git a/liveMedia/include/RTSPRegisterSender.hh b/liveMedia/include/RTSPRegisterSender.hh
index ca49c25..7f99e8d 100644
--- a/liveMedia/include/RTSPRegisterSender.hh
+++ b/liveMedia/include/RTSPRegisterSender.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A special object which, when created, sends a custom RTSP "REGISTER" command to a specified client.
// C++ header
diff --git a/liveMedia/include/RTSPServer.hh b/liveMedia/include/RTSPServer.hh
index bf68cb6..c2ddb99 100644
--- a/liveMedia/include/RTSPServer.hh
+++ b/liveMedia/include/RTSPServer.hh
@@ -14,94 +14,35 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A RTSP server
// C++ header
#ifndef _RTSP_SERVER_HH
#define _RTSP_SERVER_HH
-#ifndef _SERVER_MEDIA_SESSION_HH
-#include "ServerMediaSession.hh"
-#endif
-#ifndef _NET_ADDRESS_HH
-#include <NetAddress.hh>
+#ifndef _GENERIC_MEDIA_SERVER_HH
+#include "GenericMediaServer.hh"
#endif
#ifndef _DIGEST_AUTHENTICATION_HH
#include "DigestAuthentication.hh"
#endif
-// A data structure used for optional user/password authentication:
-
-class UserAuthenticationDatabase {
-public:
- UserAuthenticationDatabase(char const* realm = NULL,
- Boolean passwordsAreMD5 = False);
- // If "passwordsAreMD5" is True, then each password stored into, or removed from,
- // the database is actually the value computed
- // by md5(<username>:<realm>:<actual-password>)
- virtual ~UserAuthenticationDatabase();
-
- virtual void addUserRecord(char const* username, char const* password);
- virtual void removeUserRecord(char const* username);
-
- virtual char const* lookupPassword(char const* username);
- // returns NULL if the user name was not present
-
- char const* realm() { return fRealm; }
- Boolean passwordsAreMD5() { return fPasswordsAreMD5; }
-
-protected:
- HashTable* fTable;
- char* fRealm;
- Boolean fPasswordsAreMD5;
-};
-
-#ifndef RTSP_BUFFER_SIZE
-#define RTSP_BUFFER_SIZE 10000 // for incoming requests, and outgoing responses
-#endif
-
-class RTSPServer: public Medium {
+class RTSPServer: public GenericMediaServer {
public:
static RTSPServer* createNew(UsageEnvironment& env, Port ourPort = 554,
UserAuthenticationDatabase* authDatabase = NULL,
- unsigned reclamationTestSeconds = 65);
+ unsigned reclamationSeconds = 65);
// If ourPort.num() == 0, we'll choose the port number
// Note: The caller is responsible for reclaiming "authDatabase"
- // If "reclamationTestSeconds" > 0, then the "RTSPClientSession" state for
+ // If "reclamationSeconds" > 0, then the "RTSPClientSession" state for
// each client will get reclaimed (and the corresponding RTP stream(s)
// torn down) if no RTSP commands - or RTCP "RR" packets - from the
- // client are received in at least "reclamationTestSeconds" seconds.
+ // client are received in at least "reclamationSeconds" seconds.
static Boolean lookupByName(UsageEnvironment& env, char const* name,
RTSPServer*& resultServer);
- void addServerMediaSession(ServerMediaSession* serverMediaSession);
-
- virtual ServerMediaSession* lookupServerMediaSession(char const* streamName);
-
- void removeServerMediaSession(ServerMediaSession* serverMediaSession);
- // Removes the "ServerMediaSession" object from our lookup table, so it will no longer be accessible by new RTSP clients.
- // (However, any *existing* RTSP client sessions that use this "ServerMediaSession" object will continue streaming.
- // The "ServerMediaSession" object will not get deleted until all of these RTSP client sessions have closed.)
- // (To both delete the "ServerMediaSession" object *and* close all RTSP client sessions that use it,
- // call "deleteServerMediaSession(serverMediaSession)" instead.)
- void removeServerMediaSession(char const* streamName);
- // ditto
-
- void closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession);
- // Closes (from the server) all RTSP client sessions that are currently using this "ServerMediaSession" object.
- // Note, however, that the "ServerMediaSession" object remains accessible by new RTSP clients.
- void closeAllClientSessionsForServerMediaSession(char const* streamName);
- // ditto
-
- void deleteServerMediaSession(ServerMediaSession* serverMediaSession);
- // Equivalent to:
- // "closeAllClientSessionsForServerMediaSession(serverMediaSession); removeServerMediaSession(serverMediaSession);"
- void deleteServerMediaSession(char const* streamName);
- // Equivalent to:
- // "closeAllClientSessionsForServerMediaSession(streamName); removeServerMediaSession(streamName);
-
typedef void (responseHandlerForREGISTER)(RTSPServer* rtspServer, unsigned requestId, int resultCode, char* resultString);
unsigned registerStream(ServerMediaSession* serverMediaSession,
char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum,
@@ -136,6 +77,10 @@ public:
// Changes the server's authentication database to "newDB", returning a pointer to the old database (if there was one).
// "newDB" may be NULL (you can use this to disable authentication at runtime, if desired).
+ void disableStreamingRTPOverTCP() {
+ fAllowStreamingRTPOverTCP = False;
+ }
+
Boolean setUpTunnelingOverHTTP(Port httpPort);
// (Attempts to) enable RTSP-over-HTTP tunneling on the specified port.
// Returns True iff the specified port can be used in this way (i.e., it's not already being used for a separate HTTP server).
@@ -146,12 +91,10 @@ protected:
RTSPServer(UsageEnvironment& env,
int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase,
- unsigned reclamationTestSeconds);
+ unsigned reclamationSeconds);
// called only by createNew();
virtual ~RTSPServer();
- static int setUpOurSocket(UsageEnvironment& env, Port& ourPort);
-
virtual char const* allowedCommandNames(); // used to implement "RTSPClientConnection::handleCmd_OPTIONS()"
virtual Boolean weImplementREGISTER(char const* proxyURLSuffix, char*& responseStr);
// used to implement "RTSPClientConnection::handleCmd_REGISTER()"
@@ -175,12 +118,10 @@ private: // redefined virtual functions
virtual Boolean isRTSPServer() const;
public: // should be protected, but some old compilers complain otherwise
- class RTSPClientSession; // forward
// The state of a TCP connection used by a RTSP client:
- class RTSPClientConnection {
+ class RTSPClientSession; // forward
+ class RTSPClientConnection: public GenericMediaServer::ClientConnection {
public:
- RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr);
- virtual ~RTSPClientConnection();
// A data structure that's used to implement the "REGISTER" command:
class ParamsForREGISTER {
public:
@@ -195,8 +136,16 @@ public: // should be protected, but some old compilers complain otherwise
Boolean fReuseConnection, fDeliverViaTCP;
char* fProxyURLSuffix;
};
+ protected: // redefined virtual functions:
+ virtual void handleRequestBytes(int newBytesRead);
+
protected:
+ RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr);
+ virtual ~RTSPClientConnection();
+
+ friend class RTSPServer;
friend class RTSPClientSession;
+
// Make the handler functions for each command virtual, to allow subclasses to reimplement them, if necessary:
virtual void handleCmd_OPTIONS();
// You probably won't need to subclass/reimplement this function; reimplement "RTSPServer::allowedCommandNames()" instead.
@@ -224,14 +173,10 @@ public: // should be protected, but some old compilers complain otherwise
virtual Boolean handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize);
virtual void handleHTTPCmd_StreamingGET(char const* urlSuffix, char const* fullRequestStr);
protected:
- UsageEnvironment& envir() { return fOurServer.envir(); }
void resetRequestBuffer();
- void closeSockets();
- static void incomingRequestHandler(void*, int /*mask*/);
- void incomingRequestHandler1();
+ void closeSocketsRTSP();
static void handleAlternativeRequestByte(void*, u_int8_t requestByte);
void handleAlternativeRequestByte1(u_int8_t requestByte);
- void handleRequestBytes(int newBytesRead);
Boolean authenticationOK(char const* cmdName, char const* urlSuffix, char const* fullRequestStr);
void changeClientInputSocket(int newSocketNum, unsigned char const* extraData, unsigned extraDataSize);
// used to implement RTSP-over-HTTP tunneling
@@ -244,14 +189,11 @@ public: // should be protected, but some old compilers complain otherwise
void setRTSPResponse(char const* responseStr, char const* contentStr);
void setRTSPResponse(char const* responseStr, u_int32_t sessionId, char const* contentStr);
- RTSPServer& fOurServer;
+ RTSPServer& fOurRTSPServer; // same as ::fOurServer
+ int& fClientInputSocket; // aliased to ::fOurSocket
+ int fClientOutputSocket;
Boolean fIsActive;
- int fClientInputSocket, fClientOutputSocket;
- struct sockaddr_in fClientAddr;
- unsigned char fRequestBuffer[RTSP_BUFFER_SIZE];
- unsigned fRequestBytesAlreadySeen, fRequestBufferBytesLeft;
unsigned char* fLastCRLF;
- unsigned char fResponseBuffer[RTSP_BUFFER_SIZE];
unsigned fRecursionCount;
char const* fCurrentCSeq;
Authenticator fCurrentAuthenticator; // used if access control is needed
@@ -260,11 +202,11 @@ public: // should be protected, but some old compilers complain otherwise
};
// The state of an individual client session (using one or more sequential TCP connections) handled by a RTSP server:
- class RTSPClientSession {
- public:
+ class RTSPClientSession: public GenericMediaServer::ClientSession {
+ protected:
RTSPClientSession(RTSPServer& ourServer, u_int32_t sessionId);
virtual ~RTSPClientSession();
- protected:
+
friend class RTSPServer;
friend class RTSPClientConnection;
// Make the handler functions for each command virtual, to allow subclasses to redefine them:
@@ -285,12 +227,9 @@ public: // should be protected, but some old compilers complain otherwise
virtual void handleCmd_SET_PARAMETER(RTSPClientConnection* ourClientConnection,
ServerMediaSubsession* subsession, char const* fullRequestStr);
protected:
- UsageEnvironment& envir() { return fOurServer.envir(); }
+ void deleteStreamByTrack(unsigned trackNum);
void reclaimStreamStates();
Boolean isMulticast() const { return fIsMulticast; }
- void noteLiveness();
- static void noteClientLiveness(RTSPClientSession* clientSession);
- static void livenessTimeoutTask(RTSPClientSession* clientSession);
// Shortcuts for setting up a RTSP response (prior to sending it):
void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr) { ourClientConnection->setRTSPResponse(responseStr); }
@@ -299,70 +238,50 @@ public: // should be protected, but some old compilers complain otherwise
void setRTSPResponse(RTSPClientConnection* ourClientConnection, char const* responseStr, u_int32_t sessionId, char const* contentStr) { ourClientConnection->setRTSPResponse(responseStr, sessionId, contentStr); }
protected:
- RTSPServer& fOurServer;
- u_int32_t fOurSessionId;
- ServerMediaSession* fOurServerMediaSession;
+ RTSPServer& fOurRTSPServer; // same as ::fOurServer
Boolean fIsMulticast, fStreamAfterSETUP;
unsigned char fTCPStreamIdCount; // used for (optional) RTP/TCP
Boolean usesTCPTransport() const { return fTCPStreamIdCount > 0; }
- TaskToken fLivenessCheckTask;
unsigned fNumStreamStates;
struct streamState {
ServerMediaSubsession* subsession;
+ int tcpSocketNum;
void* streamToken;
} * fStreamStates;
};
-protected:
+protected: // redefined virtual functions
// If you subclass "RTSPClientConnection", then you must also redefine this virtual function in order
// to create new objects of your subclass:
- virtual RTSPClientConnection*
- createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr);
+ virtual ClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr);
+protected:
// If you subclass "RTSPClientSession", then you must also redefine this virtual function in order
// to create new objects of your subclass:
- virtual RTSPClientSession*
- createNewClientSession(u_int32_t sessionId);
-
- // An iterator over our "ServerMediaSession" objects:
- class ServerMediaSessionIterator {
- public:
- ServerMediaSessionIterator(RTSPServer& server);
- virtual ~ServerMediaSessionIterator();
- ServerMediaSession* next();
- private:
- HashTable::Iterator* fOurIterator;
- };
+ virtual ClientSession* createNewClientSession(u_int32_t sessionId);
private:
- static void incomingConnectionHandlerRTSP(void*, int /*mask*/);
- void incomingConnectionHandlerRTSP1();
-
static void incomingConnectionHandlerHTTP(void*, int /*mask*/);
- void incomingConnectionHandlerHTTP1();
+ void incomingConnectionHandlerHTTP();
- void incomingConnectionHandler(int serverSocket);
-
-protected:
- Port fRTSPServerPort;
+ void noteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum);
+ void unnoteTCPStreamingOnSocket(int socketNum, RTSPClientSession* clientSession, unsigned trackNum);
+ void stopTCPStreamingOnSocket(int socketNum);
private:
friend class RTSPClientConnection;
friend class RTSPClientSession;
- friend class ServerMediaSessionIterator;
friend class RegisterRequestRecord;
- int fRTSPServerSocket;
int fHTTPServerSocket; // for optional RTSP-over-HTTP tunneling
Port fHTTPServerPort; // ditto
- HashTable* fServerMediaSessions; // maps 'stream name' strings to "ServerMediaSession" objects
- HashTable* fClientConnections; // the "ClientConnection" objects that we're using
HashTable* fClientConnectionsForHTTPTunneling; // maps client-supplied 'session cookie' strings to "RTSPClientConnection"s
// (used only for optional RTSP-over-HTTP tunneling)
- HashTable* fClientSessions; // maps 'session id' strings to "RTSPClientSession" objects
+ HashTable* fTCPStreamingDatabase;
+ // maps TCP socket numbers to ids of sessions that are streaming over it (RTP/RTCP-over-TCP)
HashTable* fPendingRegisterRequests;
unsigned fRegisterRequestCounter;
UserAuthenticationDatabase* fAuthDB;
- unsigned fReclamationTestSeconds;
+ Boolean fAllowStreamingRTPOverTCP; // by default, True
};
@@ -373,14 +292,14 @@ public:
static RTSPServerWithREGISTERProxying* createNew(UsageEnvironment& env, Port ourPort = 554,
UserAuthenticationDatabase* authDatabase = NULL,
UserAuthenticationDatabase* authDatabaseForREGISTER = NULL,
- unsigned reclamationTestSeconds = 65,
+ unsigned reclamationSeconds = 65,
Boolean streamRTPOverTCP = False,
int verbosityLevelForProxying = 0);
protected:
RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER,
- unsigned reclamationTestSeconds,
+ unsigned reclamationSeconds,
Boolean streamRTPOverTCP, int verbosityLevelForProxying);
// called only by createNew();
virtual ~RTSPServerWithREGISTERProxying();
diff --git a/liveMedia/include/RTSPServerSupportingHTTPStreaming.hh b/liveMedia/include/RTSPServerSupportingHTTPStreaming.hh
index 1bf8920..0b3ba7a 100644
--- a/liveMedia/include/RTSPServerSupportingHTTPStreaming.hh
+++ b/liveMedia/include/RTSPServerSupportingHTTPStreaming.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A server that supports both RTSP, and HTTP streaming (using Apple's "HTTP Live Streaming" protocol)
// C++ header
@@ -48,7 +48,7 @@ protected:
virtual ~RTSPServerSupportingHTTPStreaming();
protected: // redefined virtual functions
- virtual RTSPClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr);
+ virtual ClientConnection* createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr);
public: // should be protected, but some old compilers complain otherwise
class RTSPClientConnectionSupportingHTTPStreaming: public RTSPServer::RTSPClientConnection {
@@ -64,6 +64,7 @@ public: // should be protected, but some old compilers complain otherwise
private:
u_int32_t fClientSessionId;
+ FramedSource* fStreamSource;
ByteStreamMemoryBufferSource* fPlaylistSource;
TCPStreamSink* fTCPSink;
};
diff --git a/liveMedia/include/SIPClient.hh b/liveMedia/include/SIPClient.hh
index 4a9ef2c..bc2501e 100644
--- a/liveMedia/include/SIPClient.hh
+++ b/liveMedia/include/SIPClient.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic SIP client
// C++ header
diff --git a/liveMedia/include/ServerMediaSession.hh b/liveMedia/include/ServerMediaSession.hh
index dbeefc5..8f81959 100644
--- a/liveMedia/include/ServerMediaSession.hh
+++ b/liveMedia/include/ServerMediaSession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A data structure that represents a session that consists of
// potentially multiple (audio and/or video) sub-sessions
// (This data structure is used for media *streamers* - i.e., servers.
@@ -24,17 +24,8 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#ifndef _SERVER_MEDIA_SESSION_HH
#define _SERVER_MEDIA_SESSION_HH
-#ifndef _MEDIA_HH
-#include "Media.hh"
-#endif
-#ifndef _FRAMED_SOURCE_HH
-#include "FramedSource.hh"
-#endif
-#ifndef _GROUPEID_HH
-#include "GroupEId.hh"
-#endif
-#ifndef _RTP_INTERFACE_HH
-#include "RTPInterface.hh" // for ServerRequestAlternativeByteHandler
+#ifndef _RTCP_HH
+#include "RTCP.hh"
#endif
class ServerMediaSubsession; // forward
@@ -148,7 +139,8 @@ public:
ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,
void* serverRequestAlternativeByteHandlerClientData) = 0;
virtual void pauseStream(unsigned clientSessionId, void* streamToken);
- virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT, double streamDuration, u_int64_t& numBytes);
+ virtual void seekStream(unsigned clientSessionId, void* streamToken, double& seekNPT,
+ double streamDuration, u_int64_t& numBytes);
// This routine is used to seek by relative (i.e., NPT) time.
// "streamDuration", if >0.0, specifies how much data to stream, past "seekNPT". (If <=0.0, all remaining data is streamed.)
// "numBytes" returns the size (in bytes) of the data to be streamed, or 0 if unknown or unlimited.
@@ -157,11 +149,18 @@ public:
// "absStart" should be a string of the form "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.<frac>Z".
// "absEnd" should be either NULL (for no end time), or a string of the same form as "absStart".
// These strings may be modified in-place, or can be reassigned to a newly-allocated value (after delete[]ing the original).
- virtual void nullSeekStream(unsigned clientSessionId, void* streamToken);
+ virtual void nullSeekStream(unsigned clientSessionId, void* streamToken,
+ double streamEndTime, u_int64_t& numBytes);
// Called whenever we're handling a "PLAY" command without a specified start time.
virtual void setStreamScale(unsigned clientSessionId, void* streamToken, float scale);
virtual float getCurrentNPT(void* streamToken);
virtual FramedSource* getStreamSource(void* streamToken);
+ virtual void getRTPSinkandRTCP(void* streamToken,
+ RTPSink const*& rtpSink, RTCPInstance const*& rtcp) = 0;
+ // Returns pointers to the "RTPSink" and "RTCPInstance" objects for "streamToken".
+ // (This can be useful if you want to get the associated 'Groupsock' objects, for example.)
+ // You must not delete these objects, or start/stop playing them; instead, that is done
+ // using the "startStream()" and "deleteStream()" functions.
virtual void deleteStream(unsigned clientSessionId, void*& streamToken);
virtual void testScaleFactor(float& scale); // sets "scale" to the actual supported scale
diff --git a/liveMedia/include/SimpleRTPSink.hh b/liveMedia/include/SimpleRTPSink.hh
index 808fc4a..babcee3 100644
--- a/liveMedia/include/SimpleRTPSink.hh
+++ b/liveMedia/include/SimpleRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A simple RTP sink that packs frames into each outgoing
// packet, without any fragmentation or special headers.
// C++ header
diff --git a/liveMedia/include/SimpleRTPSource.hh b/liveMedia/include/SimpleRTPSource.hh
index 1c4194a..49f1fb6 100644
--- a/liveMedia/include/SimpleRTPSource.hh
+++ b/liveMedia/include/SimpleRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A RTP source for a simple RTP payload format that
// - doesn't have any special headers following the RTP header
// (if necessary, the "offset" parameter can be used to specify a
diff --git a/liveMedia/include/StreamReplicator.hh b/liveMedia/include/StreamReplicator.hh
index 2fdc3a7..3993c5e 100644
--- a/liveMedia/include/StreamReplicator.hh
+++ b/liveMedia/include/StreamReplicator.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// An class that can be used to create (possibly multiple) 'replicas' of an incoming stream.
// C++ header
diff --git a/liveMedia/include/T140TextRTPSink.hh b/liveMedia/include/T140TextRTPSink.hh
index 6a3d178..2b2d9b0 100644
--- a/liveMedia/include/T140TextRTPSink.hh
+++ b/liveMedia/include/T140TextRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for T.140 text (RFC 2793)
// C++ header
diff --git a/liveMedia/include/TCPStreamSink.hh b/liveMedia/include/TCPStreamSink.hh
index c039d13..69abd79 100644
--- a/liveMedia/include/TCPStreamSink.hh
+++ b/liveMedia/include/TCPStreamSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A sink representing a TCP output stream
// C++ header
diff --git a/liveMedia/include/TextRTPSink.hh b/liveMedia/include/TextRTPSink.hh
index 32678ce..3dc8f3e 100644
--- a/liveMedia/include/TextRTPSink.hh
+++ b/liveMedia/include/TextRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTP sink for text codecs (abstract base class)
// C++ header
diff --git a/liveMedia/include/TheoraVideoRTPSink.hh b/liveMedia/include/TheoraVideoRTPSink.hh
index 1bd8733..28f4cc2 100644
--- a/liveMedia/include/TheoraVideoRTPSink.hh
+++ b/liveMedia/include/TheoraVideoRTPSink.hh
@@ -1,7 +1,22 @@
-/*
- * Theora Video RTP packetizer
- * Copied from live555's VorbisAudioRTPSink
- */
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+**********/
+// "liveMedia"
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// RTP sink for Theora video
+// C++ header
#ifndef _THEORA_VIDEO_RTP_SINK_HH
#define _THEORA_VIDEO_RTP_SINK_HH
@@ -12,25 +27,23 @@
class TheoraVideoRTPSink: public VideoRTPSink {
public:
- enum PixFmt {
- YUV420,
- YUV422,
- YUV444,
- };
-
- static TheoraVideoRTPSink* createNew(UsageEnvironment& env,
- Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency,
- unsigned width, unsigned height, enum PixFmt pf,
- // The following headers provide the 'configuration' information, for the SDP description:
- u_int8_t* identificationHeader, unsigned identificationHeaderSize,
- u_int8_t* commentHeader, unsigned commentHeaderSize,
- u_int8_t* setupHeader, unsigned setupHeaderSize, u_int32_t identField);
+ static TheoraVideoRTPSink*
+ createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ // The following headers provide the 'configuration' information, for the SDP description:
+ u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField = 0xFACADE);
+ static TheoraVideoRTPSink*
+ createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ char const* configStr);
+ // an optional variant of "createNew()" that takes a Base-64-encoded 'configuration' string,
+ // rather than the raw configuration headers as parameter.
+
protected:
TheoraVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency,
- unsigned width, unsigned height, enum PixFmt pf,
+ u_int8_t rtpPayloadFormat,
u_int8_t* identificationHeader, unsigned identificationHeaderSize,
u_int8_t* commentHeader, unsigned commentHeaderSize,
u_int8_t* setupHeader, unsigned setupHeaderSize,
diff --git a/liveMedia/include/VorbisAudioRTPSource.hh b/liveMedia/include/TheoraVideoRTPSource.hh
similarity index 73%
copy from liveMedia/include/VorbisAudioRTPSource.hh
copy to liveMedia/include/TheoraVideoRTPSource.hh
index 7242b77..cdb863d 100644
--- a/liveMedia/include/VorbisAudioRTPSource.hh
+++ b/liveMedia/include/TheoraVideoRTPSource.hh
@@ -14,33 +14,31 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// Vorbis Audio RTP Sources
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// Theora Video Audio RTP Sources
// C++ header
-#ifndef _VORBIS_AUDIO_RTP_SOURCE_HH
-#define _VORBIS_AUDIO_RTP_SOURCE_HH
+#ifndef _THEORA_VIDEO_RTP_SOURCE_HH
+#define _THEORA_VIDEO_RTP_SOURCE_HH
#ifndef _MULTI_FRAMED_RTP_SOURCE_HH
#include "MultiFramedRTPSource.hh"
#endif
-class VorbisAudioRTPSource: public MultiFramedRTPSource {
+class TheoraVideoRTPSource: public MultiFramedRTPSource {
public:
- static VorbisAudioRTPSource*
+ static TheoraVideoRTPSource*
createNew(UsageEnvironment& env, Groupsock* RTPgs,
- unsigned char rtpPayloadFormat,
- unsigned rtpTimestampFrequency);
+ unsigned char rtpPayloadFormat);
u_int32_t curPacketIdent() const { return fCurPacketIdent; } // The current "Ident" field; only the low-order 24 bits are used
protected:
- VorbisAudioRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
- unsigned char rtpPayloadFormat,
- unsigned rtpTimestampFrequency);
+ TheoraVideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
+ unsigned char rtpPayloadFormat);
// called only by createNew()
- virtual ~VorbisAudioRTPSource();
+ virtual ~TheoraVideoRTPSource();
protected:
// redefined virtual functions:
diff --git a/liveMedia/include/VP8VideoRTPSink.hh b/liveMedia/include/VP8VideoRTPSink.hh
index e745d5e..97cc35f 100644
--- a/liveMedia/include/VP8VideoRTPSink.hh
+++ b/liveMedia/include/VP8VideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for VP8 video
// C++ header
diff --git a/liveMedia/include/VP8VideoRTPSource.hh b/liveMedia/include/VP8VideoRTPSource.hh
index 744e3d1..31ed426 100644
--- a/liveMedia/include/VP8VideoRTPSource.hh
+++ b/liveMedia/include/VP8VideoRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// VP8 Video RTP Sources
// C++ header
diff --git a/liveMedia/include/VP8VideoRTPSink.hh b/liveMedia/include/VP9VideoRTPSink.hh
similarity index 81%
copy from liveMedia/include/VP8VideoRTPSink.hh
copy to liveMedia/include/VP9VideoRTPSink.hh
index e745d5e..6a877dc 100644
--- a/liveMedia/include/VP8VideoRTPSink.hh
+++ b/liveMedia/include/VP9VideoRTPSink.hh
@@ -14,26 +14,26 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// RTP sink for VP8 video
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// RTP sink for VP9 video
// C++ header
-#ifndef _VP8_VIDEO_RTP_SINK_HH
-#define _VP8_VIDEO_RTP_SINK_HH
+#ifndef _VP9_VIDEO_RTP_SINK_HH
+#define _VP9_VIDEO_RTP_SINK_HH
#ifndef _VIDEO_RTP_SINK_HH
#include "VideoRTPSink.hh"
#endif
-class VP8VideoRTPSink: public VideoRTPSink {
+class VP9VideoRTPSink: public VideoRTPSink {
public:
- static VP8VideoRTPSink* createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat);
+ static VP9VideoRTPSink* createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat);
protected:
- VP8VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat);
+ VP9VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat);
// called only by createNew()
- virtual ~VP8VideoRTPSink();
+ virtual ~VP9VideoRTPSink();
private: // redefined virtual functions:
virtual void doSpecialFrameHandling(unsigned fragmentationOffset,
diff --git a/liveMedia/include/VP8VideoRTPSource.hh b/liveMedia/include/VP9VideoRTPSource.hh
similarity index 80%
copy from liveMedia/include/VP8VideoRTPSource.hh
copy to liveMedia/include/VP9VideoRTPSource.hh
index 744e3d1..5c5bdc6 100644
--- a/liveMedia/include/VP8VideoRTPSource.hh
+++ b/liveMedia/include/VP9VideoRTPSource.hh
@@ -14,31 +14,31 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
-// VP8 Video RTP Sources
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
+// VP9 Video RTP Sources
// C++ header
-#ifndef _VP8_VIDEO_RTP_SOURCE_HH
-#define _VP8_VIDEO_RTP_SOURCE_HH
+#ifndef _VP9_VIDEO_RTP_SOURCE_HH
+#define _VP9_VIDEO_RTP_SOURCE_HH
#ifndef _MULTI_FRAMED_RTP_SOURCE_HH
#include "MultiFramedRTPSource.hh"
#endif
-class VP8VideoRTPSource: public MultiFramedRTPSource {
+class VP9VideoRTPSource: public MultiFramedRTPSource {
public:
- static VP8VideoRTPSource*
+ static VP9VideoRTPSource*
createNew(UsageEnvironment& env, Groupsock* RTPgs,
unsigned char rtpPayloadFormat,
unsigned rtpTimestampFrequency = 90000);
protected:
- VP8VideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
+ VP9VideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
unsigned char rtpPayloadFormat,
unsigned rtpTimestampFrequency);
// called only by createNew()
- virtual ~VP8VideoRTPSource();
+ virtual ~VP9VideoRTPSource();
protected:
// redefined virtual functions:
diff --git a/liveMedia/include/VideoRTPSink.hh b/liveMedia/include/VideoRTPSink.hh
index c763092..b129bce 100644
--- a/liveMedia/include/VideoRTPSink.hh
+++ b/liveMedia/include/VideoRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A generic RTP sink for video codecs (abstract base class)
// C++ header
diff --git a/liveMedia/include/VorbisAudioRTPSink.hh b/liveMedia/include/VorbisAudioRTPSink.hh
index d88c7bd..d434c2d 100644
--- a/liveMedia/include/VorbisAudioRTPSink.hh
+++ b/liveMedia/include/VorbisAudioRTPSink.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// RTP sink for Vorbis audio
// C++ header
@@ -27,26 +27,29 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
class VorbisAudioRTPSink: public AudioRTPSink {
public:
- static VorbisAudioRTPSink* createNew(UsageEnvironment& env,
- Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels,
- // The following headers provide the 'configuration' information, for the SDP description:
- u_int8_t* identificationHeader, unsigned identificationHeaderSize,
- u_int8_t* commentHeader, unsigned commentHeaderSize,
- u_int8_t* setupHeader, unsigned setupHeaderSize);
- static VorbisAudioRTPSink* createNew(UsageEnvironment& env,
- Groupsock* RTPgs,
- u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels,
- char const* configStr);
+ static VorbisAudioRTPSink*
+ createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ u_int32_t rtpTimestampFrequency, unsigned numChannels,
+ // The following headers provide the 'configuration' information, for the SDP description:
+ u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField = 0xFACADE);
+
+ static VorbisAudioRTPSink*
+ createNew(UsageEnvironment& env, Groupsock* RTPgs, u_int8_t rtpPayloadFormat,
+ u_int32_t rtpTimestampFrequency, unsigned numChannels,
+ char const* configStr);
// an optional variant of "createNew()" that takes a Base-64-encoded 'configuration' string,
- // rather than the raw configuration headers. as parameter.
+ // rather than the raw configuration headers as parameter.
+
protected:
VorbisAudioRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency, unsigned numChannels,
u_int8_t* identificationHeader, unsigned identificationHeaderSize,
u_int8_t* commentHeader, unsigned commentHeaderSize,
u_int8_t* setupHeader, unsigned setupHeaderSize,
- u_int32_t identField = 0xFACADE);
+ u_int32_t identField);
// called only by createNew()
virtual ~VorbisAudioRTPSink();
@@ -63,9 +66,20 @@ private: // redefined virtual functions:
unsigned numBytesInFrame) const;
virtual unsigned specialHeaderSize() const;
virtual unsigned frameSpecificHeaderSize() const;
-#endif
private:
u_int32_t fIdent; // "Ident" field used by this stream. (Only the low 24 bits of this are used.)
char* fFmtpSDPLine;
};
+
+
+// A general function used by both "VorbisAudioRTPSink" and "TheoraVideoRTPSink" to construct
+// a Base64-encoded 'config' string (for SDP) from "identification", "comment", "setup" headers.
+// (Note: The result string was heap-allocated, and the caller should delete[] it afterwards.)
+
+char* generateVorbisOrTheoraConfigStr(u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+ u_int8_t* commentHeader, unsigned commentHeaderSize,
+ u_int8_t* setupHeader, unsigned setupHeaderSize,
+ u_int32_t identField);
+
+#endif
diff --git a/liveMedia/include/VorbisAudioRTPSource.hh b/liveMedia/include/VorbisAudioRTPSource.hh
index 7242b77..6489cca 100644
--- a/liveMedia/include/VorbisAudioRTPSource.hh
+++ b/liveMedia/include/VorbisAudioRTPSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Vorbis Audio RTP Sources
// C++ header
@@ -52,4 +52,15 @@ private:
u_int32_t fCurPacketIdent; // only the low-order 24 bits are used
};
+void parseVorbisOrTheoraConfigStr(char const* configStr,
+ u_int8_t*& identificationHdr, unsigned& identificationHdrSize,
+ u_int8_t*& commentHdr, unsigned& commentHdrSize,
+ u_int8_t*& setupHdr, unsigned& setupHdrSize,
+ u_int32_t& identField);
+ // Returns (in each of the result parameters) unpacked Vorbis or Theora
+ // "identification", "comment", and "setup" headers that were specified in a
+ // "config" string (in the SDP description for a Vorbis/RTP or Theora/RTP stream).
+ // Each of the "*Hdr" result arrays are dynamically allocated by this routine,
+ // and must be delete[]d by the caller.
+
#endif
diff --git a/liveMedia/include/WAVAudioFileServerMediaSubsession.hh b/liveMedia/include/WAVAudioFileServerMediaSubsession.hh
index 41787fc..a42cc9d 100644
--- a/liveMedia/include/WAVAudioFileServerMediaSubsession.hh
+++ b/liveMedia/include/WAVAudioFileServerMediaSubsession.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A 'ServerMediaSubsession' object that creates new, unicast, "RTPSink"s
// on demand, from an WAV audio file.
// C++ header
@@ -43,6 +43,8 @@ protected:
protected: // redefined virtual functions
virtual void seekStreamSource(FramedSource* inputSource, double& seekNPT, double streamDuration, u_int64_t& numBytes);
virtual void setStreamSourceScale(FramedSource* inputSource, float scale);
+ virtual void setStreamSourceDuration(FramedSource* inputSource, double streamDuration, u_int64_t& numBytes);
+
virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
diff --git a/liveMedia/include/WAVAudioFileSource.hh b/liveMedia/include/WAVAudioFileSource.hh
index 65c385e..c195af0 100644
--- a/liveMedia/include/WAVAudioFileSource.hh
+++ b/liveMedia/include/WAVAudioFileSource.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// A WAV audio file source
// NOTE: Samples are returned in little-endian order (the same order in which
// they were stored in the file).
@@ -44,7 +44,8 @@ public:
unsigned numPCMBytes() const;
void setScaleFactor(int scale);
- void seekToPCMByte(unsigned byteNumber, unsigned numBytesToStream);
+ void seekToPCMByte(unsigned byteNumber);
+ void limitNumBytesToStream(unsigned numBytesToStream);
// if "numBytesToStream" is >0, then we limit the stream to that number of bytes, before treating it as EOF
unsigned char getAudioFormat();
diff --git a/liveMedia/include/liveMedia.hh b/liveMedia/include/liveMedia.hh
index bf61c33..1f0b9bc 100644
--- a/liveMedia/include/liveMedia.hh
+++ b/liveMedia/include/liveMedia.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Inclusion of header files representing the interface
// for the entire library
//
@@ -30,6 +30,8 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "MPEG4ESVideoRTPSink.hh"
#include "AMRAudioFileSink.hh"
#include "H264VideoFileSink.hh"
+#include "H265VideoFileSink.hh"
+#include "OggFileSink.hh"
#include "BasicUDPSink.hh"
#include "GSMAudioRTPSink.hh"
#include "H263plusVideoRTPSink.hh"
@@ -63,7 +65,9 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "JPEGVideoSource.hh"
#include "MPEG1or2VideoRTPSource.hh"
#include "VorbisAudioRTPSource.hh"
+#include "TheoraVideoRTPSource.hh"
#include "VP8VideoRTPSource.hh"
+#include "VP9VideoRTPSource.hh"
#include "MPEG2TransportStreamFromPESSource.hh"
#include "MPEG2TransportStreamFromESSource.hh"
#include "MPEG2TransportStreamFramer.hh"
@@ -71,6 +75,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "H261VideoRTPSource.hh"
#include "H263plusVideoRTPSource.hh"
#include "H264VideoRTPSource.hh"
+#include "H265VideoRTPSource.hh"
#include "MP3FileSource.hh"
#include "MP3ADU.hh"
#include "MP3ADUinterleaving.hh"
@@ -82,7 +87,9 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "AC3AudioRTPSource.hh"
#include "AC3AudioRTPSink.hh"
#include "VorbisAudioRTPSink.hh"
+#include "TheoraVideoRTPSink.hh"
#include "VP8VideoRTPSink.hh"
+#include "VP9VideoRTPSink.hh"
#include "MPEG4GenericRTPSink.hh"
#include "MPEG1or2VideoStreamDiscreteFramer.hh"
#include "MPEG4VideoStreamDiscreteFramer.hh"
@@ -117,7 +124,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
#include "AC3AudioFileServerMediaSubsession.hh"
#include "MPEG2TransportUDPServerMediaSubsession.hh"
#include "MatroskaFileServerDemux.hh"
+#include "OggFileServerDemux.hh"
#include "ProxyServerMediaSession.hh"
-#include "DarwinInjector.hh"
#endif
diff --git a/liveMedia/include/liveMedia_version.hh b/liveMedia/include/liveMedia_version.hh
index a38163b..df4b6d7 100644
--- a/liveMedia/include/liveMedia_version.hh
+++ b/liveMedia/include/liveMedia_version.hh
@@ -1,10 +1,10 @@
// Version information for the "liveMedia" library
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2015 Live Networks, Inc. All rights reserved.
#ifndef _LIVEMEDIA_VERSION_HH
#define _LIVEMEDIA_VERSION_HH
-#define LIVEMEDIA_LIBRARY_VERSION_STRING "2014.01.13"
-#define LIVEMEDIA_LIBRARY_VERSION_INT 1389571200
+#define LIVEMEDIA_LIBRARY_VERSION_STRING "2015.12.22"
+#define LIVEMEDIA_LIBRARY_VERSION_INT 1450742400
#endif
diff --git a/liveMedia/ourMD5.hh b/liveMedia/include/ourMD5.hh
similarity index 70%
rename from liveMedia/ourMD5.hh
rename to liveMedia/include/ourMD5.hh
index 9bd15ac..1cd7bbf 100644
--- a/liveMedia/ourMD5.hh
+++ b/liveMedia/include/ourMD5.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Because MD5 may not be implemented (at least, with the same interface) on all systems,
// we have our own implementation.
// C++ header
@@ -26,4 +26,13 @@ extern char* our_MD5Data(unsigned char const* data, unsigned dataSize, char* out
// "outputDigest" must be either NULL (in which case this function returns a heap-allocated
// buffer, which should be later delete[]d by the caller), or else it must point to
// a (>=)33-byte buffer (which this function will also return).
+
+extern unsigned char* our_MD5DataRaw(unsigned char const* data, unsigned dataSize,
+ unsigned char* outputDigest);
+ // Like "ourMD5Data()", except that it returns the digest in 'raw' binary form, rather than
+ // as an ASCII hex string.
+ // "outputDigest" must be either NULL (in which case this function returns a heap-allocated
+ // buffer, which should be later delete[]d by the caller), or else it must point to
+ // a (>=)16-byte buffer (which this function will also return).
+
#endif
diff --git a/liveMedia/include/uLawAudioFilter.hh b/liveMedia/include/uLawAudioFilter.hh
index d6066bc..92441bb 100644
--- a/liveMedia/include/uLawAudioFilter.hh
+++ b/liveMedia/include/uLawAudioFilter.hh
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Filters for converting between raw PCM audio and uLaw
// C++ header
diff --git a/liveMedia/ourMD5.cpp b/liveMedia/ourMD5.cpp
index 751b0ab..12b21d2 100644
--- a/liveMedia/ourMD5.cpp
+++ b/liveMedia/ourMD5.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Because MD5 may not be implemented (at least, with the same interface) on all systems,
// we have our own implementation.
// Implementation
@@ -36,11 +36,11 @@ public:
void addData(unsigned char const* inputData, unsigned inputDataSize);
void end(char* outputDigest /*must point to an array of size DIGEST_SIZE_AS_STRING*/);
-
-private:
void finalize(unsigned char* outputDigestInBytes);
// Like "end()", except that the argument is a byte array, of size DIGEST_SIZE_IN_BYTES.
// This function is used to implement "end()".
+
+private:
void zeroize(); // to remove potentially sensitive information
void transform64Bytes(unsigned char const block[64]); // does the actual MD5 transform
@@ -61,6 +61,18 @@ char* our_MD5Data(unsigned char const* data, unsigned dataSize, char* outputDige
return outputDigest;
}
+unsigned char* our_MD5DataRaw(unsigned char const* data, unsigned dataSize,
+ unsigned char* outputDigest) {
+ MD5Context ctx;
+
+ ctx.addData(data, dataSize);
+
+ if (outputDigest == NULL) outputDigest = new unsigned char[DIGEST_SIZE_IN_BYTES];
+ ctx.finalize(outputDigest);
+
+ return outputDigest;
+}
+
////////// MD5Context implementation //////////
diff --git a/liveMedia/rtcp_from_spec.h b/liveMedia/rtcp_from_spec.h
index 912d2d9..629971f 100644
--- a/liveMedia/rtcp_from_spec.h
+++ b/liveMedia/rtcp_from_spec.h
@@ -40,6 +40,7 @@ typedef void* packet;
#define PACKET_RTP 1
#define PACKET_RTCP_REPORT 2
#define PACKET_BYE 3
+#define PACKET_RTCP_APP 4
/* The code from the spec calls drand48(), but we have drand30() instead */
#define drand48 drand30
diff --git a/liveMedia/uLawAudioFilter.cpp b/liveMedia/uLawAudioFilter.cpp
index efacad6..041e67e 100644
--- a/liveMedia/uLawAudioFilter.cpp
+++ b/liveMedia/uLawAudioFilter.cpp
@@ -14,7 +14,7 @@ along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
-// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
+// Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved.
// Filters for converting between raw PCM audio and uLaw
// Implementation
diff --git a/mediaServer/DynamicRTSPServer.cpp b/mediaServer/DynamicRTSPServer.cpp
index 1159dec..a5a23f6 100644
--- a/mediaServer/DynamicRTSPServer.cpp
+++ b/mediaServer/DynamicRTSPServer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A subclass of "RTSPServer" that creates "ServerMediaSession"s on demand,
// based on whether or not the specified stream name exists as a file
// Implementation
@@ -44,8 +44,8 @@ DynamicRTSPServer::~DynamicRTSPServer() {
static ServerMediaSession* createNewSMS(UsageEnvironment& env,
char const* fileName, FILE* fid); // forward
-ServerMediaSession*
-DynamicRTSPServer::lookupServerMediaSession(char const* streamName) {
+ServerMediaSession* DynamicRTSPServer
+::lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession) {
// First, check whether the specified "streamName" exists as a local file:
FILE* fid = fopen(streamName, "rb");
Boolean fileExists = fid != NULL;
@@ -59,28 +59,52 @@ DynamicRTSPServer::lookupServerMediaSession(char const* streamName) {
if (smsExists) {
// "sms" was created for a file that no longer exists. Remove it:
removeServerMediaSession(sms);
+ sms = NULL;
}
+
return NULL;
} else {
- if (!smsExists) {
- // Create a new "ServerMediaSession" object for streaming from the named file.
- sms = createNewSMS(envir(), streamName, fid);
+ 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;
}
}
// Special code for handling Matroska files:
-static char newMatroskaDemuxWatchVariable;
-static MatroskaFileServerDemux* demux;
-static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* /*clientData*/) {
- demux = newDemux;
- newMatroskaDemuxWatchVariable = 1;
+struct MatroskaDemuxCreationState {
+ MatroskaFileServerDemux* demux;
+ char watchVariable;
+};
+static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* clientData) {
+ MatroskaDemuxCreationState* creationState = (MatroskaDemuxCreationState*)clientData;
+ creationState->demux = newDemux;
+ creationState->watchVariable = 1;
}
// END Special code for handling Matroska files:
+// Special code for handling Ogg files:
+struct OggDemuxCreationState {
+ OggFileServerDemux* demux;
+ char watchVariable;
+};
+static void onOggDemuxCreation(OggFileServerDemux* newDemux, void* clientData) {
+ OggDemuxCreationState* creationState = (OggDemuxCreationState*)clientData;
+ creationState->demux = newDemux;
+ creationState->watchVariable = 1;
+}
+// END Special code for handling Ogg files:
+
#define NEW_SMS(description) do {\
char const* descStr = description\
", streamed by the LIVE555 Media Server";\
@@ -181,15 +205,33 @@ static ServerMediaSession* createNewSMS(UsageEnvironment& env,
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.)
- newMatroskaDemuxWatchVariable = 0;
- MatroskaFileServerDemux::createNew(env, fileName, onMatroskaDemuxCreation, NULL);
- env.taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
+ // 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 = demux->newServerMediaSubsession()) != NULL) {
+ while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
}
}
diff --git a/mediaServer/DynamicRTSPServer.hh b/mediaServer/DynamicRTSPServer.hh
index bb58817..3478f40 100644
--- a/mediaServer/DynamicRTSPServer.hh
+++ b/mediaServer/DynamicRTSPServer.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A subclass of "RTSPServer" that creates "ServerMediaSession"s on demand,
// based on whether or not the specified stream name exists as a file
// Header file
@@ -38,7 +38,8 @@ protected:
virtual ~DynamicRTSPServer();
protected: // redefined virtual functions
- virtual ServerMediaSession* lookupServerMediaSession(char const* streamName);
+ virtual ServerMediaSession*
+ lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession);
};
#endif
diff --git a/mediaServer/live555MediaServer.cpp b/mediaServer/live555MediaServer.cpp
index af805c9..58ef0ca 100644
--- a/mediaServer/live555MediaServer.cpp
+++ b/mediaServer/live555MediaServer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// LIVE555 Media Server
// main program
@@ -68,6 +68,7 @@ int main(int argc, char** argv) {
*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";
diff --git a/mediaServer/version.hh b/mediaServer/version.hh
index 4081652..e6876fa 100644
--- a/mediaServer/version.hh
+++ b/mediaServer/version.hh
@@ -1,10 +1,10 @@
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// Version information for the LIVE555 Media Server application
// Header file
#ifndef _MEDIA_SERVER_VERSION_HH
#define _MEDIA_SERVER_VERSION_HH
-#define MEDIA_SERVER_VERSION_STRING "0.80"
+#define MEDIA_SERVER_VERSION_STRING "0.88"
#endif
diff --git a/proxyServer/live555ProxyServer.cpp b/proxyServer/live555ProxyServer.cpp
index 3182175..3d02d9a 100644
--- a/proxyServer/live555ProxyServer.cpp
+++ b/proxyServer/live555ProxyServer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// LIVE555 Proxy Server
// main program
diff --git a/testProgs/MPEG2TransportStreamIndexer.cpp b/testProgs/MPEG2TransportStreamIndexer.cpp
index 0c84736..850baf6 100644
--- a/testProgs/MPEG2TransportStreamIndexer.cpp
+++ b/testProgs/MPEG2TransportStreamIndexer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that reads an existing MPEG-2 Transport Stream file,
// and generates a separate index file that can be used - by our RTSP server
// implementation - to support 'trick play' operations when streaming the
diff --git a/testProgs/Makefile.tail b/testProgs/Makefile.tail
index 7432620..9c3fad3 100644
--- a/testProgs/Makefile.tail
+++ b/testProgs/Makefile.tail
@@ -1,6 +1,6 @@
##### End of variables to change
-MULTICAST_STREAMER_APPS = testMP3Streamer$(EXE) testMPEG1or2VideoStreamer$(EXE) testMPEG1or2AudioVideoStreamer$(EXE) testMPEG2TransportStreamer$(EXE) testMPEG4VideoStreamer$(EXE) testH264VideoStreamer$(EXE) testH265VideoStreamer$(EXE) testDVVideoStreamer$(EXE) testWAVAudioStreamer$(EXE) testAMRAudioStreamer$(EXE) vobStreamer$(EXE)
+MULTICAST_STREAMER_APPS = testMP3Streamer$(EXE) testMPEG1or2VideoStreamer$(EXE) testMPEG1or2AudioVideoStreamer$(EXE) testMPEG2TransportStreamer$(EXE) testMPEG4VideoStreamer$(EXE) testH264VideoStreamer$(EXE) testH265VideoStreamer$(EXE) testDVVideoStreamer$(EXE) testWAVAudioStreamer$(EXE) testAMRAudioStreamer$(EXE) testMKVStreamer$(EXE) testOggStreamer$(EXE) vobStreamer$(EXE)
MULTICAST_RECEIVER_APPS = testMP3Receiver$(EXE) testMPEG1or2VideoReceiver$(EXE) testMPEG2TransportReceiver$(EXE) sapWatch$(EXE)
MULTICAST_MISC_APPS = testRelay$(EXE) testReplicator$(EXE)
MULTICAST_APPS = $(MULTICAST_STREAMER_APPS) $(MULTICAST_RECEIVER_APPS) $(MULTICAST_MISC_APPS)
@@ -39,6 +39,8 @@ DV_VIDEO_STREAMER_OBJS = testDVVideoStreamer.$(OBJ)
WAV_AUDIO_STREAMER_OBJS = testWAVAudioStreamer.$(OBJ)
AMR_AUDIO_STREAMER_OBJS = testAMRAudioStreamer.$(OBJ)
ON_DEMAND_RTSP_SERVER_OBJS = testOnDemandRTSPServer.$(OBJ)
+MKV_STREAMER_OBJS = testMKVStreamer.$(OBJ)
+OGG_STREAMER_OBJS = testOggStreamer.$(OBJ)
VOB_STREAMER_OBJS = vobStreamer.$(OBJ)
TEST_RTSP_CLIENT_OBJS = testRTSPClient.$(OBJ)
OPEN_RTSP_OBJS = openRTSP.$(OBJ) playCommon.$(OBJ)
@@ -103,6 +105,10 @@ testAMRAudioStreamer$(EXE): $(AMR_AUDIO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(AMR_AUDIO_STREAMER_OBJS) $(LIBS)
testOnDemandRTSPServer$(EXE): $(ON_DEMAND_RTSP_SERVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(ON_DEMAND_RTSP_SERVER_OBJS) $(LIBS)
+testMKVStreamer$(EXE): $(MKV_STREAMER_OBJS) $(LOCAL_LIBS)
+ $(LINK)$@ $(CONSOLE_LINK_OPTS) $(MKV_STREAMER_OBJS) $(LIBS)
+testOggStreamer$(EXE): $(OGG_STREAMER_OBJS) $(LOCAL_LIBS)
+ $(LINK)$@ $(CONSOLE_LINK_OPTS) $(OGG_STREAMER_OBJS) $(LIBS)
vobStreamer$(EXE): $(VOB_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(VOB_STREAMER_OBJS) $(LIBS)
testRTSPClient$(EXE): $(TEST_RTSP_CLIENT_OBJS) $(LOCAL_LIBS)
diff --git a/testProgs/openRTSP.cpp b/testProgs/openRTSP.cpp
index 85aaeba..0fd2675 100644
--- a/testProgs/openRTSP.cpp
+++ b/testProgs/openRTSP.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A RTSP client application that opens a RTSP URL argument,
// and extracts and records the data from each incoming RTP stream.
//
diff --git a/testProgs/playCommon.cpp b/testProgs/playCommon.cpp
index 3c0ee93..c78b432 100644
--- a/testProgs/playCommon.cpp
+++ b/testProgs/playCommon.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A common framework, used for the "openRTSP" and "playSIP" applications
// Implementation
//
@@ -54,6 +54,7 @@ void shutdown(int exitCode = 1);
void signalHandlerShutdown(int sig);
void checkForPacketArrival(void* clientData);
void checkInterPacketGaps(void* clientData);
+void checkSessionTimeoutBrokenServer(void* clientData);
void beginQOSMeasurement();
char const* progName;
@@ -63,6 +64,7 @@ Authenticator* ourAuthenticator = NULL;
char const* streamURL = NULL;
MediaSession* session = NULL;
TaskToken sessionTimerTask = NULL;
+TaskToken sessionTimeoutBrokenServerTask = NULL;
TaskToken arrivalCheckTimerTask = NULL;
TaskToken interPacketGapCheckTimerTask = NULL;
TaskToken qosMeasurementTimerTask = NULL;
@@ -91,6 +93,8 @@ Boolean sendOptionsRequest = True;
Boolean sendOptionsRequestOnly = False;
Boolean oneFilePerFrame = False;
Boolean notifyOnPacketArrival = False;
+Boolean sendKeepAlivesToBrokenServers = False;
+unsigned sessionTimeoutParameter = 0;
Boolean streamUsingTCP = False;
Boolean forceMulticastOnUnspecified = False;
unsigned short desiredPortNum = 0;
@@ -136,7 +140,7 @@ void usage() {
<< "]" << (supportCodecSelection ? " [-A <audio-codec-rtp-payload-format-code>|-M <mime-subtype-name>]" : "")
<< " [-s <initial-seek-time>]|[-U <absolute-seek-time>] [-z <scale>] [-g user-agent]"
<< " [-k <username-for-REGISTER> <password-for-REGISTER>]"
- << " [-P <interval-in-seconds>]"
+ << " [-P <interval-in-seconds>] [-K]"
<< " [-w <width> -h <height>] [-f <frames-per-second>] [-y] [-H] [-Q [<measurement-interval>]] [-F <filename-prefix>] [-b <file-sink-buffer-size>] [-B <input-socket-buffer-size>] [-I <input-interface-ip-address>] [-m] [<url>|-R [<port-num>]] (or " << progName << " -o [-V] <url>)\n";
shutdown();
}
@@ -363,6 +367,11 @@ int main(int argc, char** argv) {
break;
}
+ case 'K': { // Send periodic 'keep-alive' requests to keep broken server sessions alive
+ sendKeepAlivesToBrokenServers = True;
+ break;
+ }
+
case 'A': { // specify a desired audio RTP payload format
unsigned formatArg;
if (sscanf(argv[2], "%u", &formatArg) != 1
@@ -705,9 +714,14 @@ void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) {
<< "\" subsession: " << env->getResultMsg() << "\n";
} else {
*env << "Created receiver for \"" << subsession->mediumName()
- << "/" << subsession->codecName()
- << "\" subsession (client ports " << subsession->clientPortNum()
- << "-" << subsession->clientPortNum()+1 << ")\n";
+ << "/" << subsession->codecName() << "\" subsession (";
+ if (subsession->rtcpIsMuxed()) {
+ *env << "client port " << subsession->clientPortNum();
+ } else {
+ *env << "client ports " << subsession->clientPortNum()
+ << "-" << subsession->clientPortNum()+1;
+ }
+ *env << ")\n";
madeProgress = True;
if (subsession->rtpSource() != NULL) {
@@ -756,12 +770,18 @@ void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) {
MediaSubsession *subsession;
Boolean madeProgress = False;
-void continueAfterSETUP(RTSPClient*, int resultCode, char* resultString) {
+void continueAfterSETUP(RTSPClient* client, int resultCode, char* resultString) {
if (resultCode == 0) {
*env << "Setup \"" << subsession->mediumName()
<< "/" << subsession->codecName()
- << "\" subsession (client ports " << subsession->clientPortNum()
- << "-" << subsession->clientPortNum()+1 << ")\n";
+ << "\" subsession (";
+ if (subsession->rtcpIsMuxed()) {
+ *env << "client port " << subsession->clientPortNum();
+ } else {
+ *env << "client ports " << subsession->clientPortNum()
+ << "-" << subsession->clientPortNum()+1;
+ }
+ *env << ")\n";
madeProgress = True;
} else {
*env << "Failed to setup \"" << subsession->mediumName()
@@ -770,6 +790,8 @@ void continueAfterSETUP(RTSPClient*, int resultCode, char* resultString) {
}
delete[] resultString;
+ sessionTimeoutParameter = client->sessionTimeoutParameter();
+
// Set up the next subsession, if any:
setupStreams();
}
@@ -842,25 +864,49 @@ void createOutputFiles(char const* periodicFilenameSuffix) {
// (unless the '-P <interval-in-seconds>' option was given):
sprintf(outFileName, "stdout");
}
- FileSink* fileSink;
- if (strcmp(subsession->mediumName(), "audio") == 0 &&
- (strcmp(subsession->codecName(), "AMR") == 0 ||
- strcmp(subsession->codecName(), "AMR-WB") == 0)) {
- // For AMR audio streams, we use a special sink that inserts AMR frame hdrs:
- fileSink = AMRAudioFileSink::createNew(*env, outFileName,
- fileSinkBufferSize, oneFilePerFrame);
- } else if (strcmp(subsession->mediumName(), "video") == 0 &&
- (strcmp(subsession->codecName(), "H264") == 0)) {
- // For H.264 video stream, we use a special sink that adds 'start codes', and (at the start) the SPS and PPS NAL units:
- fileSink = H264VideoFileSink::createNew(*env, outFileName,
- subsession->fmtp_spropparametersets(),
- fileSinkBufferSize, oneFilePerFrame);
- } else {
+
+ FileSink* fileSink = NULL;
+ Boolean createOggFileSink = False; // by default
+ if (strcmp(subsession->mediumName(), "video") == 0) {
+ if (strcmp(subsession->codecName(), "H264") == 0) {
+ // For H.264 video stream, we use a special sink that adds 'start codes',
+ // and (at the start) the SPS and PPS NAL units:
+ fileSink = H264VideoFileSink::createNew(*env, outFileName,
+ subsession->fmtp_spropparametersets(),
+ fileSinkBufferSize, oneFilePerFrame);
+ } else if (strcmp(subsession->codecName(), "H265") == 0) {
+ // For H.265 video stream, we use a special sink that adds 'start codes',
+ // and (at the start) the VPS, SPS, and PPS NAL units:
+ fileSink = H265VideoFileSink::createNew(*env, outFileName,
+ subsession->fmtp_spropvps(),
+ subsession->fmtp_spropsps(),
+ subsession->fmtp_sproppps(),
+ fileSinkBufferSize, oneFilePerFrame);
+ } else if (strcmp(subsession->codecName(), "THEORA") == 0) {
+ createOggFileSink = True;
+ }
+ } else if (strcmp(subsession->mediumName(), "audio") == 0) {
+ if (strcmp(subsession->codecName(), "AMR") == 0 ||
+ strcmp(subsession->codecName(), "AMR-WB") == 0) {
+ // For AMR audio streams, we use a special sink that inserts AMR frame hdrs:
+ fileSink = AMRAudioFileSink::createNew(*env, outFileName,
+ fileSinkBufferSize, oneFilePerFrame);
+ } else if (strcmp(subsession->codecName(), "VORBIS") == 0 ||
+ strcmp(subsession->codecName(), "OPUS") == 0) {
+ createOggFileSink = True;
+ }
+ }
+ if (createOggFileSink) {
+ fileSink = OggFileSink
+ ::createNew(*env, outFileName,
+ subsession->rtpTimestampFrequency(), subsession->fmtp_config());
+ } else if (fileSink == NULL) {
// Normal case:
fileSink = FileSink::createNew(*env, outFileName,
fileSinkBufferSize, oneFilePerFrame);
}
subsession->sink = fileSink;
+
if (subsession->sink == NULL) {
*env << "Failed to create FileSink for \"" << outFileName
<< "\": " << env->getResultMsg() << "\n";
@@ -974,6 +1020,7 @@ void continueAfterPLAY(RTSPClient*, int resultCode, char* resultString) {
*env << "Failed to start playing session: " << resultString << "\n";
delete[] resultString;
shutdown();
+ return;
} else {
*env << "Started playing session\n";
}
@@ -1019,9 +1066,12 @@ void continueAfterPLAY(RTSPClient*, int resultCode, char* resultString) {
#endif
}
+ sessionTimeoutBrokenServerTask = NULL;
+
// Watch for incoming packets (if desired):
checkForPacketArrival(NULL);
checkInterPacketGaps(NULL);
+ checkSessionTimeoutBrokenServer(NULL);
}
void closeMediaSinks() {
@@ -1078,6 +1128,7 @@ void sessionAfterPlaying(void* /*clientData*/) {
if (env != NULL) {
env->taskScheduler().unscheduleDelayedTask(periodicFileOutputTask);
env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
+ env->taskScheduler().unscheduleDelayedTask(sessionTimeoutBrokenServerTask);
env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
@@ -1320,6 +1371,7 @@ void shutdown(int exitCode) {
if (env != NULL) {
env->taskScheduler().unscheduleDelayedTask(periodicFileOutputTask);
env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
+ env->taskScheduler().unscheduleDelayedTask(sessionTimeoutBrokenServerTask);
env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
@@ -1451,3 +1503,21 @@ void checkInterPacketGaps(void* /*clientData*/) {
(TaskFunc*)checkInterPacketGaps, NULL);
}
}
+
+void checkSessionTimeoutBrokenServer(void* /*clientData*/) {
+ if (!sendKeepAlivesToBrokenServers) return; // we're not checking
+
+ // Send an "OPTIONS" request, starting with the second call
+ if (sessionTimeoutBrokenServerTask != NULL) {
+ getOptions(NULL);
+ }
+
+ unsigned sessionTimeout = sessionTimeoutParameter == 0 ? 60/*default*/ : sessionTimeoutParameter;
+ unsigned secondsUntilNextKeepAlive = sessionTimeout <= 5 ? 1 : sessionTimeout - 5;
+ // Reduce the interval a little, to be on the safe side
+
+ sessionTimeoutBrokenServerTask
+ = env->taskScheduler().scheduleDelayedTask(secondsUntilNextKeepAlive*1000000,
+ (TaskFunc*)checkSessionTimeoutBrokenServer, NULL);
+
+}
diff --git a/testProgs/playCommon.hh b/testProgs/playCommon.hh
index 68d20cb..c719773 100644
--- a/testProgs/playCommon.hh
+++ b/testProgs/playCommon.hh
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A common framework, used for the "openRTSP" and "playSIP" applications
// Interfaces
diff --git a/testProgs/playSIP.cpp b/testProgs/playSIP.cpp
index a6bef00..9756958 100644
--- a/testProgs/playSIP.cpp
+++ b/testProgs/playSIP.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A SIP client test program that opens a SIP URL argument,
// and extracts the data from each incoming RTP stream.
diff --git a/testProgs/registerRTSPStream.cpp b/testProgs/registerRTSPStream.cpp
index 89d0606..4658c08 100644
--- a/testProgs/registerRTSPStream.cpp
+++ b/testProgs/registerRTSPStream.cpp
@@ -13,11 +13,9 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A demonstration application that uses our custom RTSP "REGISTER" command to register a stream
// (given by "rtsp://" URL) with a RTSP client or proxy server
-// splits it into Audio (AC3) and Video (MPEG) Elementary Streams,
-// and streams both using RTP.
// main program
#include "liveMedia.hh"
diff --git a/testProgs/sapWatch.cpp b/testProgs/sapWatch.cpp
index b3449cb..c4aa1e3 100644
--- a/testProgs/sapWatch.cpp
+++ b/testProgs/sapWatch.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that receives and prints SDP/SAP announcements
// (on the default SDP/SAP directory: 224.2.127.254/9875)
diff --git a/testProgs/test.mov b/testProgs/test.mov
new file mode 100644
index 0000000..abf63ad
Binary files /dev/null and b/testProgs/test.mov differ
diff --git a/testProgs/test.txt b/testProgs/test.txt
new file mode 100644
index 0000000..5c6983a
--- /dev/null
+++ b/testProgs/test.txt
@@ -0,0 +1,26 @@
+File size: 5016383
+mdat <4997601 interior bytes>
+moov
+1: mvhd :00000000:d29dcd1b:d29dcd1b:00015f90:005121ba:00010000:01000000:00000000:00000000:00010000:00000000:00000000:00000000:00010000:00000000:00000000:00000000:40000000:00000000:00000000:00000000:00000000:00000000:00000000:00000002
+1: trak
+2: tkhd :0000000f:d29dcd1b:d29dcd1b:00000001:00000000:005121ba:00000000:00000000:00000000:01000000:00010000:00000000:00000000:00000000:00010000:00000000:00000000:00000000:40000000:00f00000:00b40000
+2: edts
+3: elst :00000000:0000000c:0006e42d:00000000:00010000:0006e4ad:00000bff:00010000:0006e4ae:000017ff:00010000:0006e4ac:000023ff:00010000:0006e4ad:00002fff:00010000:0006e4ac:00003bff:00010000:0006e4ae:000047ff:00010000:0006e4ad:000053ff:00010000:0006e4ac:00005fff:00010000:0006e4ac:00006bff:00010000:0006e4ae:000077ff:00010000:0003ca50:000083ff:00010000
+2: mdia
+3: mdhd :00000000:d29dcd1b:d29dcd1b:00000258:00008a77:00000000
+3: hdlr :00000000:6d686c72:76696465:6170706c:00000000:00000000:19417070:6c652056:6964656f:204d6564:69612048:616e646c:6572
+3: minf
+4: vmhd :00000001:00408000:80008000
+4: hdlr :00000000:64686c72:616c6973:6170706c:00000000:00000000:18417070:6c652041:6c696173:20446174:61204861:6e646c65:72
+4: dinf
+5: dref :00000000:00000001
+6: alis :00000001
+4: stbl
+5: stsd :00000000:00000001
+6: avc1 :00000000:00000001:00000000:6170706c:00000000:00000000:00f000b4:00480000:00480000:00000000:00010548:2e323634:00000000:00000000:00000000:00000000:00000000:00000000:00000018:ffff
+7: avcC :014d4033:ffe10018:674d4033:92540c04:b4200000:03004000:000cd1e3:06540000:01000668:ee3c8000:00
+5: stts :00000000:00000003:00000023:00000018:00000001:00000017:000005a1:00000018
+5: stss :00000000:00000026:00000001:00000033:00000065:00000097:000000c9:000000f5:00000129:00000157:0000015b:0000018d:000001bf:000001e7:000001f1:0000021f:00000223:00000255:00000287:000002b0:000002b9:000002eb:0000031d:0000034f:00000381:000003b3:000003dc:000003e5:00000416:00000417:00000449:00000476:0000047b:000004ad:000004df:00000511:0000053f:00000543:00000575:000005a7
+5: stsc :00000000:00000003:00000001:00000001:00000001:00000313:00000002:00000001:00000314:00000001:00000001
+5: stsz :00000000:00000000:000005c5:000028ff:0000086f:000009ab:00000d94:00000d2f:00000c36:00000b9e:00000d13:00000d3a:00000c25:00000d19:00000e8d:00000c75:00000d6c:00000da3:00000e31:00000c1e:00000c4a:00000ee3:00000e20:00000c15:00000cb8:00000cdd:00000df7:00000c93:00000e32:00002f7e:0000093e:00000d7e:000010e9:00001205:000010de:00000e1a:00000fda:00000dbe:00000e02:00000dce:00001211:00000e9b:00000dc5:00000ee6:00000c0b:00000d52:00000eb1:00000e11:00000e7a:00000bb0:00000eb1:00000ffd:00001178:00004fc4:000005d1:00000912:00000b4e:00000c9b:00000de3:00000bc0:00001060:0000156e:00000632:000007ba:000009ff:00000838:000009e0:0000094a:000008e3:00000a05:00000bb9:00000b15:00000d14:00000b4c:00000b4b:00000c95:00000d49:00000b8c:00000bc1:000009fc:000008f5:0000075b:000008cb:00000757:00000a16:00000666:00000671:00000539:000005d3:0000066b:00000446:00000580:00001252:000006d5:000008a6:00000788:0000061e:00000655:000006d7:000006f5:00000714:00000670:00000869:000019ba:00000355:000004d3:0000057d:00000736:000005e1:00000673:00000616:000006b4:00000723:000008d5:000006b9:00000788:0000083a:00001862:00000a10:0000097e:00000b47:00000b8c:00000aeb:00000b17:00000abf:00000a23:00000a8b:00000b5d:00000a1c:0000098a:00000992:000009b3:000009a8:00000973:00000b25:00000a2d:00000a83:00000b16:000009f7:00000b9a:00000afa:00000aa1:00000aa9:00000baa:00000b69:00000a14:00000b7d:00000b65:00000af1:00000b06:00000ade:00000acf:00000a72:0000296a:000007d5:000008a2:00000a61:00000b3e:00000ade:00000b62:00000c3a:00000b9e:00000c70:00000c3c:00000bfc:00000c0f:0000127a:00000c8b:00000d00:000009d6:000008d5:000004d1:00000b96:00000ed4:000010d5:00001791:000005b8:00000656:0000079b:00000a0d:000005f4:000007fc:000007ef:000008ed:00000829:000008f1:00000965:00000a00:00000797:00000854:0000086a:0000084a:00000855:00000876:0000072c:00000842:00000840:00000861:0000076f:00000847:00000877:000008fa:000007a4:00003610:00000298:000005df:000005d6:0000088e:00000775:000006a0:0000089c:00000637:00000879:0000051b:00000835:0000092c:0000072a:00000769:00000709:000007b8:00000854:0000089e:00000793:000014f4:00001341:0000141c:000012e7:000013fe:00001458:000014d4:000016a8:000016c1:0000184d:00001892:0000177d:00001a26:00001c70:00001d27:00001e28:00001f93:00002005:00002234:00002325:00001fea:00002011:00002027:00001fdc:000038c4:0000204d:00002291:000022c8:000021fd:0000218b:00002197:00002094:0000204b:00002149:000020bd:000020f6:000020de:0000213f:00002177:000021ea:00002114:000022c8:00002335:00002305:00002447:00002684:000023ca:000016bb:000004f8:0000098d:000008b6:00000a63:000009bc:00000990:0000095e:000009b0:00000a4f:00000987:000009d7:00000984:00000931:000009ca:0000095d:00000986:00000a60:000009fd:00000b54:00000ba4:00000b5a:00000a56:00000ab8:00000b35:00000abc:00000b7a:00000bfb:00000bab:00002d1c:00001674:00000917:00000b70:00000d30:00000b65:00000b2f:00000b91:00000adb:00000c9b:00000cfd:00000c71:00000aa5:00000cc7:00000c0d:00000b53:00000c0e:00000b7d:00000af6:00000c12:00000a67:00000a4b:00000be2:00000b48:00000c9e:00000a45:00000bfb:00000b5a:00000b30:00000a69:00000a3f:0000230e:00001d72:00001c5c:00001d4a:00001d0c:00001c51:00001cd9:00001db0:00001cd9:00001d68:00001dc4:00001e87:00001ec3:00001f1c:00001ee5:000037bb:00001a7f:00001a9b:00001e79:000025c7:00000ffc:0000123b:00001313:000013f1:000015c2:00001752:0000182d:00001966:00001af5:00001a90:00001b70:00001ceb:00001928:00001a72:00001a88:00001958:00001852:00001bcd:00001ac5:0000197f:000018d0:000017aa:0000176e:000016d6:0000158e:00001608:00001607:0000160e:00001688:000016f9:0000163d:00001580:000014f0:00001421:000013a8:000013b8:000013f3:0000150c:00001524:0000159f:0000168b:0000182f:00001a2d:00001a32:00001ae6:0000198a:00001b07:00001c11:00001b4b:000036a7:000018b1:000017fd:000017c3:00001a03:0000188f:00001867:0000256a:0000200a:000020ab:000020de:00002023:00002032:000020a3:00001f46:00001dfa:00001cad:00001bd7:00001c12:00001c10:00001c66:00001c29:00001cd8:00001c72:00001ca4:00001caf:00001ceb:00001e0a:00001bac:00001ca3:00001d21:00001d76:00001e5f:00001f62:00002059:00001d47:0000060b:00000601:0000099c:00000b13:00000b95:00000a8d:00000af6:000009c6:00000a43:00000a7d:00000ab7:00000ad7:00000b5d:00000a9b:0000326a:00000322:000006e3:00000981:000008f4:00000819:00000bc4:00000a07:00000959:000009e6:00000a1e:000008f8:0000096e:000009fe:000009ac:00000a1c:000009c7:000008ff:00000ad7:00000a86:0000097e:00000939:000009d2:00000aaf:00000977:00000941:0000090b:000009da:00000a1b:00000921:0000091c:00000909:000007fb:0000088f:00000a7c:0000086b:00000962:000009ba:000008bd:000008ac:0000378f:0000032f:000005e2:00000927:000008a2:0000095d:000009de:00000cc0:00000b0c:00000bd0:000035ca:0000052c:000008ba:00000796:00000a80:00000a89:0000097e:000008a4:00000949:00000837:00000ae9:00000a95:0000093b:0000099d:00000a74:00000b27:000009f5:00000acb:0000095f:00000d1d:000005d2:0000062a:00000803:000007ec:00000774:00000938:000009e0:00000991:00000a71:00000866:00000633:00000689:00000646:000003a0:000004cb:00000366:00000377:000003ef:0000033f:0000039c:0000035a:000003bb:000003ad:000003a3:000002e0:00000359:00001fc1:0000062b:0000077a:00000762:000018dd:00000518:000007e5:00000798:0000081b:000007de:000007a5:00000774:00000841:000008cb:000008de:00000933:00000a0a:00000925:00000a3d:00000ad7:00000a3f:00000a21:0000085d:00000730:00000740:00000874:00000844:000011b5:000003ee:00000484:000006dc:00000683:0000056a:00000552:000005fa:0000062e:000005b1:00000650:0000058b:00000512:000005d3:00000591:00000552:000005de:00000562:00000588:000005f9:00000542:000005a8:0000061c:00000594:0000057b:000006bd:00000697:00002117:0000030e:000004ba:000004c2:00000736:0000069b:0000062c:00000707:0000064f:000005ae:00000db3:00000627:0000068b:000006f9:000006b8:000008a5:00000841:000006dd:00000943:00000a2f:00000906:00000859:00000898:000008ad:000008b7:00000885:00000854:00000839:000008a6:000008a2:0000097c:00000a2a:00000969:00001fa4:00000ae2:0000097f:00000e2f:00000cb0:00000c81:00000c4c:00000c15:00000e88:00000d72:00000d3d:00000cf3:00000d1b:00000dcd:00000d40:00000c81:00000ca3:00003782:0000052d:0000096c:00000983:00000a7f:00000d5d:00000bae:00000bb2:000015a9:00000614:00000746:000008d0:00000828:000007be:000009a8:00000707:00000687:00000804:000007b7:000007ff:00000892:00000877:00000809:0000084c:000007ac:00000790:00000807:0000070f:00000922:000008cc:000008bb:000007e2:000008dc:000006c2:000006bc:000007a3:00000944:00000689:00000812:000007b8:00000693:00003ba2:00000a2a:00000d32:00000dbd:00000f49:00000f39:00000fc9:00000ff3:00000fce:00002f20:00000bee:00000df8:00000e8f:00000ebf:00000f49:00001188:00000fa4:00000f15:0000100d:00000f9e:00000f9c:00001081:00000fed:00001095:000010ae:000010be:00001016:00001009:00000f6c:00001060:00000ee6:00000e76:00000f3e:00000fde:00000e44:00000e6b:00000ee5:00000ebb:00000f1b:00000e96:00000eb1:00000dad:00001cc9:00000535:00000714:0000093d:00000762:00000979:0000094c:00000868:00000869:00000878:000008fd:00000a48:00000aa9:0000089a:000007c7:00000922:000005c6:00002ff0:0000036e:000005d7:0000108d:00000696:00000695:00000745:0000072d:0000068c:0000081c:000007d4:0000081f:00000864:000017c9:00000bd5:00000cf7:00000d33:00000e78:00001081:00000f0e:000010c3:00000e09:00000d03:00000e5d:00000e10:00000e5f:00000d32:00000ce2:00000ce4:00000ce2:00000d15:00000e0f:000011d0:000007b8:0000079d:0000080e:000008af:0000081c:000007bf:0000081b:000007a1:000007a1:000007ec:000007d6:000007cd:00000789:000006ed:000006cf:00000835:0000080f:00001d29:000004b6:00000703:00000796:00000853:0000080d:0000079b:00000802:00000778:0000070e:0000164a:00000733:00000883:000007b5:000008b6:00000837:00000a1d:00000896:00000836:000008c8:0000092a:000007fd:000008e4:0000092b:00000856:00000827:000008bf:0000093d:000008e3:00000942:0000091b:00000858:00000837:000007a3:00002171:000007b8:00000a8d:00000ccf:00000c6a:00000be7:00000c95:00000cb2:00000c4b:00000ccd:00000d21:00000bb8:00000c62:00000c31:00000bbf:00000c28:000033d9:00000599:00000949:00000bf8:00000abb:00000b76:00000bd8:00000d9e:00000cac:00001cd2:0000059c:0000085a:00000a76:0000092c:000008df:00000929:00000a5c:000009ad:00000800:00000899:000009fc:000009c0:0000082b:00000921:0000089f:000008d1:00000850:00000890:00000824:000009b1:00000953:0000096e:000007c0:00000989:000008df:000009da:000009e1:00000986:00000905:00001a1f:0000055a:000008ad:00000b89:00000bcd:00000910:00000a7c:00000ae7:00000ad5:00000979:00000a1c:00002f37:0000041a:000006cc:00000818:00000b6a:000008af:00000816:000009c8:00000776:00000940:000009b5:00000767:00002981:000004f5:00000792:00000a2a:000009c3:000009c2:00000a13:00000b38:00000a0e:00000a0c:000009f5:00000c8e:00000b57:000009c7:00000a68:00000d71:000009d4:00000ca7:00000b9a:00000a33:00000ca7:0000091a:00000b76:00000d16:00002143:000006aa:00000974:00000bfc:00000aee:00000a1b:00000aac:00000ac9:000009ff:00000af1:00000c06:00000a37:00000b27:00000c4e:00003a47:00000434:00000785:000009e2:00000932:00000a8f:000009cf:00000a71:00000ad1:00000b2d:00000aa6:00000bf1:00000b18:00000aa2:00000ca1:00000aef:0000099f:00000af0:00000952:000009fa:00000b85:000009f1:00000a3c:00000baa:0000095a:00000a0b:00000a24:00001347:00000712:00000800:0000082f:00000a15:00000957:00000951:00000954:00000917:00000843:000007d5:000008a5:00000703:00000652:000016c0:000005fb:000006ff:00000788:0000073e:00000792:0000088f:00000863:00000905:000018f9:0000055b:000007d5:00000961:00000915:000009e0:00000a78:000009ee:00000a3a:00000a52:00000980:000008b3:0000098a:000007ee:0000084a:00000860:00000846:00000868:00000866:000007ee:000007fd:000008e2:00000819:00000790:00000757:00000873:00001eab:00000923:00000c39:00000a44:00000a9a:00000c94:00000b95:00000bee:00000b4d:00000c1e:00000d28:000009d1:00000a99:00000bcc:00000b9a:00000d53:00000d44:00000c5c:00000c3e:00000cfa:00000b9b:00000b95:00000ab0:00003c65:00002973:000007e7:00000b4c:00000c05:00000b8f:00000bf5:00000c52:00000d30:00000e71:00000f05:0000102f:00000be8:00000bad:00000bb6:00000b68:00000c55:00000d1a:00000c5a:00000ce8:00000cce:00000b76:00000b62:00000b83:00000a55:00000c5d:00000b43:00000ab2:00000a04:00001903:0000096a:00000a66:00000c7f:00000b9d:00000be0:00000bb2:00000b2d:00000afa:00000af5:00000a08:000009b2:00000a33:00000960:00000c71:00000aa2:00000a9b:00000bbf:00000c8e:00000be5:00000bc6:00000b8b:00002459:0000057c:000008d8:0000086c:00000a7b:00000a97:00000a03:000009b9:00000a31:00000954:00000924:00000933:00000899:00000885:0000084e:00000956:0000085b:00000810:00000860:00000817:00000816:00001285:000004df:00000778:0000063e:000007c0:0000077f:00000670:000005a8:0000070c:00000624:000005da:00000762:0000073c:00000648:000006e5:0000061c:000006bb:00000711:0000064c:0000058f:000006da:0000066d:00000594:00000650:00002b86:000008cf:00000db4:00000e10:00000e48:0000263e:00000a26:00000da6:00000e56:00000c2a:00000d81:00000ee5:00000faa:000010dd:00000f60:00000ed2:00000e04:00000f5b:00002004:00000b8f:00000cb1:00000eed:00000eba:00000f82:00000e26:00001002:00001004:00000e4f:00000ea3:00000f1c:00000e4e:00000ef1:00000f88:00000e55:00000e7c:00000f16:00000e20:00000ebf:00000e6c:00000d5e:00000e63:00000f00:00000e37:00000e4e:00000ee2:00000df9:00000da9:0000117e:00000eab:00000f76:00001023:00000f8c:00000dee:00000fce:00000e9f:00002ff6:00000a2d:00000bdd:00000f27:00000f3b:00000ea9:00000df2:00000e9f:00000d74:00000e62:00000f53:00000e8a:00000e62:00000f49:00000e37:00000f18:00000f1b:00000dc3:00000df8:00000f19:00000d9b:00000e91:00000f5b:00000e33:00000ef9:00000f49:00000e02:00000e61:00000f3f:00000eaf:00000f0b:00000f2c:00000dc9:00000db9:00000dca:0000101a:00000f06:0000101f:00000cf9:00000dbd:00000eef:00000d77:00000de9:00000eb1:00000d0f:00000e10:00000f1a:00000e4b:00000ee0:00000f44:00002e55:00000961:00000d93:00000c39:00000ca3:00000d83:00000f3d:00000eca:00000dd6:00000de2:00000db5:00001abe:00000cb0:00000d4d:00000e07:00000f94:00000f34:00000ec1:00000e36:00000e70:00000e62:00000d2c:00000cf9:00000d6e:00000bb6:00000bf5:00000bd2:00000a2f:00000bb7:00001fe4:00000a6f:00000e98:00000fb7:00000dd7:00000edd:00000fdc:00000df4:00000e8c:00000fa8:00000e98:00000eeb:00001006:00000e67:00000e6c:00000f81:000010d2:00000f41:000011f0:00000f2a:00000fd8:000037a0:0000083e:00000cba:00000e75:00000e1c:00000e93:00000ecd:00000d78:00000e80:000010aa:00000de2:00001e10:000006ac:00000988:00000a3c:000009b6:0000087f:00000997:00000ba4:00000af7:00000c98:000009da:00000ad5:000007b0:000008c1:00001fbd:00000821:00000baa:00000a8c:00000d67:00000cbf:00000c17:00000cdb:00000bdf:00000b9f:00000bf0:00000d0a:00000bcc:00000bc6:00000c8c:00000cd3:00000b3c:00000bbd:00000b43:00000b78:00000c14:00001f0f:00000682:000007f0:00000a25:00001cd8:000006e9:00000862:00000891:00000909:000009bc:00000966:000008ec:00000958:00000951:00000982:00000a63:000008ff:000009e6:000010ba:000006f9:00000909:00000711:0000074c:0000076e:00000723:00000714:00000712:00000717:00000700:00000738:0000083a:00000803:0000073f:000007e3:00000743:000007a0:00000680:00000727:0000073a:00000793:000007b1:00000899:000007eb:00000956:000009a2:000009c2:00000919:00000894:00000795:00000902:000008c8:00000875:0000086a:0000074f:00001a12:000003f2:00000676:00000658:000006e8:0000081c:000007fa:00000789:000007f0:00000708:00000979:00000742:00000769:0000075c:000007ed:000007c9:0000081e:000008ab:0000082d:00001d3c:00000744:00000939:000007f6:0000090c:0000075a:00000c64:00000b7a:00000884:00000887:00000bb4:0000096b:00000843:00000ace:00000956:0000086d:00000a5c:000008e3:00000876:000007f7:00000bab:0000095a:000008f6:000007ec:00000809:000009eb:000009c3:0000088a:000009ab:000008f9:00000889:000030e8:000003f2:00000580:00000841:000007fa:00000864:00000918:000008e1:00000848:00000a4c:00000aef:0000092e:00000a43:00000993:000009e2:00000a4f:000009d5:00001953:00000be7:00000c4c:00000c96:00000c26:00000bbd:00000e64:00000dbc:00000c06:00000cce:00000ae6:00000b49:00000b00:00000ba6
+5: co64 :00000000:000005c4:00000000:00000010:00000000:0000290f:00000000:0000317e:00000000:00003b29:00000000:000048bd:00000000:000055ec:00000000:00006222:00000000:00006dc0:00000000:00007ad3:00000000:0000880d:00000000:00009432:00000000:0000a14b:00000000:0000afd8:00000000:0000bc4d:00000000:0000c9b9:00000000:0000d75c:00000000:0000e58d:00000000:0000f1ab:00000000:0000fdf5:00000000:00010cd8:00000000:00011af8:00000000:0001270d:00000000:000133c5:00000000:000140a2:00000000:00014e99:00000000:00015b2c:00000000:0001695e:00000000:000198dc:00000000:0001a21a:00000000:0001af98:00000000:0001c081:00000000:0001d286:00000000:0001e364:00000000:0001f17e:00000000:00020158:00000000:00020f16:00000000:00021d18:00000000:00022ae6:00000000:00023cf7:00000000:00024b92:00000000:00025957:00000000:0002683d:00000000:00027448:00000000:0002819a:00000000:0002904b:00000000:00029e5c:00000000:0002acd6:00000000:0002b886:00000000:0002c737:00000000:0002d734:00000000:0002e8ac:00000000:00033870:00000000:00033e41:00000000:00034753:00000000:000352a1:00000000:00035f3c:00000000:00036d1f:00000000:000378df:00000000:0003893f:00000000:00039ead:00000000:0003a4df:00000000:0003ac99:00000000:0003b698:00000000:0003bed0:00000000:0003c8b0:00000000:0003d1fa:00000000:0003dadd:00000000:0003e4e2:00000000:0003f09b:00000000:0003fbb0:00000000:000408c4:00000000:00041410:00000000:00041f5b:00000000:00042bf0:00000000:00043939:00000000:000444c5:00000000:00045086:00000000:00045a82:00000000:00046377:00000000:00046ad2:00000000:0004739d:00000000:00047af4:00000000:0004850a:00000000:00048b70:00000000:000491e1:00000000:0004971a:00000000:00049ced:00000000:0004a358:00000000:0004a79e:00000000:0004ad1e:00000000:0004bf70:00000000:0004c645:00000000:0004ceeb:00000000:0004d673:00000000:0004dc91:00000000:0004e2e6:00000000:0004e9bd:00000000:0004f0b2:00000000:0004f7c6:00000000:0004fe36:00000000:0005069f:00000000:00052059:00000000:000523ae:00000000:00052881:00000000:00052dfe:00000000:00053534:00000000:00053b15:00000000:00054188:00000000:0005479e:00000000:00054e52:00000000:00055575:00000000:00055e4a:00000000:00056503:00000000:00056c8b:00000000:000574c5:00000000:00058d27:00000000:00059737:00000000:0005a0b5:00000000:0005abfc:00000000:0005b788:00000000:0005c273:00000000:0005cd8a:00000000:0005d849:00000000:0005e26c:00000000:0005ecf7:00000000:0005f854:00000000:00060270:00000000:00060bfa:00000000:0006158c:00000000:00061f3f:00000000:000628e7:00000000:0006325a:00000000:00063d7f:00000000:000647ac:00000000:0006522f:00000000:00065d45:00000000:0006673c:00000000:000672d6:00000000:00067dd0:00000000:00068871:00000000:0006931a:00000000:00069ec4:00000000:0006aa2d:00000000:0006b441:00000000:0006bfbe:00000000:0006cb23:00000000:0006d614:00000000:0006e11a:00000000:0006ebf8:00000000:0006f6c7:00000000:00070139:00000000:00072aa3:00000000:00073278:00000000:00073b1a:00000000:0007457b:00000000:000750b9:00000000:00075b97:00000000:000766f9:00000000:00077333:00000000:00077ed1:00000000:00078b41:00000000:0007977d:00000000:0007a379:00000000:0007af88:00000000:0007c202:00000000:0007ce8d:00000000:0007db8d:00000000:0007e563:00000000:0007ee38:00000000:0007f309:00000000:0007fe9f:00000000:00080d73:00000000:00081e48:00000000:000835d9:00000000:00083b91:00000000:000841e7:00000000:00084982:00000000:0008538f:00000000:00085983:00000000:0008617f:00000000:0008696e:00000000:0008725b:00000000:00087a84:00000000:00088375:00000000:00088cda:00000000:000896da:00000000:00089e71:00000000:0008a6c5:00000000:0008af2f:00000000:0008b779:00000000:0008bfce:00000000:0008c844:00000000:0008cf70:00000000:0008d7b2:00000000:0008dff2:00000000:0008e853:00000000:0008efc2:00000000:0008f809:00000000:00090080:00000000:0009097a:00000000:0009111e:00000000:0009472e:00000000:000949c6:00000000:00094fa5:00000000:0009557b:00000000:00095e09:00000000:0009657e:00000000:00096c1e:00000000:000974ba:00000000:00097af1:00000000:0009836a:00000000:00098885:00000000:000990ba:00000000:000999e6:00000000:0009a110:00000000:0009a879:00000000:0009af82:00000000:0009b73a:00000000:0009bf8e:00000000:0009c82c:00000000:0009cfbf:00000000:0009e4b3:00000000:0009f7f4:00000000:000a0c10:00000000:000a1ef7:00000000:000a32f5:00000000:000a474d:00000000:000a5c21:00000000:000a72c9:00000000:000a898a:00000000:000aa1d7:00000000:000aba69:00000000:000ad1e6:00000000:000aec0c:00000000:000b087c:00000000:000b25a3:00000000:000b43cb:00000000:000b635e:00000000:000b8363:00000000:000ba597:00000000:000bc8bc:00000000:000be8a6:00000000:000c08b7:00000000:000c28de:00000000:000c48ba:00000000:000c817e:00000000:000ca1cb:00000000:000cc45c:00000000:000ce724:00000000:000d0921:00000000:000d2aac:00000000:000d4c43:00000000:000d6cd7:00000000:000d8d22:00000000:000dae6b:00000000:000dcf28:00000000:000df01e:00000000:000e10fc:00000000:000e323b:00000000:000e53b2:00000000:000e759c:00000000:000e96b0:00000000:000eb978:00000000:000edcad:00000000:000effb2:00000000:000f23f9:00000000:000f4a7d:00000000:000f6e47:00000000:000f8502:00000000:000f89fa:00000000:000f9387:00000000:000f9c3d:00000000:000fa6a0:00000000:000fb05c:00000000:000fb9ec:00000000:000fc34a:00000000:000fccfa:00000000:000fd749:00000000:000fe0d0:00000000:000feaa7:00000000:000ff42b:00000000:000ffd5c:00000000:00100726:00000000:00101083:00000000:00101a09:00000000:00102469:00000000:00102e66:00000000:001039ba:00000000:0010455e:00000000:001050b8:00000000:00105b0e:00000000:001065c6:00000000:001070fb:00000000:00107bb7:00000000:00108731:00000000:0010932c:00000000:00109ed7:00000000:0010cbf3:00000000:0010e267:00000000:0010eb7e:00000000:0010f6ee:00000000:0011041e:00000000:00110f83:00000000:00111ab2:00000000:00112643:00000000:0011311e:00000000:00113db9:00000000:00114ab6:00000000:00115727:00000000:001161cc:00000000:00116e93:00000000:00117aa0:00000000:001185f3:00000000:00119201:00000000:00119d7e:00000000:0011a874:00000000:0011b486:00000000:0011beed:00000000:0011c938:00000000:0011d51a:00000000:0011e062:00000000:0011ed00:00000000:0011f745:00000000:00120340:00000000:00120e9a:00000000:001219ca:00000000:00122433:00000000:00122e72:00000000:00125180:00000000:00126ef2:00000000:00128b4e:00000000:0012a898:00000000:0012c5a4:00000000:0012e1f5:00000000:0012fece:00000000:00131c7e:00000000:00133957:00000000:001356bf:00000000:00137483:00000000:0013930a:00000000:0013b1cd:00000000:0013d0e9:00000000:0013efce:00000000:00142789:00000000:00144208:00000000:00145ca3:00000000:00147b1c:00000000:0014a0e3:00000000:0014b0df:00000000:0014c31a:00000000:0014d62d:00000000:0014ea1e:00000000:0014ffe0:00000000:00151732:00000000:00152f5f:00000000:001548c5:00000000:001563ba:00000000:00157e4a:00000000:001599ba:00000000:0015b6a5:00000000:0015cfcd:00000000:0015ea3f:00000000:001604c7:00000000:00161e1f:00000000:00163671:00000000:0016523e:00000000:00166d03:00000000:00168682:00000000:00169f52:00000000:0016b6fc:00000000:0016ce6a:00000000:0016e540:00000000:0016face:00000000:001710d6:00000000:001726dd:00000000:00173ceb:00000000:00175373:00000000:00176a6c:00000000:001780a9:00000000:00179629:00000000:0017ab19:00000000:0017bf3a:00000000:0017d2e2:00000000:0017e69a:00000000:0017fa8d:00000000:00180f99:00000000:001824bd:00000000:00183a5c:00000000:001850e7:00000000:00186916:00000000:00188343:00000000:00189d75:00000000:0018b85b:00000000:0018d1e5:00000000:0018ecec:00000000:001908fd:00000000:00192448:00000000:00195aef:00000000:001973a0:00000000:00198b9d:00000000:0019a360:00000000:0019bd63:00000000:0019d5f2:00000000:0019ee59:00000000:001a13c3:00000000:001a33cd:00000000:001a5478:00000000:001a7556:00000000:001a9579:00000000:001ab5ab:00000000:001ad64e:00000000:001af594:00000000:001b138e:00000000:001b303b:00000000:001b4c12:00000000:001b6824:00000000:001b8434:00000000:001ba09a:00000000:001bbcc3:00000000:001bd99b:00000000:001bf60d:00000000:001c12b1:00000000:001c2f60:00000000:001c4c4b:00000000:001c6a55:00000000:001c8601:00000000:001ca2a4:00000000:001cbfc5:00000000:001cdd3b:00000000:001cfb9a:00000000:001d1afc:00000000:001d3b55:00000000:001d589c:00000000:001d5ea7:00000000:001d64a8:00000000:001d6e44:00000000:001d7957:00000000:001d84ec:00000000:001d8f79:00000000:001d9a6f:00000000:001da435:00000000:001dae78:00000000:001db8f5:00000000:001dc3ac:00000000:001dce83:00000000:001dd9e0:00000000:001de47b:00000000:001e16e5:00000000:001e1a07:00000000:001e20ea:00000000:001e2a6b:00000000:001e335f:00000000:001e3b78:00000000:001e473c:00000000:001e5143:00000000:001e5a9c:00000000:001e6482:00000000:001e6ea0:00000000:001e7798:00000000:001e8106:00000000:001e8b04:00000000:001e94b0:00000000:001e9ecc:00000000:001ea893:00000000:001eb192:00000000:001ebc69:00000000:001ec6ef:00000000:001ed06d:00000000:001ed9a6:00000000:001ee378:00000000:001eee27:00000000:001ef79e:00000000:001f00df:00000000:001f09ea:00000000:001f13c4:00000000:001f1ddf:00000000:001f2700:00000000:001f301c:00000000:001f3925:00000000:001f4120:00000000:001f49af:00000000:001f542b:00000000:001f5c96:00000000:001f65f8:00000000:001f6fb2:00000000:001f786f:00000000:001f811b:00000000:001fb8aa:00000000:001fbbd9:00000000:001fc1bb:00000000:001fcae2:00000000:001fd384:00000000:001fdce1:00000000:001fe6bf:00000000:001ff37f:00000000:001ffe8b:00000000:00200a5b:00000000:00204025:00000000:00204551:00000000:00204e0b:00000000:002055a1:00000000:00206021:00000000:00206aaa:00000000:00207428:00000000:00207ccc:00000000:00208615:00000000:00208e4c:00000000:00209935:00000000:0020a3ca:00000000:0020ad05:00000000:0020b6a2:00000000:0020c116:00000000:0020cc3d:00000000:0020d632:00000000:0020e0fd:00000000:0020ea5c:00000000:0020f779:00000000:0020fd4b:00000000:00210375:00000000:00210b78:00000000:00211364:00000000:00211ad8:00000000:00212410:00000000:00212df0:00000000:00213781:00000000:002141f2:00000000:00214a58:00000000:0021508b:00000000:00215714:00000000:00215d5a:00000000:002160fa:00000000:002165c5:00000000:0021692b:00000000:00216ca2:00000000:00217091:00000000:002173d0:00000000:0021776c:00000000:00217ac6:00000000:00217e81:00000000:0021822e:00000000:002185d1:00000000:002188b1:00000000:00218c0a:00000000:0021abcb:00000000:0021b1f6:00000000:0021b970:00000000:0021c0d2:00000000:0021d9af:00000000:0021dec7:00000000:0021e6ac:00000000:0021ee44:00000000:0021f65f:00000000:0021fe3d:00000000:002205e2:00000000:00220d56:00000000:00221597:00000000:00221e62:00000000:00222740:00000000:00223073:00000000:00223a7d:00000000:002243a2:00000000:00224ddf:00000000:002258b6:00000000:002262f5:00000000:00226d16:00000000:00227573:00000000:00227ca3:00000000:002283e3:00000000:00228c57:00000000:0022949b:00000000:0022a650:00000000:0022aa3e:00000000:0022aec2:00000000:0022b59e:00000000:0022bc21:00000000:0022c18b:00000000:0022c6dd:00000000:0022ccd7:00000000:0022d305:00000000:0022d8b6:00000000:0022df06:00000000:0022e491:00000000:0022e9a3:00000000:0022ef76:00000000:0022f507:00000000:0022fa59:00000000:00230037:00000000:00230599:00000000:00230b21:00000000:0023111a:00000000:0023165c:00000000:00231c04:00000000:00232220:00000000:002327b4:00000000:00232d2f:00000000:002333ec:00000000:00233a83:00000000:00235b9a:00000000:00235ea8:00000000:00236362:00000000:00236824:00000000:00236f5a:00000000:002375f5:00000000:00237c21:00000000:00238328:00000000:00238977:00000000:00238f25:00000000:00239cd8:00000000:0023a2ff:00000000:0023a98a:00000000:0023b083:00000000:0023b73b:00000000:0023bfe0:00000000:0023c821:00000000:0023cefe:00000000:0023d841:00000000:0023e270:00000000:0023eb76:00000000:0023f3cf:00000000:0023fc67:00000000:00240514:00000000:00240dcb:00000000:00241650:00000000:00241ea4:00000000:002426dd:00000000:00242f83:00000000:00243825:00000000:002441a1:00000000:00244bcb:00000000:00245534:00000000:002474d8:00000000:00247fba:00000000:00248939:00000000:00249768:00000000:0024a418:00000000:0024b099:00000000:0024bce5:00000000:0024c8fa:00000000:0024d782:00000000:0024e4f4:00000000:0024f231:00000000:0024ff24:00000000:00250c3f:00000000:00251a0c:00000000:0025274c:00000000:002533cd:00000000:00254070:00000000:002577f2:00000000:00257d1f:00000000:0025868b:00000000:0025900e:00000000:00259a8d:00000000:0025a7ea:00000000:0025b398:00000000:0025bf4a:00000000:0025d4f3:00000000:0025db07:00000000:0025e24d:00000000:0025eb1d:00000000:0025f345:00000000:0025fb03:00000000:002604ab:00000000:00260bb2:00000000:00261239:00000000:00261a3d:00000000:002621f4:00000000:002629f3:00000000:00263285:00000000:00263afc:00000000:00264305:00000000:00264b51:00000000:002652fd:00000000:00265a8d:00000000:00266294:00000000:002669a3:00000000:002672c5:00000000:00267b91:00000000:0026844c:00000000:00268c2e:00000000:0026950a:00000000:00269bcc:00000000:0026a288:00000000:0026aa2b:00000000:0026b36f:00000000:0026b9f8:00000000:0026c20a:00000000:0026c9c2:00000000:0026d055:00000000:00270bf7:00000000:00271621:00000000:00272353:00000000:00273110:00000000:00274059:00000000:00274f92:00000000:00275f5b:00000000:00276f4e:00000000:00277f1c:00000000:0027ae3c:00000000:0027ba2a:00000000:0027c822:00000000:0027d6b1:00000000:0027e570:00000000:0027f4b9:00000000:00280641:00000000:002815e5:00000000:002824fa:00000000:00283507:00000000:002844a5:00000000:00285441:00000000:002864c2:00000000:002874af:00000000:00288544:00000000:002895f2:00000000:0028a6b0:00000000:0028b6c6:00000000:0028c6cf:00000000:0028d63b:00000000:0028e69b:00000000:0028f581:00000000:002903f7:00000000:00291335:00000000:00292313:00000000:00293157:00000000:00293fc2:00000000:00294ea7:00000000:00295d62:00000000:00296c7d:00000000:00297b13:00000000:002989c4:00000000:00299771:00000000:0029b43a:00000000:0029b96f:00000000:0029c083:00000000:0029c9c0:00000000:0029d122:00000000:0029da9b:00000000:0029e3e7:00000000:0029ec4f:00000000:0029f4b8:00000000:0029fd30:00000000:002a062d:00000000:002a1075:00000000:002a1b1e:00000000:002a23b8:00000000:002a2b7f:00000000:002a34a1:00000000:002a3a67:00000000:002a6a57:00000000:002a6dc5:00000000:002a739c:00000000:002a8429:00000000:002a8abf:00000000:002a9154:00000000:002a9899:00000000:002a9fc6:00000000:002aa652:00000000:002aae6e:00000000:002ab642:00000000:002abe61:00000000:002ac6c5:00000000:002ade8e:00000000:002aea63:00000000:002af75a:00000000:002b048d:00000000:002b1305:00000000:002b2386:00000000:002b3294:00000000:002b4357:00000000:002b5160:00000000:002b5e63:00000000:002b6cc0:00000000:002b7ad0:00000000:002b892f:00000000:002b9661:00000000:002ba343:00000000:002bb027:00000000:002bbd09:00000000:002bca1e:00000000:002bd82d:00000000:002be9fd:00000000:002bf1b5:00000000:002bf952:00000000:002c0160:00000000:002c0a0f:00000000:002c122b:00000000:002c19ea:00000000:002c2205:00000000:002c3147:00000000:002c3933:00000000:002c4109:00000000:002c48d6:00000000:002c505f:00000000:002c574c:00000000:002c5e1b:00000000:002c6650:00000000:002c6e5f:00000000:002c8b88:00000000:002c903e:00000000:002c9741:00000000:002c9ed7:00000000:002ca72a:00000000:002caf37:00000000:002cb6d2:00000000:002cbed4:00000000:002cc64c:00000000:002ccd5a:00000000:002ce3a4:00000000:002cead7:00000000:002cf35a:00000000:002cfb0f:00000000:002d03c5:00000000:002d0bfc:00000000:002d1619:00000000:002d1eaf:00000000:002d26e5:00000000:002d2fad:00000000:002d38d7:00000000:002d40d4:00000000:002d49b8:00000000:002d52e3:00000000:002d5b39:00000000:002d6360:00000000:002d6c1f:00000000:002d755c:00000000:002d7e3f:00000000:002d8781:00000000:002d909c:00000000:002d98f4:00000000:002da12b:00000000:002da8ce:00000000:002dca3f:00000000:002dd1f7:00000000:002ddc84:00000000:002de953:00000000:002df5bd:00000000:002e01a4:00000000:002e0e39:00000000:002e1aeb:00000000:002e2736:00000000:002e3403:00000000:002e4124:00000000:002e4cdc:00000000:002e593e:00000000:002e656f:00000000:002e712e:00000000:002e7d56:00000000:002eb12f:00000000:002eb6c8:00000000:002ec011:00000000:002ecc09:00000000:002ed6c4:00000000:002ee23a:00000000:002eee12:00000000:002efbb0:00000000:002f085c:00000000:002f252e:00000000:002f2aca:00000000:002f3324:00000000:002f3d9a:00000000:002f46c6:00000000:002f4fa5:00000000:002f58ce:00000000:002f632a:00000000:002f6cd7:00000000:002f74d7:00000000:002f7d70:00000000:002f876c:00000000:002f912c:00000000:002f9957:00000000:002fa278:00000000:002fab17:00000000:002fb3e8:00000000:002fbc38:00000000:002fc4c8:00000000:002fccec:00000000:002fd69d:00000000:002fdff0:00000000:002fe95e:00000000:002ff11e:00000000:002ffaa7:00000000:00300386:00000000:00300d60:00000000:00301741:00000000:003020c7:00000000:003029cc:00000000:003043eb:00000000:00304945:00000000:003051f2:00000000:00305d7b:00000000:00306948:00000000:00307258:00000000:00307cd4:00000000:003087bb:00000000:00309290:00000000:00309c09:00000000:0030a625:00000000:0030d55c:00000000:0030d976:00000000:0030e042:00000000:0030e85a:00000000:0030f3c4:00000000:0030fc73:00000000:00310489:00000000:00310e51:00000000:003115c7:00000000:00311f07:00000000:003128bc:00000000:00313023:00000000:003159a4:00000000:00315e99:00000000:0031662b:00000000:00317055:00000000:00317a18:00000000:003183da:00000000:00318ded:00000000:00319925:00000000:0031a333:00000000:0031ad3f:00000000:0031b734:00000000:0031c3c2:00000000:0031cf19:00000000:0031d8e0:00000000:0031e348:00000000:0031f0b9:00000000:0031fa8d:00000000:00320734:00000000:003212ce:00000000:00321d01:00000000:003229a8:00000000:003232c2:00000000:00323e38:00000000:00324b4e:00000000:00326c91:00000000:0032733b:00000000:00327caf:00000000:003288ab:00000000:00329399:00000000:00329db4:00000000:0032a860:00000000:0032b329:00000000:0032bd28:00000000:0032c819:00000000:0032d41f:00000000:0032de56:00000000:0032e97d:00000000:0032f5cb:00000000:00333012:00000000:00333446:00000000:00333bcb:00000000:003345ad:00000000:00334edf:00000000:0033596e:00000000:0033633d:00000000:00336dae:00000000:0033787f:00000000:003383ac:00000000:00338e52:00000000:00339a43:00000000:0033a55b:00000000:0033affd:00000000:0033bc9e:00000000:0033c78d:00000000:0033d12c:00000000:0033dc1c:00000000:0033e56e:00000000:0033ef68:00000000:0033faed:00000000:003404de:00000000:00340f1a:00000000:00341ac4:00000000:0034241e:00000000:00342e29:00000000:0034384d:00000000:00344b94:00000000:003452a6:00000000:00345aa6:00000000:003462d5:00000000:00346cea:00000000:00347641:00000000:00347f92:00000000:003488e6:00000000:003491fd:00000000:00349a40:00000000:0034a215:00000000:0034aaba:00000000:0034b1bd:00000000:0034b80f:00000000:0034cecf:00000000:0034d4ca:00000000:0034dbc9:00000000:0034e351:00000000:0034ea8f:00000000:0034f221:00000000:0034fab0:00000000:00350313:00000000:00350c18:00000000:00352511:00000000:00352a6c:00000000:00353241:00000000:00353ba2:00000000:003544b7:00000000:00354e97:00000000:0035590f:00000000:003562fd:00000000:00356d37:00000000:00357789:00000000:00358109:00000000:003589bc:00000000:00359346:00000000:00359b34:00000000:0035a37e:00000000:0035abde:00000000:0035b424:00000000:0035bc8c:00000000:0035c4f2:00000000:0035cce0:00000000:0035d4dd:00000000:0035ddbf:00000000:0035e5d8:00000000:0035ed68:00000000:0035f4bf:00000000:0035fd32:00000000:00361bdd:00000000:00362500:00000000:00363139:00000000:00363b7d:00000000:00364617:00000000:003652ab:00000000:00365e40:00000000:00366a2e:00000000:0036757b:00000000:00368199:00000000:00368ec1:00000000:00369892:00000000:0036a32b:00000000:0036aef7:00000000:0036ba91:00000000:0036c7e4:00000000:0036d528:00000000:0036e184:00000000:0036edc2:00000000:0036fabc:00000000:00370657:00000000:003711ec:00000000:00371c9c:00000000:00375901:00000000:00378274:00000000:00378a5b:00000000:003795a7:00000000:0037a1ac:00000000:0037ad3b:00000000:0037b930:00000000:0037c582:00000000:0037d2b2:00000000:0037e123:00000000:0037f028:00000000:00380057:00000000:00380c3f:00000000:003817ec:00000000:003823a2:00000000:00382f0a:00000000:00383b5f:00000000:00384879:00000000:003854d3:00000000:003861bb:00000000:00386e89:00000000:003879ff:00000000:00388561:00000000:003890e4:00000000:00389b39:00000000:0038a796:00000000:0038b2d9:00000000:0038bd8b:00000000:0038c78f:00000000:0038e092:00000000:0038e9fc:00000000:0038f462:00000000:003900e1:00000000:00390c7e:00000000:0039185e:00000000:00392410:00000000:00392f3d:00000000:00393a37:00000000:0039452c:00000000:00394f34:00000000:003958e6:00000000:00396319:00000000:00396c79:00000000:003978ea:00000000:0039838c:00000000:00398e27:00000000:003999e6:00000000:0039a674:00000000:0039b259:00000000:0039be1f:00000000:0039c9aa:00000000:0039ee03:00000000:0039f37f:00000000:0039fc57:00000000:003a04c3:00000000:003a0f3e:00000000:003a19d5:00000000:003a23d8:00000000:003a2d91:00000000:003a37c2:00000000:003a4116:00000000:003a4a3a:00000000:003a536d:00000000:003a5c06:00000000:003a648b:00000000:003a6cd9:00000000:003a762f:00000000:003a7e8a:00000000:003a869a:00000000:003a8efa:00000000:003a9711:00000000:003a9f27:00000000:003ab1ac:00000000:003ab68b:00000000:003abe03:00000000:003ac441:00000000:003acc01:00000000:003ad380:00000000:003ad9f0:00000000:003adf98:00000000:003ae6a4:00000000:003aecc8:00000000:003af2a2:00000000:003afa04:00000000:003b0140:00000000:003b0788:00000000:003b0e6d:00000000:003b1489:00000000:003b1b44:00000000:003b2255:00000000:003b28a1:00000000:003b2e30:00000000:003b350a:00000000:003b3b77:00000000:003b410b:00000000:003b475b:00000000:003b72e1:00000000:003b7bb0:00000000:003b8964:00000000:003b9774:00000000:003ba5bc:00000000:003bcbfa:00000000:003bd620:00000000:003be3c6:00000000:003bf21c:00000000:003bfe46:00000000:003c0bc7:00000000:003c1aac:00000000:003c2a56:00000000:003c3b33:00000000:003c4a93:00000000:003c5965:00000000:003c6769:00000000:003c76c4:00000000:003c96c8:00000000:003ca257:00000000:003caf08:00000000:003cbdf5:00000000:003cccaf:00000000:003cdc31:00000000:003cea57:00000000:003cfa59:00000000:003d0a5d:00000000:003d18ac:00000000:003d274f:00000000:003d366b:00000000:003d44b9:00000000:003d53aa:00000000:003d6332:00000000:003d7187:00000000:003d8003:00000000:003d8f19:00000000:003d9d39:00000000:003dabf8:00000000:003dba64:00000000:003dc7c2:00000000:003dd625:00000000:003de525:00000000:003df35c:00000000:003e01aa:00000000:003e108c:00000000:003e1e85:00000000:003e2c2e:00000000:003e3dac:00000000:003e4c57:00000000:003e5bcd:00000000:003e6bf0:00000000:003e7b7c:00000000:003e896a:00000000:003e9938:00000000:003ea7d7:00000000:003ed7cd:00000000:003ee1fa:00000000:003eedd7:00000000:003efcfe:00000000:003f0c39:00000000:003f1ae2:00000000:003f28d4:00000000:003f3773:00000000:003f44e7:00000000:003f5349:00000000:003f629c:00000000:003f7126:00000000:003f7f88:00000000:003f8ed1:00000000:003f9d08:00000000:003fac20:00000000:003fbb3b:00000000:003fc8fe:00000000:003fd6f6:00000000:003fe60f:00000000:003ff3aa:00000000:0040023b:00000000:00401196:00000000:00401fc9:00000000:00402ec2:00000000:00403e0b:00000000:00404c0d:00000000:00405a6e:00000000:004069ad:00000000:0040785c:00000000:00408767:00000000:00409693:00000000:0040a45c:00000000:0040b215:00000000:0040bfdf:00000000:0040cff9:00000000:0040deff:00000000:0040ef1e:00000000:0040fc17:00000000:004109d4:00000000:004118c3:00000000:0041263a:00000000:00413423:00000000:004142d4:00000000:00414fe3:00000000:00415df3:00000000:00416d0d:00000000:00417b58:00000000:00418a38:00000000:0041997c:00000000:0041c7d1:00000000:0041d132:00000000:0041dec5:00000000:0041eafe:00000000:0041f7a1:00000000:00420524:00000000:00421461:00000000:0042232b:00000000:00423101:00000000:00423ee3:00000000:00424c98:00000000:00426756:00000000:00427406:00000000:00428153:00000000:00428f5a:00000000:00429eee:00000000:0042ae22:00000000:0042bce3:00000000:0042cb19:00000000:0042d989:00000000:0042e7eb:00000000:0042f517:00000000:00430210:00000000:00430f7e:00000000:00431b34:00000000:00432729:00000000:004332fb:00000000:00433d2a:00000000:004348e1:00000000:004368c5:00000000:00437334:00000000:004381cc:00000000:00439183:00000000:00439f5a:00000000:0043ae37:00000000:0043be13:00000000:0043cc07:00000000:0043da93:00000000:0043ea3b:00000000:0043f8d3:00000000:004407be:00000000:004417c4:00000000:0044262b:00000000:00443497:00000000:00444418:00000000:004454ea:00000000:0044642b:00000000:0044761b:00000000:00448545:00000000:0044951d:00000000:0044ccbd:00000000:0044d4fb:00000000:0044e1b5:00000000:0044f02a:00000000:0044fe46:00000000:00450cd9:00000000:00451ba6:00000000:0045291e:00000000:0045379e:00000000:00454848:00000000:0045562a:00000000:0045743a:00000000:00457ae6:00000000:0045846e:00000000:00458eaa:00000000:00459860:00000000:0045a0df:00000000:0045aa76:00000000:0045b61a:00000000:0045c111:00000000:0045cda9:00000000:0045d783:00000000:0045e258:00000000:0045ea08:00000000:0045f2c9:00000000:00461286:00000000:00461aa7:00000000:00462651:00000000:004630dd:00000000:00463e44:00000000:00464b03:00000000:0046571a:00000000:004663f5:00000000:00466fd4:00000000:00467b73:00000000:00468763:00000000:0046946d:00000000:0046a039:00000000:0046abff:00000000:0046b88b:00000000:0046c55e:00000000:0046d09a:00000000:0046dc57:00000000:0046e79a:00000000:0046f312:00000000:0046ff26:00000000:00471e35:00000000:004724b7:00000000:00472ca7:00000000:004736cc:00000000:004753a4:00000000:00475a8d:00000000:004762ef:00000000:00476b80:00000000:00477489:00000000:00477e45:00000000:004787ab:00000000:00479097:00000000:004799ef:00000000:0047a340:00000000:0047acc2:00000000:0047b725:00000000:0047c024:00000000:0047ca0a:00000000:0047dac4:00000000:0047e1bd:00000000:0047eac6:00000000:0047f1d7:00000000:0047f923:00000000:00480091:00000000:004807b4:00000000:00480ec8:00000000:004815da:00000000:00481cf1:00000000:004823f1:00000000:00482b29:00000000:00483363:00000000:00483b66:00000000:004842a5:00000000:00484a88:00000000:004851cb:00000000:0048596b:00000000:00485feb:00000000:00486712:00000000:00486e4c:00000000:004875df:00000000:00487d90:00000000:00488629:00000000:00488e14:00000000:0048976a:00000000:0048a10c:00000000:0048aace:00000000:0048b3e7:00000000:0048bc7b:00000000:0048c410:00000000:0048cd12:00000000:0048d5da:00000000:0048de4f:00000000:0048e6b9:00000000:0048ee08:00000000:0049081a:00000000:00490c0c:00000000:00491282:00000000:004918da:00000000:00491fc2:00000000:004927de:00000000:00492fd8:00000000:00493761:00000000:00493f51:00000000:00494659:00000000:00494fd2:00000000:00495714:00000000:00495e7d:00000000:004965d9:00000000:00496dc6:00000000:0049758f:00000000:00497dad:00000000:00498658:00000000:00498e85:00000000:0049abc1:00000000:0049b305:00000000:0049bc3e:00000000:0049c434:00000000:0049cd40:00000000:0049d49a:00000000:0049e0fe:00000000:0049ec78:00000000:0049f4fc:00000000:0049fd83:00000000:004a0937:00000000:004a12a2:00000000:004a1ae5:00000000:004a25b3:00000000:004a2f09:00000000:004a3776:00000000:004a41d2:00000000:004a4ab5:00000000:004a532b:00000000:004a5b22:00000000:004a66cd:00000000:004a7027:00000000:004a791d:00000000:004a8109:00000000:004a8912:00000000:004a92fd:00000000:004a9cc0:00000000:004aa54a:00000000:004aaef5:00000000:004ab7ee:00000000:004ac077:00000000:004af15f:00000000:004af551:00000000:004afad1:00000000:004b0312:00000000:004b0b0c:00000000:004b1370:00000000:004b1c88:00000000:004b2569:00000000:004b2db1:00000000:004b37fd:00000000:004b42ec:00000000:004b4c1a:00000000:004b565d:00000000:004b5ff0:00000000:004b69d2:00000000:004b7421:00000000:004b7df6:00000000:004b9749:00000000:004ba330:00000000:004baf7c:00000000:004bbc12:00000000:004bc838:00000000:004bd3f5:00000000:004be259:00000000:004bf015:00000000:004bfc1b:00000000:004c08e9:00000000:004c13cf:00000000:004c1f18:00000000:004c2a18
diff --git a/testProgs/testAMRAudioStreamer.cpp b/testProgs/testAMRAudioStreamer.cpp
index 9476da9..c00fcce 100644
--- a/testProgs/testAMRAudioStreamer.cpp
+++ b/testProgs/testAMRAudioStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads an AMR audio file (as defined in RFC 3267)
// and streams it using RTP
// main program
diff --git a/testProgs/testDVVideoStreamer.cpp b/testProgs/testDVVideoStreamer.cpp
index c280bd8..0f0f75a 100644
--- a/testProgs/testDVVideoStreamer.cpp
+++ b/testProgs/testDVVideoStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a DV Video Elementary Stream file,
// and streams it using RTP
// main program
diff --git a/testProgs/testGSMStreamer.cpp b/testProgs/testGSMStreamer.cpp
index ac4716a..2977a18 100644
--- a/testProgs/testGSMStreamer.cpp
+++ b/testProgs/testGSMStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that streams GSM audio via RTP/RTCP
// main program
diff --git a/testProgs/testH264VideoStreamer.cpp b/testProgs/testH264VideoStreamer.cpp
index efb52b1..237f0a1 100644
--- a/testProgs/testH264VideoStreamer.cpp
+++ b/testProgs/testH264VideoStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a H.264 Elementary Stream video file
// and streams it using RTP
// main program
diff --git a/testProgs/testH264VideoToTransportStream.cpp b/testProgs/testH264VideoToTransportStream.cpp
index 656e78f..89b5160 100644
--- a/testProgs/testH264VideoToTransportStream.cpp
+++ b/testProgs/testH264VideoToTransportStream.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that converts a H.264 (Elementary Stream) video file into a Transport Stream file.
// main program
diff --git a/testProgs/testH265VideoStreamer.cpp b/testProgs/testH265VideoStreamer.cpp
index 6234e96..8ae44ce 100644
--- a/testProgs/testH265VideoStreamer.cpp
+++ b/testProgs/testH265VideoStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a H.265 Elementary Stream video file
// and streams it using RTP
// main program
diff --git a/testProgs/testH265VideoToTransportStream.cpp b/testProgs/testH265VideoToTransportStream.cpp
index 0e8c0dc..57c9ac0 100644
--- a/testProgs/testH265VideoToTransportStream.cpp
+++ b/testProgs/testH265VideoToTransportStream.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that converts a H.265 (Elementary Stream) video file into a Transport Stream file.
// main program
diff --git a/testProgs/testMKVStreamer.cpp b/testProgs/testMKVStreamer.cpp
new file mode 100644
index 0000000..8471a29
--- /dev/null
+++ b/testProgs/testMKVStreamer.cpp
@@ -0,0 +1,178 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+**********/
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
+// A test program that reads a ".mkv" (i.e., Matroska) file, demultiplexes each track
+// (video, audio, subtitles), and streams each track using RTP multicast.
+// main program
+
+#include <liveMedia.hh>
+#include <BasicUsageEnvironment.hh>
+#include <GroupsockHelper.hh>
+
+UsageEnvironment* env;
+char const* inputFileName = "test.mkv";
+struct in_addr destinationAddress;
+RTSPServer* rtspServer;
+ServerMediaSession* sms;
+MatroskaFile* matroskaFile;
+MatroskaDemux* matroskaDemux;
+
+// An array of structures representing the state of the video, audio, and subtitle tracks:
+static struct {
+ unsigned trackNumber;
+ FramedSource* source;
+ RTPSink* sink;
+ RTCPInstance* rtcp;
+} trackState[3];
+
+void onMatroskaFileCreation(MatroskaFile* newFile, void* clientData); // forward
+
+int main(int argc, char** argv) {
+ // Begin by setting up our usage environment:
+ TaskScheduler* scheduler = BasicTaskScheduler::createNew();
+ env = BasicUsageEnvironment::createNew(*scheduler);
+
+ // Define our destination (multicast) IP address:
+ destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
+ // Note: This is a multicast address. If you wish instead to stream
+ // using unicast, then you should use the "testOnDemandRTSPServer"
+ // test program - not this test program - as a model.
+
+ // Create our RTSP server. (Receivers will need to use RTSP to access the stream.)
+ rtspServer = RTSPServer::createNew(*env, 8554);
+ if (rtspServer == NULL) {
+ *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
+ exit(1);
+ }
+ sms = ServerMediaSession::createNew(*env, "testStream", inputFileName,
+ "Session streamed by \"testMKVStreamer\"",
+ True /*SSM*/);
+
+ // Arrange to create a "MatroskaFile" object for the specified file.
+ // (Note that this object is not created immediately, but instead via a callback.)
+ MatroskaFile::createNew(*env, inputFileName, onMatroskaFileCreation, NULL, "jpn");
+
+ env->taskScheduler().doEventLoop(); // does not return
+
+ return 0; // only to prevent compiler warning
+}
+
+void play(); // forward
+
+void onMatroskaFileCreation(MatroskaFile* newFile, void* /*clientData*/) {
+ matroskaFile = newFile;
+
+ // Create a new demultiplexor for the file:
+ matroskaDemux = matroskaFile->newDemux();
+
+ // Create source streams, "RTPSink"s, and "RTCPInstance"s for each preferred track;
+ unsigned short rtpPortNum = 44444;
+ const unsigned char ttl = 255;
+
+ const unsigned maxCNAMElen = 100;
+ unsigned char CNAME[maxCNAMElen+1];
+ gethostname((char*)CNAME, maxCNAMElen);
+ CNAME[maxCNAMElen] = '\0'; // just in case
+
+ for (unsigned i = 0; i < 3; ++i) {
+ unsigned trackNumber;
+ FramedSource* baseSource = matroskaDemux->newDemuxedTrack(trackNumber);
+ trackState[i].trackNumber = trackNumber;
+
+ unsigned estBitrate, numFiltersInFrontOfTrack;
+ trackState[i].source = matroskaFile
+ ->createSourceForStreaming(baseSource, trackNumber, estBitrate, numFiltersInFrontOfTrack);
+ trackState[i].sink = NULL; // by default; may get changed below
+ trackState[i].rtcp = NULL; // ditto
+
+ if (trackState[i].source != NULL) {
+ Groupsock* rtpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum, ttl);
+ Groupsock* rtcpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum+1, ttl);
+ rtpPortNum += 2;
+
+ trackState[i].sink
+ = matroskaFile->createRTPSinkForTrackNumber(trackNumber, rtpGroupsock, 96+i);
+ if (trackState[i].sink != NULL) {
+ if (trackState[i].sink->estimatedBitrate() > 0) {
+ estBitrate = trackState[i].sink->estimatedBitrate(); // hack
+ }
+ trackState[i].rtcp
+ = RTCPInstance::createNew(*env, rtcpGroupsock, estBitrate, CNAME,
+ trackState[i].sink, NULL /* we're a server */,
+ True /* we're a SSM source */);
+ // Note: This starts RTCP running automatically
+
+ // Having set up a track for streaming, add it to our RTSP server's "ServerMediaSession":
+ sms->addSubsession(PassiveServerMediaSubsession::createNew(*trackState[i].sink, trackState[i].rtcp));
+ }
+ }
+ }
+
+ if (sms->numSubsessions() == 0) {
+ *env << "Error: The Matroska file \"" << inputFileName << "\" has no streamable tracks\n";
+ *env << "(Perhaps the file does not exist, or is not a 'Matroska' file.)\n";
+ exit(1);
+ }
+
+ rtspServer->addServerMediaSession(sms);
+
+ char* url = rtspServer->rtspURL(sms);
+ *env << "Play this stream using the URL \"" << url << "\"\n";
+ delete[] url;
+
+ // Start the streaming:
+ play();
+}
+
+void afterPlaying(void* /*clientData*/) {
+ *env << "...done reading from file\n";
+
+ // Stop playing all "RTPSink"s, then close the source streams
+ // (which will also close the demultiplexor itself):
+ unsigned i;
+ for (i = 0; i < 3; ++i) {
+ if (trackState[i].sink != NULL) trackState[i].sink->stopPlaying();
+ Medium::close(trackState[i].source); trackState[i].source = NULL;
+ }
+
+ // Create a new demultiplexor from our Matroska file, then new data sources for each track:
+ matroskaDemux = matroskaFile->newDemux();
+ for (i = 0; i < 3; ++i) {
+ if (trackState[i].trackNumber != 0) {
+ FramedSource* baseSource
+ = matroskaDemux->newDemuxedTrackByTrackNumber(trackState[i].trackNumber);
+
+ unsigned estBitrate, numFiltersInFrontOfTrack;
+ trackState[i].source = matroskaFile
+ ->createSourceForStreaming(baseSource, trackState[i].trackNumber,
+ estBitrate, numFiltersInFrontOfTrack);
+ }
+ }
+
+ // Start playing once again:
+ play();
+}
+
+void play() {
+ *env << "Beginning to read from file...\n";
+
+ // Start playing each track's RTP sink from its corresponding source:
+ for (unsigned i = 0; i < 3; ++i) {
+ if (trackState[i].sink != NULL && trackState[i].source != NULL) {
+ trackState[i].sink->startPlaying(*trackState[i].source, afterPlaying, NULL);
+ }
+ }
+}
diff --git a/testProgs/testMP3Receiver.cpp b/testProgs/testMP3Receiver.cpp
index d3bf560..cea66b6 100644
--- a/testProgs/testMP3Receiver.cpp
+++ b/testProgs/testMP3Receiver.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that receives a RTP/RTCP multicast MP3 stream,
// and outputs the resulting MP3 file stream to 'stdout'
// main program
diff --git a/testProgs/testMP3Streamer.cpp b/testProgs/testMP3Streamer.cpp
index 362c1db..7085bb0 100644
--- a/testProgs/testMP3Streamer.cpp
+++ b/testProgs/testMP3Streamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that streams a MP3 file via RTP/RTCP
// main program
diff --git a/testProgs/testMPEG1or2AudioVideoStreamer.cpp b/testProgs/testMPEG1or2AudioVideoStreamer.cpp
index f039a81..7000445 100644
--- a/testProgs/testMPEG1or2AudioVideoStreamer.cpp
+++ b/testProgs/testMPEG1or2AudioVideoStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-1 or 2 Program Stream file,
// splits it into Audio and Video Elementary Streams,
// and streams both using RTP
diff --git a/testProgs/testMPEG1or2ProgramToTransportStream.cpp b/testProgs/testMPEG1or2ProgramToTransportStream.cpp
index 642303f..87a1726 100644
--- a/testProgs/testMPEG1or2ProgramToTransportStream.cpp
+++ b/testProgs/testMPEG1or2ProgramToTransportStream.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that converts a MPEG-1 or 2 Program Stream file into
// a Transport Stream file.
// main program
diff --git a/testProgs/testMPEG1or2Splitter.cpp b/testProgs/testMPEG1or2Splitter.cpp
index defd9c5..0593929 100644
--- a/testProgs/testMPEG1or2Splitter.cpp
+++ b/testProgs/testMPEG1or2Splitter.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that splits a MPEG-1 or 2 Program Stream file into
// video and audio output files.
// main program
diff --git a/testProgs/testMPEG1or2VideoReceiver.cpp b/testProgs/testMPEG1or2VideoReceiver.cpp
index 359b4a6..b8ca6de 100644
--- a/testProgs/testMPEG1or2VideoReceiver.cpp
+++ b/testProgs/testMPEG1or2VideoReceiver.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that receives a RTP/RTCP multicast MPEG video stream,
// and outputs the resulting MPEG file stream to 'stdout'
// main program
@@ -87,7 +87,7 @@ int main(int argc, char** argv) {
sessionState.source = MPEG1or2VideoRTPSource::createNew(*env, &rtpGroupsock);
// Create (and start) a 'RTCP instance' for the RTP source:
- const unsigned estimatedSessionBandwidth = 160; // in kbps; for RTCP b/w share
+ const unsigned estimatedSessionBandwidth = 4500; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
diff --git a/testProgs/testMPEG1or2VideoStreamer.cpp b/testProgs/testMPEG1or2VideoStreamer.cpp
index 9c4836f..929dcc9 100644
--- a/testProgs/testMPEG1or2VideoStreamer.cpp
+++ b/testProgs/testMPEG1or2VideoStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-1 or 2 Video Elementary Stream file,
// and streams it using RTP
// main program
diff --git a/testProgs/testMPEG2TransportReceiver.cpp b/testProgs/testMPEG2TransportReceiver.cpp
index f517387..3d4f899 100644
--- a/testProgs/testMPEG2TransportReceiver.cpp
+++ b/testProgs/testMPEG2TransportReceiver.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that receives a RTP/RTCP multicast MPEG-2 Transport Stream,
// and outputs the resulting Transport Stream data to 'stdout'
// main program
@@ -87,7 +87,7 @@ int main(int argc, char** argv) {
sessionState.source = SimpleRTPSource::createNew(*env, &rtpGroupsock, 33, 90000, "video/MP2T", 0, False /*no 'M' bit*/);
// Create (and start) a 'RTCP instance' for the RTP source:
- const unsigned estimatedSessionBandwidth = 160; // in kbps; for RTCP b/w share
+ const unsigned estimatedSessionBandwidth = 5000; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
diff --git a/testProgs/testMPEG2TransportStreamTrickPlay.cpp b/testProgs/testMPEG2TransportStreamTrickPlay.cpp
index 92dbe56..41d0616 100644
--- a/testProgs/testMPEG2TransportStreamTrickPlay.cpp
+++ b/testProgs/testMPEG2TransportStreamTrickPlay.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A program that tests 'trick mode' operations on a MPEG-2 Transport Stream file,
// by generating a new Transport Stream file that represents the result of the
// 'trick mode' operation (seeking and/or fast forward/reverse play).
diff --git a/testProgs/testMPEG2TransportStreamer.cpp b/testProgs/testMPEG2TransportStreamer.cpp
index e29cc3b..5bfd8b3 100644
--- a/testProgs/testMPEG2TransportStreamer.cpp
+++ b/testProgs/testMPEG2TransportStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-2 Transport Stream file,
// and streams it using RTP
// main program
diff --git a/testProgs/testMPEG4VideoStreamer.cpp b/testProgs/testMPEG4VideoStreamer.cpp
index 2852ba9..407d933 100644
--- a/testProgs/testMPEG4VideoStreamer.cpp
+++ b/testProgs/testMPEG4VideoStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-4 Video Elementary Stream file,
// and streams it using RTP
// main program
diff --git a/testProgs/testOggStreamer.cpp b/testProgs/testOggStreamer.cpp
new file mode 100644
index 0000000..85f10bd
--- /dev/null
+++ b/testProgs/testOggStreamer.cpp
@@ -0,0 +1,182 @@
+/**********
+This library is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the
+Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+**********/
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
+// A test program that reads a ".ogg" (i.e., Ogg) file, demultiplexes each track
+// (audio and/or video), and streams each track using RTP multicast.
+// main program
+
+#include <liveMedia.hh>
+#include <BasicUsageEnvironment.hh>
+#include <GroupsockHelper.hh>
+
+UsageEnvironment* env;
+char const* inputFileName = "test.ogg";
+struct in_addr destinationAddress;
+RTSPServer* rtspServer;
+ServerMediaSession* sms;
+OggFile* oggFile;
+OggDemux* oggDemux;
+unsigned numTracks;
+
+// A structure representing the state of a track:
+struct TrackState {
+ u_int32_t trackNumber;
+ FramedSource* source;
+ RTPSink* sink;
+ RTCPInstance* rtcp;
+};
+TrackState* trackState;
+
+void onOggFileCreation(OggFile* newFile, void* clientData); // forward
+
+int main(int argc, char** argv) {
+ // Begin by setting up our usage environment:
+ TaskScheduler* scheduler = BasicTaskScheduler::createNew();
+ env = BasicUsageEnvironment::createNew(*scheduler);
+
+ // Define our destination (multicast) IP address:
+ destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
+ // Note: This is a multicast address. If you wish instead to stream
+ // using unicast, then you should use the "testOnDemandRTSPServer"
+ // test program - not this test program - as a model.
+
+ // Create our RTSP server. (Receivers will need to use RTSP to access the stream.)
+ rtspServer = RTSPServer::createNew(*env, 8554);
+ if (rtspServer == NULL) {
+ *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
+ exit(1);
+ }
+ sms = ServerMediaSession::createNew(*env, "testStream", inputFileName,
+ "Session streamed by \"testMKVStreamer\"",
+ True /*SSM*/);
+
+ // Arrange to create an "OggFile" object for the specified file.
+ // (Note that this object is not created immediately, but instead via a callback.)
+ OggFile::createNew(*env, inputFileName, onOggFileCreation, NULL);
+
+ env->taskScheduler().doEventLoop(); // does not return
+
+ return 0; // only to prevent compiler warning
+}
+
+void play(); // forward
+
+void onOggFileCreation(OggFile* newFile, void* clientData) {
+ oggFile = newFile;
+
+ // Create a new demultiplexor for the file:
+ oggDemux = oggFile->newDemux();
+
+ // Create source streams, "RTPSink"s, and "RTCPInstance"s for each preferred track;
+ unsigned short rtpPortNum = 22222;
+ const unsigned char ttl = 255;
+
+ const unsigned maxCNAMElen = 100;
+ unsigned char CNAME[maxCNAMElen+1];
+ gethostname((char*)CNAME, maxCNAMElen);
+ CNAME[maxCNAMElen] = '\0'; // just in case
+
+ numTracks = oggFile->numTracks();
+ trackState = new TrackState[numTracks];
+ for (unsigned i = 0; i < numTracks; ++i) {
+ u_int32_t trackNumber;
+ FramedSource* baseSource = oggDemux->newDemuxedTrack(trackNumber);
+ trackState[i].trackNumber = trackNumber;
+
+ unsigned estBitrate, numFiltersInFrontOfTrack;
+ trackState[i].source = oggFile
+ ->createSourceForStreaming(baseSource, trackNumber, estBitrate, numFiltersInFrontOfTrack);
+ trackState[i].sink = NULL; // by default; may get changed below
+ trackState[i].rtcp = NULL; // ditto
+
+ if (trackState[i].source != NULL) {
+ Groupsock* rtpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum, ttl);
+ Groupsock* rtcpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum+1, ttl);
+ rtpPortNum += 2;
+
+ trackState[i].sink
+ = oggFile->createRTPSinkForTrackNumber(trackNumber, rtpGroupsock, 96+i);
+ if (trackState[i].sink != NULL) {
+ if (trackState[i].sink->estimatedBitrate() > 0) {
+ estBitrate = trackState[i].sink->estimatedBitrate(); // hack
+ }
+ trackState[i].rtcp
+ = RTCPInstance::createNew(*env, rtcpGroupsock, estBitrate, CNAME,
+ trackState[i].sink, NULL /* we're a server */,
+ True /* we're a SSM source */);
+ // Note: This starts RTCP running automatically
+
+ // Having set up a track for streaming, add it to our RTSP server's "ServerMediaSession":
+ sms->addSubsession(PassiveServerMediaSubsession::createNew(*trackState[i].sink, trackState[i].rtcp));
+ }
+ }
+ }
+
+ if (sms->numSubsessions() == 0) {
+ *env << "Error: The Ogg file \"" << inputFileName << "\" has no streamable tracks\n";
+ *env << "(Perhaps the file does not exist, is not an 'Ogg' file, or has no tracks that we know how to stream.)\n";
+ exit(1);
+ }
+
+ rtspServer->addServerMediaSession(sms);
+
+ char* url = rtspServer->rtspURL(sms);
+ *env << "Play this stream using the URL \"" << url << "\"\n";
+ delete[] url;
+
+ // Start the streaming:
+ play();
+}
+
+void afterPlaying(void* /*clientData*/) {
+ *env << "...done reading from file\n";
+
+ // Stop playing all "RTPSink"s, then close the source streams
+ // (which will also close the demultiplexor itself):
+ unsigned i;
+ for (i = 0; i < numTracks; ++i) {
+ if (trackState[i].sink != NULL) trackState[i].sink->stopPlaying();
+ Medium::close(trackState[i].source); trackState[i].source = NULL;
+ }
+
+ // Create a new demultiplexor from our Ogg file, then new data sources for each track:
+ oggDemux = oggFile->newDemux();
+ for (i = 0; i < numTracks; ++i) {
+ if (trackState[i].trackNumber != 0) {
+ FramedSource* baseSource
+ = oggDemux->newDemuxedTrack(trackState[i].trackNumber);
+
+ unsigned estBitrate, numFiltersInFrontOfTrack;
+ trackState[i].source
+ = oggFile->createSourceForStreaming(baseSource, trackState[i].trackNumber,
+ estBitrate, numFiltersInFrontOfTrack);
+ }
+ }
+
+ // Start playing once again:
+ play();
+}
+
+void play() {
+ *env << "Beginning to read from file...\n";
+
+ // Start playing each track's RTP sink from its corresponding source:
+ for (unsigned i = 0; i < numTracks; ++i) {
+ if (trackState[i].sink != NULL && trackState[i].source != NULL) {
+ trackState[i].sink->startPlaying(*trackState[i].source, afterPlaying, NULL);
+ }
+ }
+}
diff --git a/testProgs/testOnDemandRTSPServer.cpp b/testProgs/testOnDemandRTSPServer.cpp
index dcfaf6c..680c2de 100644
--- a/testProgs/testOnDemandRTSPServer.cpp
+++ b/testProgs/testOnDemandRTSPServer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that demonstrates how to stream - via unicast RTP
// - various kinds of file on demand, using a built-in RTSP server.
// main program
@@ -36,11 +36,18 @@ Boolean iFramesOnly = False;
static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
char const* streamName, char const* inputFileName); // fwd
-static char newMatroskaDemuxWatchVariable;
-static MatroskaFileServerDemux* demux;
+static char newDemuxWatchVariable;
+
+static MatroskaFileServerDemux* matroskaDemux;
static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* /*clientData*/) {
- demux = newDemux;
- newMatroskaDemuxWatchVariable = 1;
+ matroskaDemux = newDemux;
+ newDemuxWatchVariable = 1;
+}
+
+static OggFileServerDemux* oggDemux;
+static void onOggDemuxCreation(OggFileServerDemux* newDemux, void* /*clientData*/) {
+ oggDemux = newDemux;
+ newDemuxWatchVariable = 1;
}
int main(int argc, char** argv) {
@@ -296,13 +303,13 @@ int main(int argc, char** argv) {
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
- newMatroskaDemuxWatchVariable = 0;
+ newDemuxWatchVariable = 0;
MatroskaFileServerDemux::createNew(*env, inputFileName, onMatroskaDemuxCreation, NULL);
- env->taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
+ env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
Boolean sessionHasTracks = False;
ServerMediaSubsession* smss;
- while ((smss = demux->newServerMediaSubsession()) != NULL) {
+ while ((smss = matroskaDemux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
sessionHasTracks = True;
}
@@ -323,13 +330,66 @@ int main(int argc, char** argv) {
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
- newMatroskaDemuxWatchVariable = 0;
+ newDemuxWatchVariable = 0;
MatroskaFileServerDemux::createNew(*env, inputFileName, onMatroskaDemuxCreation, NULL);
- env->taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
+ env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
+
+ Boolean sessionHasTracks = False;
+ ServerMediaSubsession* smss;
+ while ((smss = matroskaDemux->newServerMediaSubsession()) != NULL) {
+ sms->addSubsession(smss);
+ sessionHasTracks = True;
+ }
+ if (sessionHasTracks) {
+ rtspServer->addServerMediaSession(sms);
+ }
+ // otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
+
+ announceStream(rtspServer, sms, streamName, inputFileName);
+ }
+
+ // An Ogg ('.ogg') file, with video and/or audio streams:
+ {
+ char const* streamName = "oggFileTest";
+ char const* inputFileName = "test.ogg";
+ ServerMediaSession* sms
+ = ServerMediaSession::createNew(*env, streamName, streamName,
+ descriptionString);
+
+ newDemuxWatchVariable = 0;
+ OggFileServerDemux::createNew(*env, inputFileName, onOggDemuxCreation, NULL);
+ env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
+
+ Boolean sessionHasTracks = False;
+ ServerMediaSubsession* smss;
+ while ((smss = oggDemux->newServerMediaSubsession()) != NULL) {
+ sms->addSubsession(smss);
+ sessionHasTracks = True;
+ }
+ if (sessionHasTracks) {
+ rtspServer->addServerMediaSession(sms);
+ }
+ // otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
+
+ announceStream(rtspServer, sms, streamName, inputFileName);
+ }
+
+ // An Opus ('.opus') audio file:
+ // (Note: ".opus' files are special types of Ogg files, so we use the same code as the Ogg ('.ogg') file code above.)
+ {
+ char const* streamName = "opusFileTest";
+ char const* inputFileName = "test.opus";
+ ServerMediaSession* sms
+ = ServerMediaSession::createNew(*env, streamName, streamName,
+ descriptionString);
+
+ newDemuxWatchVariable = 0;
+ OggFileServerDemux::createNew(*env, inputFileName, onOggDemuxCreation, NULL);
+ env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
Boolean sessionHasTracks = False;
ServerMediaSubsession* smss;
- while ((smss = demux->newServerMediaSubsession()) != NULL) {
+ while ((smss = oggDemux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
sessionHasTracks = True;
}
diff --git a/testProgs/testRTSPClient.cpp b/testProgs/testRTSPClient.cpp
index 52746ac..aa48714 100644
--- a/testProgs/testRTSPClient.cpp
+++ b/testProgs/testRTSPClient.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A demo application, showing how to create and run a RTSP client (that can potentially receive multiple streams concurrently).
//
// NOTE: This code - although it builds a running application - is intended only to illustrate how to develop your own RTSP
@@ -238,8 +238,13 @@ void setupNextSubsession(RTSPClient* rtspClient) {
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";
+ env << *rtspClient << "Initiated the \"" << *scs.subsession << "\" subsession (";
+ if (scs.subsession->rtcpIsMuxed()) {
+ env << "client port " << scs.subsession->clientPortNum();
+ } else {
+ env << "client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1;
+ }
+ env << ")\n";
// Continue setting up this subsession, by sending a RTSP "SETUP" command:
rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
@@ -267,8 +272,13 @@ void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultStri
break;
}
- env << *rtspClient << "Set up the \"" << *scs.subsession
- << "\" subsession (client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1 << ")\n";
+ env << *rtspClient << "Set up the \"" << *scs.subsession << "\" subsession (";
+ if (scs.subsession->rtcpIsMuxed()) {
+ env << "client port " << scs.subsession->clientPortNum();
+ } else {
+ env << "client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1;
+ }
+ env << ")\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,
@@ -283,7 +293,7 @@ void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultStri
}
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->miscPtr = rtspClient; // a hack to let subsession handler 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:
diff --git a/testProgs/testRelay.cpp b/testProgs/testRelay.cpp
index 189b2ff..735ed43 100644
--- a/testProgs/testRelay.cpp
+++ b/testProgs/testRelay.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that receives a UDP multicast stream
// and retransmits it to another (multicast or unicast) address & port
// main program
diff --git a/testProgs/testReplicator.cpp b/testProgs/testReplicator.cpp
index 6d8a69a..f0919e1 100644
--- a/testProgs/testReplicator.cpp
+++ b/testProgs/testReplicator.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A demo application that receives a UDP multicast stream, replicates it (using the "StreamReplicator" class),
// and retransmits one replica stream to another (multicast or unicast) address & port,
// and writes the other replica stream to a file.
diff --git a/testProgs/testWAVAudioStreamer.cpp b/testProgs/testWAVAudioStreamer.cpp
index 12d742d..6d9c20d 100644
--- a/testProgs/testWAVAudioStreamer.cpp
+++ b/testProgs/testWAVAudioStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that streams a WAV audio file via RTP/RTCP
// main program
diff --git a/testProgs/vobStreamer.cpp b/testProgs/vobStreamer.cpp
index b883a1a..b64ddd7 100644
--- a/testProgs/vobStreamer.cpp
+++ b/testProgs/vobStreamer.cpp
@@ -13,7 +13,7 @@ You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
-// Copyright (c) 1996-2014, Live Networks, Inc. All rights reserved
+// Copyright (c) 1996-2016, Live Networks, Inc. All rights reserved
// A test program that reads a VOB file
// splits it into Audio (AC3) and Video (MPEG) Elementary Streams,
// and streams both using RTP.
--
liblivemedia packaging
More information about the pkg-multimedia-commits
mailing list