mirror of
https://github.com/mpv-player/mpv
synced 2024-12-27 01:22:30 +00:00
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
This commit is contained in:
parent
11bc29e6cc
commit
fa788640e2
@ -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},
|
||||
|
52
configure
vendored
52
configure
vendored
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
431
libmpdemux/demux_rtp.cpp
Normal file
431
libmpdemux/demux_rtp.cpp
Normal file
@ -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;
|
||||
}
|
33
libmpdemux/demux_rtp.h
Normal file
33
libmpdemux/demux_rtp.h
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user