summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarpi <arpi@b3059339-0415-0410-9bf9-f77b7e298cf2>2002-08-05 00:39:07 +0000
committerarpi <arpi@b3059339-0415-0410-9bf9-f77b7e298cf2>2002-08-05 00:39:07 +0000
commitfa788640e26698f98070cc01be09dc5071580881 (patch)
tree3963d1dd69d48ad440e02251286001d90f5fbb01
parent11bc29e6cc3aaa2a70562f1142609d2800e2f6b9 (diff)
downloadmpv-fa788640e26698f98070cc01be09dc5071580881.tar.bz2
mpv-fa788640e26698f98070cc01be09dc5071580881.tar.xz
applied live.com streaming patch (-sdp and rtsp:// support) by Ross Finlayson <finlayson@live.com>
see <http://www.live.com/mplayer/> for details. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@6911 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--cfg-mplayer.h11
-rwxr-xr-xconfigure52
-rw-r--r--libmpdemux/Makefile18
-rw-r--r--libmpdemux/demux_rtp.cpp431
-rw-r--r--libmpdemux/demux_rtp.h33
-rw-r--r--libmpdemux/demuxer.c20
-rw-r--r--libmpdemux/demuxer.h9
-rw-r--r--libmpdemux/network.c28
-rw-r--r--libmpdemux/open.c12
-rw-r--r--libmpdemux/video.c13
10 files changed, 618 insertions, 9 deletions
diff --git a/cfg-mplayer.h b/cfg-mplayer.h
index 58feb8e245..56272fc3a4 100644
--- a/cfg-mplayer.h
+++ b/cfg-mplayer.h
@@ -80,6 +80,10 @@ extern int vo_zr_parseoption(struct config * conf, char *opt, char * param);
extern void vo_zr_revertoption(config_t* opt,char* pram);
#endif
+#ifdef STREAMING_LIVE_DOT_COM
+extern int isSDPFile;
+#endif
+
#ifdef HAVE_NEW_GUI
extern char * skinName;
#endif
@@ -276,6 +280,13 @@ static config_t mplayer_opts[]={
{"zr*", vo_zr_parseoption, CONF_TYPE_FUNC_FULL, 0, 0, 0, &vo_zr_revertoption },
#endif
+#ifdef STREAMING_LIVE_DOT_COM
+ // -sdp option, specifying that the source is a SDP file
+ {"sdp", &isSDPFile, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+#else
+ {"sdp", "MPlayer was compiled WITHOUT the \"LIVE.COM Streaming Media\" libraries!\n", CONF_TYPE_PRINT, CONF_NOCFG, 0, 0, NULL},
+#endif
+
//---------------------- mplayer-only options ------------------------
{"osdlevel", &osd_level, CONF_TYPE_INT, CONF_RANGE, 0, 2 , NULL},
diff --git a/configure b/configure
index 6fbf5fd6b6..7d7bfba0bd 100755
--- a/configure
+++ b/configure
@@ -154,6 +154,7 @@ Optional features:
--disable-libdv disable libdv 0.9.5 support [autodetect]
--disable-streaming disable network streaming support
(support for: http/mms/rtp) [enable]
+ --disable-live disable LIVE.COM Streaming Media support [disable]
--disable-vidix disable VIDIX stuff [enable on x86 *nix]
--disable-new-input disable new input system [enable]
--enable-joystick enable joystick support in new input [disable]
@@ -241,6 +242,7 @@ Use these options if autodetection fails:
--with-gtk-config=PATH path to gtk*-config (e.g. /opt/bin/gtk-config)
--with-glib-config=PATH path to glib*-config (e.g. /opt/bin/glib-config)
--with-dvdnav-config=PATH path to dvdnav-config
+ --with-livelibdir=DIR path to LIVE.COM Streaming Media libraries
EOF
exit 0
@@ -929,6 +931,7 @@ _dvdread=auto
_dvdkit=auto
_xanim=auto
_real=auto
+_live=no
_xinerama=auto
_mga=auto
_xmga=auto
@@ -1052,6 +1055,8 @@ for ac_option do
--disable-xanim) _xanim=no ;;
--enable-real) _real=yes ;;
--disable-real) _real=no ;;
+ --enable-live) _live=yes ;;
+ --disable-live) _live=no ;;
--enable-xinerama) _xinerama=yes ;;
--disable-xinerama) _xinerama=no ;;
--enable-mga) _mga=yes ;;
@@ -1149,6 +1154,10 @@ for ac_option do
_reallibdir=`echo $ac_option | cut -d '=' -f 2`
_real=yes
;;
+ --with-livelibdir=*)
+ _livelibdir=`echo $ac_option | cut -d '=' -f 2`
+ _live=yes
+ ;;
--with-csslibdir=*)
_csslibdir=`echo $ac_option | cut -d '=' -f 2`
_css=yes
@@ -3397,6 +3406,41 @@ else
fi
+if test -z "$_livelibdir" ; then
+ for I in $_libdir/live /usr/lib/live /usr/local/lib/live; do
+ if test -d "$I" ; then
+ _livelibdir="$I"
+ break;
+ fi;
+ done
+fi
+
+echocheck "LIVE.COM Streaming Media libraries"
+if test "$_live" = auto ; then
+ _live=yes
+ test "$_livelibdir" || _live=no
+ # TODO: deeper, more reliable test of libs, and version!
+ # (users may have empty live/ dir or something different there, for
+ # example 'live config files', or they may have old, incompatibel version)
+fi
+if test "$_live" = yes ; then
+ echores "yes (using $_livelibdir)"
+ _streaming=yes
+ _def_live='#define STREAMING_LIVE_DOT_COM 1'
+ _live_libs_def="# LIVE.COM Streaming Media libraries:
+LIVE_LIB_DIR = $_livelibdir
+LIVE_LIBS = \$(LIVE_LIB_DIR)/liveMedia/libliveMedia.a
+LIVE_LIBS += \$(LIVE_LIB_DIR)/groupsock/libgroupsock.a
+LIVE_LIBS += \$(LIVE_LIB_DIR)/UsageEnvironment/libUsageEnvironment.a
+LIVE_LIBS += \$(LIVE_LIB_DIR)/BasicUsageEnvironment/libBasicUsageEnvironment.a
+LIVE_LIBS += -lstdc++"
+ _ld_live='$(LIVE_LIBS)'
+else
+ echores "no"
+ _def_live='#undef STREAMING_LIVE_DOT_COM'
+fi
+
+
echocheck "iconv"
if test "$_iconv" = auto ; then
if freebsd ; then
@@ -4069,8 +4113,11 @@ EXTRA_INC = $_inc_extra $_inc_gtk
WIN32_PATH = -DWIN32_PATH=\\"$_win32libdir\\"
STRIPBINARIES = $_stripbinaries
+$_live_libs_def
+
STREAMING = $_streaming
-STREAMING_LIB = $_ld_streaming
+STREAMING_LIVE_DOT_COM = $_live
+STREAMING_LIB = $_ld_streaming $_ld_live
VIDIX = $_vidix
OPENDIVX = $_opendivx
@@ -4376,6 +4423,9 @@ $_def_real
/* Default search path */
$_def_real_path
+/* LIVE.COM Streaming Media library support */
+$_def_live
+
/* Use 3dnow/mmxext/sse/mmx optimized fast memcpy() [maybe buggy... signal 4]*/
$_def_fastmemcpy
diff --git a/libmpdemux/Makefile b/libmpdemux/Makefile
index 01fc669b90..b2cb029c1a 100644
--- a/libmpdemux/Makefile
+++ b/libmpdemux/Makefile
@@ -5,14 +5,26 @@ include ../config.mak
SRCS = mp3_hdr.c video.c mpeg_hdr.c cache2.c asfheader.c aviheader.c aviprint.c aviwrite.c demux_asf.c demux_avi.c demux_mov.c parse_mp4.c demux_mpg.c demux_viv.c demuxer.c dvdauth.c dvdnav_stream.c open.c parse_es.c stream.c tv.c tvi_dummy.c tvi_v4l.c tvi_bsdbt848.c frequencies.c demux_fli.c demux_real.c demux_y4m.c yuv4mpeg.c yuv4mpeg_ratio.c demux_nuv.c demux_film.c demux_roq.c mf.c demux_mf.c demux_audio.c demux_demuxers.c opt-reg.c mpdemux.c demux_ogg.c demux_bmp.c cdda.c demux_rawaudio.c cddb.c
ifeq ($(STREAMING),yes)
-SRCS += asf_streaming.c url.c http.c network.c rtp.c asf_mmst_streaming.c
+SRCS += asf_streaming.c url.c http.c network.c asf_mmst_streaming.c
+ifeq ($(STREAMING_LIVE_DOT_COM),yes)
+CPLUSPLUSSRCS = demux_rtp.cpp
+CPLUSPLUSINCLUDE = -I$(LIVE_LIB_DIR)/liveMedia/include
+CPLUSPLUSINCLUDE += -I$(LIVE_LIB_DIR)/UsageEnvironment/include
+CPLUSPLUSINCLUDE += -I$(LIVE_LIB_DIR)/BasicUsageEnvironment/include
+CPLUSPLUSINCLUDE += -I$(LIVE_LIB_DIR)/groupsock/include
+else
+SRCS += rtp.c
+endif
endif
OBJS = $(SRCS:.c=.o)
+OBJS += $(CPLUSPLUSSRCS:.cpp=.o)
INCLUDE = -I../loader $(CSS_INC) $(EXTRA_INC)
CFLAGS = $(OPTFLAGS) $(INCLUDE)
+CPLUSPLUSFLAGS = $(CFLAGS) $(CPLUSPLUSINCLUDE)
+CPLUSPLUS = c++
-.SUFFIXES: .c .o
+.SUFFIXES: .c .cpp .o
# .PHONY: all clean
@@ -20,6 +32,8 @@ all: $(LIBNAME)
.c.o:
$(CC) -c $(CFLAGS) -o $@ $<
+.cpp.o:
+ $(CPLUSPLUS) -c $(CPLUSPLUSFLAGS) -o $@ $<
$(LIBNAME): $(OBJS)
$(AR) r $(LIBNAME) $(OBJS)
diff --git a/libmpdemux/demux_rtp.cpp b/libmpdemux/demux_rtp.cpp
new file mode 100644
index 0000000000..95101826fe
--- /dev/null
+++ b/libmpdemux/demux_rtp.cpp
@@ -0,0 +1,431 @@
+extern "C" {
+#include "demux_rtp.h"
+#include "stheader.h"
+}
+
+#include "BasicUsageEnvironment.hh"
+#include "liveMedia.hh"
+#include <unistd.h>
+
+////////// Routines (with C-linkage) that interface between "mplayer"
+////////// and the "LIVE.COM Streaming Media" libraries:
+
+extern "C" stream_t* stream_open_sdp(int fd, off_t fileSize,
+ int* file_format) {
+ *file_format = DEMUXER_TYPE_RTP;
+ stream_t* newStream = NULL;
+ do {
+ char* sdpDescription = (char*)malloc(fileSize+1);
+ if (sdpDescription == NULL) break;
+
+ ssize_t numBytesRead = read(fd, sdpDescription, fileSize);
+ if (numBytesRead != fileSize) break;
+ sdpDescription[fileSize] = '\0'; // to be safe
+
+ newStream = (stream_t*)calloc(sizeof (stream_t), 1);
+ if (newStream == NULL) break;
+
+ // Store the SDP description in the 'priv' field, for later use:
+ newStream->priv = sdpDescription;
+ } while (0);
+ return newStream;
+}
+
+extern "C" int _rtsp_streaming_seek(int /*fd*/, off_t /*pos*/,
+ streaming_ctrl_t* /*streaming_ctrl*/) {
+ return -1; // For now, we don't handle RTSP stream seeking
+}
+
+extern "C" int rtsp_streaming_start(stream_t* stream) {
+ stream->streaming_ctrl->streaming_seek = _rtsp_streaming_seek;
+
+ return 0;
+}
+
+// A data structure representing a buffer being read:
+class ReadBufferQueue; // forward
+class ReadBuffer {
+public:
+ ReadBuffer(ReadBufferQueue* ourQueue, demux_packet_t* dp);
+ virtual ~ReadBuffer();
+ Boolean enqueue();
+
+ demux_packet_t* dp() const { return fDP; }
+ ReadBufferQueue* ourQueue() { return fOurQueue; }
+
+ ReadBuffer* next;
+private:
+ demux_packet_t* fDP;
+ ReadBufferQueue* fOurQueue;
+};
+
+class ReadBufferQueue {
+public:
+ ReadBufferQueue(MediaSubsession* subsession, demuxer_t* demuxer,
+ char const* tag);
+ virtual ~ReadBufferQueue();
+
+ ReadBuffer* dequeue();
+
+ FramedSource* readSource() const { return fReadSource; }
+ demuxer_t* ourDemuxer() const { return fOurDemuxer; }
+ char const* tag() const { return fTag; }
+
+ ReadBuffer* head;
+ ReadBuffer* tail;
+ char blockingFlag; // used to implement synchronous reads
+ unsigned counter; // used for debugging
+private:
+ FramedSource* fReadSource;
+ demuxer_t* fOurDemuxer;
+ char const* fTag; // used for debugging
+};
+
+// A structure of RTP-specific state, kept so that we can cleanly
+// reclaim it:
+typedef struct RTPState {
+ char const* sdpDescription;
+ RTSPClient* rtspClient;
+ MediaSession* mediaSession;
+ ReadBufferQueue* audioBufferQueue;
+ ReadBufferQueue* videoBufferQueue;
+ int isMPEG; // TRUE for MPEG audio, video, or transport streams
+};
+
+extern "C" void demux_open_rtp(demuxer_t* demuxer) {
+ do {
+ TaskScheduler* scheduler = BasicTaskScheduler::createNew();
+ if (scheduler == NULL) break;
+ UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
+ if (env == NULL) break;
+
+ RTSPClient* rtspClient = NULL;
+ int isMPEG = 0;
+
+ // Look at the stream's 'priv' field to see if we were initiated
+ // via a SDP description:
+ char* sdpDescription = (char*)(demuxer->stream->priv);
+ if (sdpDescription == NULL) {
+ // We weren't given a SDP description directly, so assume that
+ // we were give a RTSP URL
+ char const* url = demuxer->stream->streaming_ctrl->url->url;
+
+ extern int verbose;
+ rtspClient = RTSPClient::createNew(*env, verbose);
+ if (rtspClient == NULL) {
+ fprintf(stderr, "Failed to create RTSP client: %s\n",
+ env->getResultMsg());
+ break;
+ }
+
+ sdpDescription = rtspClient->describeURL(url);
+ if (sdpDescription == NULL) {
+ fprintf(stderr, "Failed to get a SDP description from URL \"%s\": %s\n",
+ url, env->getResultMsg());
+ break;
+ }
+ }
+
+ // Now that we have a SDP description, create a MediaSession from it:
+ MediaSession* mediaSession = MediaSession::createNew(*env, sdpDescription);
+ if (mediaSession == NULL) break;
+
+ // Create RTP receivers (sources) for each subsession:
+ MediaSubsessionIterator iter(*mediaSession);
+ MediaSubsession* subsession;
+ MediaSubsession* audioSubsession = NULL;
+ MediaSubsession* videoSubsession = NULL;
+ while ((subsession = iter.next()) != NULL) {
+ // Ignore any subsession that's not audio or video:
+ if (strcmp(subsession->mediumName(), "audio") == 0) {
+ audioSubsession = subsession;
+ } else if (strcmp(subsession->mediumName(), "video") == 0) {
+ videoSubsession = subsession;
+ } else {
+ continue;
+ }
+
+ if (!subsession->initiate()) {
+ fprintf(stderr, "Failed to initiate \"%s/%s\" RTP subsession: %s\n", subsession->mediumName(), subsession->codecName(), env->getResultMsg());
+ } else {
+ fprintf(stderr, "Initiated \"%s/%s\" RTP subsession\n", subsession->mediumName(), subsession->codecName());
+
+ if (rtspClient != NULL) {
+ // Issue RTSP "SETUP" and "PLAY" commands on the chosen subsession:
+ if (!rtspClient->setupMediaSubsession(*subsession)) break;
+ if (!rtspClient->playMediaSubsession(*subsession)) break;
+ }
+
+ // Now that the subsession is ready to be read, do additional
+ // mplayer-specific initialization on it:
+ if (subsession == videoSubsession) {
+ // Create a dummy video stream header
+ // to make the main mplayer code happy:
+ sh_video_t* sh_video = new_sh_video(demuxer,0);
+ demux_stream_t* d_video = demuxer->video;
+ d_video->sh = sh_video; sh_video->ds = d_video;
+
+ // Map known video MIME types to the format code that this prog uses:
+ if (strcmp(subsession->codecName(), "MPV") == 0 ||
+ strcmp(subsession->codecName(), "MP1S") == 0 ||
+ strcmp(subsession->codecName(), "MP2T") == 0) {
+ isMPEG = 1;
+ } else if (strcmp(subsession->codecName(), "H263") == 0 ||
+ strcmp(subsession->codecName(), "H263-1998") == 0) {
+ sh_video->format = mmioFOURCC('H','2','6','3');
+ } else if (strcmp(subsession->codecName(), "H261") == 0) {
+ sh_video->format = mmioFOURCC('H','2','6','1');
+ } else if (strcmp(subsession->codecName(), "JPEG") == 0) {
+ sh_video->format = mmioFOURCC('M','J','P','G');
+ } else {
+ fprintf(stderr,
+ "Unknown mplayer format code for MIME type \"video/%s\"\n",
+ subsession->codecName());
+ }
+ } else if (subsession == audioSubsession) {
+ // Create a dummy audio stream header
+ // to make the main mplayer code happy:
+ sh_audio_t* sh_audio = new_sh_audio(demuxer,0);
+ sh_audio->wf = (WAVEFORMATEX*)calloc(1,sizeof(WAVEFORMATEX));
+ demux_stream_t* d_audio = demuxer->audio;
+ d_audio->sh = sh_audio; sh_audio->ds = d_audio;
+
+ // Map known audio MIME types to the format code that this prog uses:
+ if (strcmp(subsession->codecName(), "MPA") == 0 ||
+ strcmp(subsession->codecName(), "MPA-ROBUST") == 0 ||
+ strcmp(subsession->codecName(), "X-MP3-DRAFT-00") == 0) {
+ sh_audio->format = 0x50;
+ } else if (strcmp(subsession->codecName(), "AC3") == 0) {
+ sh_audio->format = 0x2000;
+ } else if (strcmp(subsession->codecName(), "PCMU") == 0) {
+ sh_audio->format = 0x7;
+ } else if (strcmp(subsession->codecName(), "PCMA") == 0) {
+ sh_audio->format = 0x6;
+ } else if (strcmp(subsession->codecName(), "GSM") == 0) {
+ sh_audio->format = 0x31;
+ } else {
+ fprintf(stderr,
+ "Unknown mplayer format code for MIME type \"audio/%s\"\n",
+ subsession->codecName());
+ }
+ }
+ }
+ }
+
+ // Hack: Create a 'RTPState' structure containing the state that
+ // we just created, and store it in the demuxer's 'priv' field:
+ RTPState* rtpState = new RTPState;
+ rtpState->sdpDescription = sdpDescription;
+ rtpState->rtspClient = rtspClient;
+ rtpState->mediaSession = mediaSession;
+ rtpState->audioBufferQueue
+ = new ReadBufferQueue(audioSubsession, demuxer, "audio");
+ rtpState->videoBufferQueue
+ = new ReadBufferQueue(videoSubsession, demuxer, "video");
+ rtpState->isMPEG = isMPEG;
+
+ demuxer->priv = rtpState;
+ } while (0);
+}
+
+extern "C" int demux_is_mpeg_rtp_stream(demuxer_t* demuxer) {
+ // Get the RTP state that was stored in the demuxer's 'priv' field:
+ RTPState* rtpState = (RTPState*)(demuxer->priv);
+ return rtpState->isMPEG;
+}
+
+static Boolean deliverBufferIfAvailable(ReadBufferQueue* bufferQueue,
+ demux_stream_t* ds); // forward
+
+extern "C" int demux_rtp_fill_buffer(demuxer_t* demuxer, demux_stream_t* ds) {
+ // Get a filled-in "demux_packet" from the RTP source, and deliver it.
+ // Note that this is called as a synchronous read operation, so it needs
+ // to block in the (hopefully infrequent) case where no packet is
+ // immediately available.
+
+ // Begin by finding the buffer queue that we want to read from:
+ // (Get this from the RTP state, which we stored in
+ // the demuxer's 'priv' field)
+ RTPState* rtpState = (RTPState*)(demuxer->priv);
+ ReadBufferQueue* bufferQueue = NULL;
+ if (ds == demuxer->video) {
+ bufferQueue = rtpState->videoBufferQueue;
+ } else if (ds == demuxer->audio) {
+ bufferQueue = rtpState->audioBufferQueue;
+ } else {
+ fprintf(stderr, "demux_rtp_fill_buffer: internal error: unknown stream\n");
+ return 0;
+ }
+
+ if (bufferQueue == NULL || bufferQueue->readSource() == NULL) {
+ fprintf(stderr, "demux_rtp_fill_buffer failed: no appropriate RTP subsession has been set up\n");
+ return 0;
+ }
+
+ // Check whether there's a full buffer to deliver to the client:
+ bufferQueue->blockingFlag = 0;
+ while (!deliverBufferIfAvailable(bufferQueue, ds)) {
+ // Because we weren't able to deliver a buffer to the client immediately,
+ // block myself until one comes available:
+ TaskScheduler& scheduler
+ = bufferQueue->readSource()->envir().taskScheduler();
+ scheduler.blockMyself(&bufferQueue->blockingFlag);
+ }
+
+ if (demuxer->stream->eof) return 0; // source stream has closed down
+ return 1;
+}
+
+extern "C" void demux_close_rtp(demuxer_t* demuxer) {
+ // Reclaim all RTP-related state:
+
+ // Get the RTP state that was stored in the demuxer's 'priv' field:
+ RTPState* rtpState = (RTPState*)(demuxer->priv);
+ UsageEnvironment* env = NULL;
+ TaskScheduler* scheduler = NULL;
+ if (rtpState->mediaSession != NULL) {
+ env = &(rtpState->mediaSession->envir());
+ scheduler = &(env->taskScheduler());
+ }
+ Medium::close(rtpState->mediaSession);
+ Medium::close(rtpState->rtspClient);
+ delete rtpState->audioBufferQueue;
+ delete rtpState->videoBufferQueue;
+ delete rtpState->sdpDescription;
+ delete rtpState;
+
+ delete env; delete scheduler;
+}
+
+////////// Extra routines that help implement the above interface functions:
+
+static void scheduleNewBufferRead(ReadBufferQueue* bufferQueue); // forward
+
+static Boolean deliverBufferIfAvailable(ReadBufferQueue* bufferQueue,
+ demux_stream_t* ds) {
+ Boolean deliveredBuffer = False;
+ ReadBuffer* readBuffer = bufferQueue->dequeue();
+ if (readBuffer != NULL) {
+ // Append the packet to the reader's DS stream:
+ ds_add_packet(ds, readBuffer->dp());
+ deliveredBuffer = True;
+ }
+
+ // Arrange to read a new packet into this queue:
+ scheduleNewBufferRead(bufferQueue);
+
+ return deliveredBuffer;
+}
+
+static void afterReading(void* clientData, unsigned frameSize,
+ struct timeval presentationTime); // forward
+static void onSourceClosure(void* clientData); // forward
+
+static void scheduleNewBufferRead(ReadBufferQueue* bufferQueue) {
+ if (bufferQueue->readSource()->isCurrentlyAwaitingData()) return;
+ // a read from this source is already in progress
+
+ // Allocate a new packet buffer, and arrange to read into it:
+ unsigned const bufferSize = 30000; // >= the largest conceivable RTP packet
+ demux_packet_t* dp = new_demux_packet(bufferSize);
+ if (dp == NULL) return;
+ ReadBuffer* readBuffer = new ReadBuffer(bufferQueue, dp);
+
+ // Schedule the read operation:
+ bufferQueue->readSource()->getNextFrame(dp->buffer, bufferSize,
+ afterReading, readBuffer,
+ onSourceClosure, readBuffer);
+}
+
+static void afterReading(void* clientData, unsigned frameSize,
+ struct timeval /*presentationTime*/) {
+ ReadBuffer* readBuffer = (ReadBuffer*)clientData;
+ ReadBufferQueue* bufferQueue = readBuffer->ourQueue();
+ demuxer_t* demuxer = bufferQueue->ourDemuxer();
+
+ if (frameSize > 0) demuxer->stream->eof = 0;
+
+ demux_packet_t* dp = readBuffer->dp();
+ dp->len = frameSize;
+ dp->pts = 0;
+ dp->pos = demuxer->filepos;
+ demuxer->filepos += frameSize;
+ if (!readBuffer->enqueue()) {
+ // The queue is full, so discard the buffer:
+ delete readBuffer;
+ }
+
+ // Signal any pending 'blockMyself()' call on this queue:
+ bufferQueue->blockingFlag = ~0;
+
+ // Finally, arrange to do another read, if appropriate
+ scheduleNewBufferRead(bufferQueue);
+}
+
+static void onSourceClosure(void* clientData) {
+ ReadBuffer* readBuffer = (ReadBuffer*)clientData;
+ ReadBufferQueue* bufferQueue = readBuffer->ourQueue();
+ demuxer_t* demuxer = bufferQueue->ourDemuxer();
+
+ demuxer->stream->eof = 1;
+
+ // Signal any pending 'blockMyself()' call on this queue:
+ bufferQueue->blockingFlag = ~0;
+}
+
+////////// "ReadBuffer" and "ReadBufferQueue" implementation:
+
+#define MAX_QUEUE_SIZE 5
+
+ReadBuffer::ReadBuffer(ReadBufferQueue* ourQueue, demux_packet_t* dp)
+ : next(NULL), fDP(dp), fOurQueue(ourQueue) {
+}
+
+Boolean ReadBuffer::enqueue() {
+ if (fOurQueue->counter >= MAX_QUEUE_SIZE) {
+ // This queue is full. Clear out an old entry from it, so that
+ // this new one will fit:
+ while (fOurQueue->counter >= MAX_QUEUE_SIZE) {
+ delete fOurQueue->dequeue();
+ }
+ }
+
+ // Add ourselves to the tail of our queue:
+ if (fOurQueue->tail == NULL) {
+ fOurQueue->head = this;
+ } else {
+ fOurQueue->tail->next = this;
+ }
+ fOurQueue->tail = this;
+ ++fOurQueue->counter;
+
+ return True;
+}
+
+ReadBuffer::~ReadBuffer() {
+ free_demux_packet(fDP);
+ delete next;
+}
+
+ReadBufferQueue::ReadBufferQueue(MediaSubsession* subsession,
+ demuxer_t* demuxer, char const* tag)
+ : head(NULL), tail(NULL), counter(0),
+ fReadSource(subsession == NULL ? NULL : subsession->readSource()),
+ fOurDemuxer(demuxer), fTag(strdup(tag)) {
+}
+
+ReadBufferQueue::~ReadBufferQueue() {
+ delete head;
+ delete fTag;
+}
+
+ReadBuffer* ReadBufferQueue::dequeue() {
+ ReadBuffer* readBuffer = head;
+ if (readBuffer != NULL) {
+ head = readBuffer->next;
+ if (head == NULL) tail = NULL;
+ --counter;
+ readBuffer->next = NULL;
+ }
+ return readBuffer;
+}
diff --git a/libmpdemux/demux_rtp.h b/libmpdemux/demux_rtp.h
new file mode 100644
index 0000000000..5e46191d73
--- /dev/null
+++ b/libmpdemux/demux_rtp.h
@@ -0,0 +1,33 @@
+#ifndef _DEMUX_RTP_H
+#define _DEMUX_RTP_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef __STREAM_H
+#include "stream.h"
+#endif
+#ifndef __DEMUXER_H
+#include "demuxer.h"
+#endif
+
+// Open a SDP file:
+stream_t* stream_open_sdp(int fd, off_t fileSize, int* file_format);
+
+// Open a RTSP URL:
+int rtsp_streaming_start(stream_t* stream);
+
+// Open a RTP demuxer (which was initiated either from a SDP file,
+// or from a RTSP URL):
+void demux_open_rtp(demuxer_t* demuxer);
+
+// Test whether a RTP demuxer is for a MPEG stream:
+int demux_is_mpeg_rtp_stream(demuxer_t* demuxer);
+
+// Read from a RTP demuxer:
+int demux_rtp_fill_buffer(demuxer_t *demux, demux_stream_t* ds);
+
+// Close a RTP demuxer
+void demux_close_rtp(demuxer_t* demuxer);
+
+#endif
diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c
index 1ad4090f4b..826293ff3d 100644
--- a/libmpdemux/demuxer.c
+++ b/libmpdemux/demuxer.c
@@ -150,6 +150,7 @@ extern void demux_close_fli(demuxer_t* demuxer);
extern void demux_close_nuv(demuxer_t* demuxer);
extern void demux_close_audio(demuxer_t* demuxer);
extern void demux_close_ogg(demuxer_t* demuxer);
+extern void demux_close_rtp(demuxer_t* demuxer);
extern void demux_close_demuxers(demuxer_t* demuxer);
extern void demux_close_avi(demuxer_t *demuxer);
@@ -179,6 +180,10 @@ void free_demuxer(demuxer_t *demuxer){
demux_close_audio(demuxer); break;
case DEMUXER_TYPE_OGG:
demux_close_ogg(demuxer); break;
+#ifdef STREAMING_LIVE_DOT_COM
+ case DEMUXER_TYPE_RTP:
+ demux_close_rtp(demuxer); break;
+#endif
case DEMUXER_TYPE_DEMUXERS:
demux_close_demuxers(demuxer); return;
case DEMUXER_TYPE_AVI:
@@ -255,6 +260,7 @@ int demux_mov_fill_buffer(demuxer_t *demux,demux_stream_t* ds);
int demux_vivo_fill_buffer(demuxer_t *demux);
int demux_real_fill_buffer(demuxer_t *demuxer);
int demux_nuv_fill_buffer(demuxer_t *demux);
+int demux_rtp_fill_buffer(demuxer_t *demux, demux_stream_t* ds);
#ifdef USE_TV
#include "tv.h"
extern tvi_handle_t *tv_handler;
@@ -296,6 +302,9 @@ int demux_fill_buffer(demuxer_t *demux,demux_stream_t *ds){
case DEMUXER_TYPE_DEMUXERS: return demux_demuxers_fill_buffer(demux,ds);
case DEMUXER_TYPE_OGG: return demux_ogg_fill_buffer(demux);
case DEMUXER_TYPE_RAWAUDIO: return demux_rawaudio_fill_buffer(demux,ds);
+#ifdef STREAMING_LIVE_DOT_COM
+ case DEMUXER_TYPE_RTP: return demux_rtp_fill_buffer(demux, ds);
+#endif
}
return 0;
}
@@ -793,6 +802,11 @@ if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_AUDIO){
demuxer = NULL;
}
}
+//=============== Try to open as a RTP stream): ===========
+ if(file_format==DEMUXER_TYPE_RTP) {
+ demuxer=new_demuxer(stream,DEMUXER_TYPE_RTP,audio_id,video_id,dvdsub_id);
+ }
+
//=============== Unknown, exiting... ===========================
if(file_format==DEMUXER_TYPE_UNKNOWN || demuxer == NULL){
mp_msg(MSGT_DEMUXER,MSGL_ERR,MSGTR_FormatNotRecognized);
@@ -949,6 +963,12 @@ switch(file_format){
break;
}
#endif
+#ifdef STREAMING_LIVE_DOT_COM
+ case DEMUXER_TYPE_RTP: {
+ demux_open_rtp(demuxer);
+ break;
+ }
+#endif
} // switch(file_format)
pts_from_bps=0; // !!!
return demuxer;
diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h
index 255c525062..6f8b9c5aa6 100644
--- a/libmpdemux/demuxer.h
+++ b/libmpdemux/demuxer.h
@@ -29,10 +29,11 @@
#define DEMUXER_TYPE_OGG 18
#define DEMUXER_TYPE_BMP 19
#define DEMUXER_TYPE_RAWAUDIO 20
+#define DEMUXER_TYPE_RTP 21
// This should always match the higest demuxer type number.
// Unless you want to disallow users to force the demuxer to some types
#define DEMUXER_TYPE_MIN 0
-#define DEMUXER_TYPE_MAX 20
+#define DEMUXER_TYPE_MAX 21
#define DEMUXER_TYPE_DEMUXERS (1<<16)
// A virtual demuxer type for the network code
@@ -123,9 +124,9 @@ typedef struct demuxer_st {
} demuxer_t;
inline static demux_packet_t* new_demux_packet(int len){
- demux_packet_t* dp=malloc(sizeof(demux_packet_t));
+ demux_packet_t* dp=(demux_packet_t*)malloc(sizeof(demux_packet_t));
dp->len=len;
- dp->buffer=len?malloc(len+8):NULL;
+ dp->buffer=len?(unsigned char*)malloc(len+8):NULL;
dp->next=NULL;
dp->pts=0;
dp->pos=0;
@@ -136,7 +137,7 @@ inline static demux_packet_t* new_demux_packet(int len){
}
inline static demux_packet_t* clone_demux_packet(demux_packet_t* pack){
- demux_packet_t* dp=malloc(sizeof(demux_packet_t));
+ demux_packet_t* dp=(demux_packet_t*)malloc(sizeof(demux_packet_t));
while(pack->master) pack=pack->master; // find the master
memcpy(dp,pack,sizeof(demux_packet_t));
dp->next=NULL;
diff --git a/libmpdemux/network.c b/libmpdemux/network.c
index 512e3ebe5a..f02cef1b5c 100644
--- a/libmpdemux/network.c
+++ b/libmpdemux/network.c
@@ -25,7 +25,9 @@
#include "http.h"
#include "url.h"
#include "asf.h"
+#ifndef STREAMING_LIVE_DOT_COM
#include "rtp.h"
+#endif
#include "../version.h"
@@ -113,6 +115,7 @@ streaming_ctrl_free( streaming_ctrl_t *streaming_ctrl ) {
free( streaming_ctrl );
}
+#ifndef STREAMING_LIVE_DOT_COM
int
read_rtp_from_server(int fd, char *buffer, int length) {
struct rtpheader rh;
@@ -132,6 +135,7 @@ read_rtp_from_server(int fd, char *buffer, int length) {
memcpy(buffer, data, len);
return(len);
}
+#endif
// Connect to a server using a TCP connection
int
@@ -454,10 +458,18 @@ extension=NULL;
// Checking for RTSP
if( !strcasecmp(url->protocol, "rtsp") ) {
- mp_msg(MSGT_NETWORK,MSGL_ERR,"RTSP protocol not yet implemented!\n");
+#ifdef STREAMING_LIVE_DOT_COM
+ *file_format = DEMUXER_TYPE_RTP;
+ return 0;
+#else
+ mp_msg(MSGT_NETWORK,MSGL_ERR,"RTSP protocol support requires the \"LIVE.COM Streaming Media\" libraries!\n");
return -1;
+#endif
}
+#ifndef STREAMING_LIVE_DOT_COM
+ // Old, hacked RTP support, which works for MPEG Program Streams
+ // RTP streams only:
// Checking for RTP
if( !strcasecmp(url->protocol, "rtp") ) {
if( url->port==0 ) {
@@ -466,6 +478,7 @@ extension=NULL;
}
return 0;
}
+#endif
// Checking for ASF
if( !strncasecmp(url->protocol, "mms", 3) ) {
@@ -688,6 +701,7 @@ nop_streaming_start( stream_t *stream ) {
return 0;
}
+#ifndef STREAMING_LIVE_DOT_COM
// Start listening on a UDP port. If multicast, join the group.
int
rtp_open_socket( URL_t *url ) {
@@ -789,6 +803,7 @@ rtp_streaming_start( stream_t *stream ) {
streaming_ctrl->status = streaming_playing_e;
return 0;
}
+#endif
int
streaming_start(stream_t *stream, int *demuxer_type, URL_t *url) {
@@ -820,6 +835,7 @@ streaming_start(stream_t *stream, int *demuxer_type, URL_t *url) {
}
}
+#ifndef STREAMING_LIVE_DOT_COM
// For RTP streams, we usually don't know the stream type until we open it.
if( !strcasecmp( stream->streaming_ctrl->url->protocol, "rtp")) {
if(stream->fd >= 0) {
@@ -829,6 +845,7 @@ streaming_start(stream_t *stream, int *demuxer_type, URL_t *url) {
stream->fd = -1;
ret = rtp_streaming_start( stream );
} else
+#endif
// For connection-oriented streams, we can usually determine the streaming type.
switch( *demuxer_type ) {
case DEMUXER_TYPE_ASF:
@@ -840,6 +857,15 @@ streaming_start(stream_t *stream, int *demuxer_type, URL_t *url) {
mp_msg(MSGT_NETWORK,MSGL_ERR,"asf_streaming_start failed\n");
}
break;
+#ifdef STREAMING_LIVE_DOT_COM
+ case DEMUXER_TYPE_RTP:
+ // RTSP/RTP streaming is handled separately:
+ ret = rtsp_streaming_start( stream );
+ if( ret<0 ) {
+ mp_msg(MSGT_NETWORK,MSGL_ERR,"rtsp_rtp_streaming_start failed\n");
+ }
+ break;
+#endif
case DEMUXER_TYPE_MPEG_ES:
case DEMUXER_TYPE_MPEG_PS:
case DEMUXER_TYPE_AVI:
diff --git a/libmpdemux/open.c b/libmpdemux/open.c
index c202895dcf..6d4473ca46 100644
--- a/libmpdemux/open.c
+++ b/libmpdemux/open.c
@@ -22,6 +22,10 @@
#ifdef STREAMING
#include "url.h"
#include "network.h"
+#ifdef STREAMING_LIVE_DOT_COM
+#include "demux_rtp.h"
+int isSDPFile = 0;
+#endif
static URL_t* url;
#endif
@@ -471,6 +475,14 @@ tv_err:
#else
mp_msg(MSGT_OPEN,MSGL_V,"File size is %u bytes\n", (unsigned int)len);
#endif
+
+#ifdef STREAMING_LIVE_DOT_COM
+ // Check for a special case: a SDP file:
+ if (isSDPFile) {
+ return stream_open_sdp(f, len, file_format);
+ }
+#endif
+
stream=new_stream(f,STREAMTYPE_FILE);
stream->end_pos=len;
return stream;
diff --git a/libmpdemux/video.c b/libmpdemux/video.c
index 6379635e1a..fbd9a2a012 100644
--- a/libmpdemux/video.c
+++ b/libmpdemux/video.c
@@ -79,6 +79,13 @@ switch(d_video->demuxer->file_format){
#endif
break;
}
+#ifdef STREAMING_LIVE_DOT_COM
+ case DEMUXER_TYPE_RTP:
+ // If the RTP stream is a MPEG stream, then we use this code to check
+ // for MPEG headers:
+ if (!demux_is_mpeg_rtp_stream(d_video->demuxer)) break;
+ // otherwise fall through to...
+#endif
case DEMUXER_TYPE_MPEG_ES:
case DEMUXER_TYPE_MPEG_PS: {
//mpeg_header_parser:
@@ -211,7 +218,11 @@ int video_read_frame(sh_video_t* sh_video,float* frame_time_ptr,unsigned char**
*start=NULL;
- if(demuxer->file_format==DEMUXER_TYPE_MPEG_ES || demuxer->file_format==DEMUXER_TYPE_MPEG_PS){
+ if(demuxer->file_format==DEMUXER_TYPE_MPEG_ES || demuxer->file_format==DEMUXER_TYPE_MPEG_PS
+#ifdef STREAMING_LIVE_DOT_COM
+ || (demuxer->file_format==DEMUXER_TYPE_RTP && demux_is_mpeg_rtp_stream(demuxer))
+#endif
+ ){
int in_frame=0;
//float newfps;
//videobuf_len=0;