From 06d22fab96dae0f6ec062058c33ef3b76a7dcd04 Mon Sep 17 00:00:00 2001 From: bertrand Date: Mon, 3 Feb 2003 10:27:50 +0000 Subject: Restruct by Ross Finlayson The code now supports 'QuickTime generic' RTP streams (the "X-QT" MIME type), which - thanks to the QuickTime codecs - makes it possible to play more QuickTime RTP streams. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@9251 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libmpdemux/Makefile | 2 +- libmpdemux/demux_rtp.cpp | 264 +++++++++++++++------------------------- libmpdemux/demux_rtp_codec.cpp | 203 ++++++++++++++++++++++++++++++ libmpdemux/demux_rtp_internal.h | 36 ++++++ 4 files changed, 341 insertions(+), 164 deletions(-) create mode 100644 libmpdemux/demux_rtp_codec.cpp create mode 100644 libmpdemux/demux_rtp_internal.h (limited to 'libmpdemux') diff --git a/libmpdemux/Makefile b/libmpdemux/Makefile index a52176b801..42d2ca21cf 100644 --- a/libmpdemux/Makefile +++ b/libmpdemux/Makefile @@ -10,7 +10,7 @@ endif ifeq ($(STREAMING),yes) SRCS += asf_streaming.c url.c http.c network.c asf_mmst_streaming.c pnm.c ifeq ($(STREAMING_LIVE_DOT_COM),yes) -CPLUSPLUSSRCS = demux_rtp.cpp +CPLUSPLUSSRCS = demux_rtp.cpp demux_rtp_codec.cpp CPLUSPLUSINCLUDE = -I$(LIVE_LIB_DIR)/liveMedia/include CPLUSPLUSINCLUDE += -I$(LIVE_LIB_DIR)/UsageEnvironment/include CPLUSPLUSINCLUDE += -I$(LIVE_LIB_DIR)/BasicUsageEnvironment/include diff --git a/libmpdemux/demux_rtp.cpp b/libmpdemux/demux_rtp.cpp index 1b8a8057b6..42bb5bb67c 100644 --- a/libmpdemux/demux_rtp.cpp +++ b/libmpdemux/demux_rtp.cpp @@ -1,15 +1,16 @@ +////////// Routines (with C-linkage) that interface between "MPlayer" +////////// and the "LIVE.COM Streaming Media" libraries: + extern "C" { #include "demux_rtp.h" #include "stheader.h" } +#include "demux_rtp_internal.h" #include "BasicUsageEnvironment.hh" #include "liveMedia.hh" #include -////////// 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; @@ -91,7 +92,7 @@ typedef struct RTPState { MediaSession* mediaSession; ReadBufferQueue* audioBufferQueue; ReadBufferQueue* videoBufferQueue; - int isMPEG; // TRUE for MPEG audio, video, or transport streams + unsigned flags; struct timeval firstSyncTime; }; @@ -109,7 +110,7 @@ extern "C" void demux_open_rtp(demuxer_t* demuxer) { if (env == NULL) break; RTSPClient* rtspClient = NULL; - int isMPEG = 0; + unsigned flags = 0; // Look at the stream's 'priv' field to see if we were initiated // via a SDP description: @@ -120,7 +121,7 @@ extern "C" void demux_open_rtp(demuxer_t* demuxer) { char const* url = demuxer->stream->streaming_ctrl->url->url; extern int verbose; - rtspClient = RTSPClient::createNew(*env, verbose, "mplayer"); + rtspClient = RTSPClient::createNew(*env, verbose, "MPlayer"); if (rtspClient == NULL) { fprintf(stderr, "Failed to create RTSP client: %s\n", env->getResultMsg()); @@ -139,17 +140,26 @@ extern "C" void demux_open_rtp(demuxer_t* demuxer) { MediaSession* mediaSession = MediaSession::createNew(*env, sdpDescription); if (mediaSession == NULL) break; + + // Create a 'RTPState' structure containing the state that we just created, + // and store it in the demuxer's 'priv' field, for future reference: + RTPState* rtpState = new RTPState; + rtpState->sdpDescription = sdpDescription; + rtpState->rtspClient = rtspClient; + rtpState->mediaSession = mediaSession; + rtpState->firstSyncTime.tv_sec = rtpState->firstSyncTime.tv_usec = 0; + demuxer->priv = rtpState; + // Create RTP receivers (sources) for each subsession: MediaSubsessionIterator iter(*mediaSession); MediaSubsession* subsession; - MediaSubsession* audioSubsession = NULL; - MediaSubsession* videoSubsession = NULL; + unsigned streamType = 0; // 0 => video; 1 => audio while ((subsession = iter.next()) != NULL) { // Ignore any subsession that's not audio or video: if (strcmp(subsession->mediumName(), "audio") == 0) { - audioSubsession = subsession; + streamType = 1; } else if (strcmp(subsession->mediumName(), "video") == 0) { - videoSubsession = subsession; + streamType = 0; } else { continue; } @@ -167,137 +177,31 @@ extern "C" void demux_open_rtp(demuxer_t* demuxer) { } // 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); - BITMAPINFOHEADER* bih - = (BITMAPINFOHEADER*)calloc(1,sizeof(BITMAPINFOHEADER)); - bih->biSize = sizeof(BITMAPINFOHEADER); - sh_video->bih = bih; - demux_stream_t* d_video = demuxer->video; - d_video->sh = sh_video; sh_video->ds = d_video; - - // If we happen to know the subsession's video frame rate, set it, - // so that the user doesn't have to give the "-fps" option instead. - int fps = (int)(subsession->videoFPS()); - if (fps != 0) sh_video->fps = fps; - - // Map known video MIME types to the BITMAPINFOHEADER parameters - // that this program uses. (Note that not all types need all - // of the parameters to be set.) - 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) { - bih->biCompression = sh_video->format - = mmioFOURCC('H','2','6','3'); - } else if (strcmp(subsession->codecName(), "H261") == 0) { - bih->biCompression = sh_video->format - = mmioFOURCC('H','2','6','1'); - } 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); - WAVEFORMATEX* wf = (WAVEFORMATEX*)calloc(1,sizeof(WAVEFORMATEX)); - sh_audio->wf = wf; - demux_stream_t* d_audio = demuxer->audio; - d_audio->sh = sh_audio; sh_audio->ds = d_audio; - - // Map known audio MIME types to the WAVEFORMATEX parameters - // that this program uses. (Note that not all types need all - // of the parameters to be set.) - wf->nSamplesPerSec - = subsession->rtpSource()->timestampFrequency(); // by default - if (strcmp(subsession->codecName(), "MPA") == 0 || - strcmp(subsession->codecName(), "MPA-ROBUST") == 0 || - strcmp(subsession->codecName(), "X-MP3-DRAFT-00") == 0) { - wf->wFormatTag = sh_audio->format = 0x55; - // Note: 0x55 is for layer III, but should work for I,II also - wf->nSamplesPerSec = 0; // sample rate is deduced from the data - } else if (strcmp(subsession->codecName(), "AC3") == 0) { - wf->wFormatTag = sh_audio->format = 0x2000; - wf->nSamplesPerSec = 0; // sample rate is deduced from the data - } else if (strcmp(subsession->codecName(), "PCMU") == 0) { - wf->wFormatTag = sh_audio->format = 0x7; - wf->nChannels = 1; - wf->nAvgBytesPerSec = 8000; - wf->nBlockAlign = 1; - wf->wBitsPerSample = 8; - wf->cbSize = 0; - } else if (strcmp(subsession->codecName(), "PCMA") == 0) { - wf->wFormatTag = sh_audio->format = 0x6; - wf->nChannels = 1; - wf->nAvgBytesPerSec = 8000; - wf->nBlockAlign = 1; - wf->wBitsPerSample = 8; - wf->cbSize = 0; - } else if (strcmp(subsession->codecName(), "GSM") == 0) { - wf->wFormatTag = sh_audio->format = mmioFOURCC('a','g','s','m'); - wf->nChannels = 1; - wf->nAvgBytesPerSec = 1650; - wf->nBlockAlign = 33; - wf->wBitsPerSample = 16; - wf->cbSize = 0; - } else if (strcmp(subsession->codecName(), "MP4A-LATM") == 0) { - wf->wFormatTag = sh_audio->format = mmioFOURCC('m','p','4','a'); -#ifndef HAVE_FAAD - fprintf(stderr, "WARNING: Playing MPEG-4 (AAC) Audio requires the \"faad\" library!\n"); -#endif -#if (LIVEMEDIA_LIBRARY_VERSION_INT < 1042761600) - fprintf(stderr, "WARNING: This audio stream might not play correctly. Please upgrade to version \"2003.01.17\" or later of the \"LIVE.COM Streaming Media\" libraries.\n"); -#else - // For the codec to work correctly, it needs "AudioSpecificConfig" - // data, which is parsed from the "StreamMuxConfig" string that - // was present (hopefully) in the SDP description: - unsigned codecdata_len; - sh_audio->codecdata - = parseStreamMuxConfigStr(subsession->fmtp_config(), - codecdata_len); - sh_audio->codecdata_len = codecdata_len; -#endif - } else { - fprintf(stderr, - "Unknown mplayer format code for MIME type \"audio/%s\"\n", - subsession->codecName()); - } + // MPlayer codec-specific initialization on it: + if (streamType == 0) { // video + rtpState->videoBufferQueue + = new ReadBufferQueue(subsession, demuxer, "video"); + rtpCodecInitialize_video(demuxer, subsession, flags); + } else { // audio + rtpState->audioBufferQueue + = new ReadBufferQueue(subsession, demuxer, "audio"); + rtpCodecInitialize_audio(demuxer, subsession, flags); } } } - - // 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; - rtpState->firstSyncTime.tv_sec = rtpState->firstSyncTime.tv_usec = 0; - - demuxer->priv = rtpState; + rtpState->flags = flags; } 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; + + return (rtpState->flags&RTPSTATE_IS_MPEG) != 0; } -static Boolean deliverBufferIfAvailable(ReadBufferQueue* bufferQueue, - demux_stream_t* ds); // forward +static ReadBuffer* getBuffer(ReadBufferQueue* bufferQueue, + demuxer_t* demuxer); // 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. @@ -324,24 +228,46 @@ extern "C" int demux_rtp_fill_buffer(demuxer_t* demuxer, demux_stream_t* ds) { 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(); -#if USAGEENVIRONMENT_LIBRARY_VERSION_INT >= 1038614400 - scheduler.doEventLoop(&bufferQueue->blockingFlag); -#else - scheduler.blockMyself(&bufferQueue->blockingFlag); -#endif - } + ReadBuffer* readBuffer = getBuffer(bufferQueue, demuxer); // blocking + if (readBuffer != NULL) ds_add_packet(ds, readBuffer->dp()); if (demuxer->stream->eof) return 0; // source stream has closed down + return 1; } +Boolean awaitRTPPacket(demuxer_t* demuxer, unsigned streamType, + unsigned char*& packetData, unsigned& packetDataLen) { + // 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 (streamType == 0) { + bufferQueue = rtpState->videoBufferQueue; + } else if (streamType == 1) { + bufferQueue = rtpState->audioBufferQueue; + } else { + fprintf(stderr, "awaitRTPPacket: internal error: unknown streamType %d\n", + streamType); + return False; + } + + if (bufferQueue == NULL || bufferQueue->readSource() == NULL) { + fprintf(stderr, "awaitRTPPacket failed: no appropriate RTP subsession has been set up\n"); + return False; + } + + ReadBuffer* readBuffer = getBuffer(bufferQueue, demuxer); // blocking + if (readBuffer == NULL) return False; + + demux_packet_t* dp = readBuffer->dp(); + packetData = dp->buffer; + packetDataLen = dp->len; + + return True; +} + extern "C" void demux_close_rtp(demuxer_t* demuxer) { // Reclaim all RTP-related state: @@ -366,24 +292,6 @@ extern "C" void demux_close_rtp(demuxer_t* demuxer) { ////////// 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 @@ -444,7 +352,7 @@ static void afterReading(void* clientData, unsigned frameSize, delete readBuffer; } - // Signal any pending 'blockMyself()' call on this queue: + // Signal any pending 'doEventLoop()' call on this queue: bufferQueue->blockingFlag = ~0; // Finally, arrange to do another read, if appropriate @@ -458,10 +366,40 @@ static void onSourceClosure(void* clientData) { demuxer->stream->eof = 1; - // Signal any pending 'blockMyself()' call on this queue: + // Signal any pending 'doEventLoop()' call on this queue: bufferQueue->blockingFlag = ~0; } +static ReadBuffer* getBufferIfAvailable(ReadBufferQueue* bufferQueue) { + ReadBuffer* readBuffer = bufferQueue->dequeue(); + + // Arrange to read a new packet into this queue: + scheduleNewBufferRead(bufferQueue); + + return readBuffer; +} + +static ReadBuffer* getBuffer(ReadBufferQueue* bufferQueue, + demuxer_t* demuxer) { + // Check whether there's a full buffer to deliver to the client: + bufferQueue->blockingFlag = 0; + ReadBuffer* readBuffer; + while ((readBuffer = getBufferIfAvailable(bufferQueue)) == NULL + && !demuxer->stream->eof) { + // 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(); +#if USAGEENVIRONMENT_LIBRARY_VERSION_INT >= 1038614400 + scheduler.doEventLoop(&bufferQueue->blockingFlag); +#else + scheduler.blockMyself(&bufferQueue->blockingFlag); +#endif + } + + return readBuffer; +} + ////////// "ReadBuffer" and "ReadBufferQueue" implementation: #define MAX_QUEUE_SIZE 5 diff --git a/libmpdemux/demux_rtp_codec.cpp b/libmpdemux/demux_rtp_codec.cpp new file mode 100644 index 0000000000..28e7f2e184 --- /dev/null +++ b/libmpdemux/demux_rtp_codec.cpp @@ -0,0 +1,203 @@ +////////// Codec-specific routines used to interface between "MPlayer" +////////// and the "LIVE.COM Streaming Media" libraries: + +#include "demux_rtp_internal.h" +extern "C" { +#include "stheader.h" +} + +static Boolean +parseQTState_video(QuickTimeGenericRTPSource::QTState const& qtState, + unsigned& fourcc); // forward +static Boolean +parseQTState_audio(QuickTimeGenericRTPSource::QTState const& qtState, + unsigned& fourcc, unsigned& numChannels); // forward + +void rtpCodecInitialize_video(demuxer_t* demuxer, + MediaSubsession* subsession, + unsigned& flags) { + flags = 0; + // Create a dummy video stream header + // to make the main MPlayer code happy: + sh_video_t* sh_video = new_sh_video(demuxer,0); + BITMAPINFOHEADER* bih + = (BITMAPINFOHEADER*)calloc(1,sizeof(BITMAPINFOHEADER)); + bih->biSize = sizeof(BITMAPINFOHEADER); + sh_video->bih = bih; + demux_stream_t* d_video = demuxer->video; + d_video->sh = sh_video; sh_video->ds = d_video; + + // If we happen to know the subsession's video frame rate, set it, + // so that the user doesn't have to give the "-fps" option instead. + int fps = (int)(subsession->videoFPS()); + if (fps != 0) sh_video->fps = fps; + + // Map known video MIME types to the BITMAPINFOHEADER parameters + // that this program uses. (Note that not all types need all + // of the parameters to be set.) + if (strcmp(subsession->codecName(), "MPV") == 0 || + strcmp(subsession->codecName(), "MP1S") == 0 || + strcmp(subsession->codecName(), "MP2T") == 0) { + flags |= RTPSTATE_IS_MPEG; + } else if (strcmp(subsession->codecName(), "H263") == 0 || + strcmp(subsession->codecName(), "H263-1998") == 0) { + bih->biCompression = sh_video->format + = mmioFOURCC('H','2','6','3'); + } else if (strcmp(subsession->codecName(), "H261") == 0) { + bih->biCompression = sh_video->format + = mmioFOURCC('H','2','6','1'); + } else if (strcmp(subsession->codecName(), "X-QT") == 0 || + strcmp(subsession->codecName(), "X-QUICKTIME") == 0) { + // QuickTime generic RTP format, as described in + // http://developer.apple.com/quicktime/icefloe/dispatch026.html + + // We can't initialize this stream until we've received the first packet + // that has QuickTime "sdAtom" information in the header. So, keep + // reading packets until we get one: + unsigned char* packetData; unsigned packetDataLen; + QuickTimeGenericRTPSource* qtRTPSource + = (QuickTimeGenericRTPSource*)(subsession->rtpSource()); + unsigned fourcc; + do { + if (!awaitRTPPacket(demuxer, 0 /*video*/, packetData, packetDataLen)) { + return; + } + } while (!parseQTState_video(qtRTPSource->qtState, fourcc)); + + bih->biCompression = sh_video->format = fourcc; + } else { + fprintf(stderr, + "Unknown MPlayer format code for MIME type \"video/%s\"\n", + subsession->codecName()); + } +} + +void rtpCodecInitialize_audio(demuxer_t* demuxer, + MediaSubsession* subsession, + unsigned& flags) { + flags = 0; + // Create a dummy audio stream header + // to make the main MPlayer code happy: + sh_audio_t* sh_audio = new_sh_audio(demuxer,0); + WAVEFORMATEX* wf = (WAVEFORMATEX*)calloc(1,sizeof(WAVEFORMATEX)); + sh_audio->wf = wf; + demux_stream_t* d_audio = demuxer->audio; + d_audio->sh = sh_audio; sh_audio->ds = d_audio; + + // Map known audio MIME types to the WAVEFORMATEX parameters + // that this program uses. (Note that not all types need all + // of the parameters to be set.) + wf->nSamplesPerSec + = subsession->rtpSource()->timestampFrequency(); // by default + if (strcmp(subsession->codecName(), "MPA") == 0 || + strcmp(subsession->codecName(), "MPA-ROBUST") == 0 || + strcmp(subsession->codecName(), "X-MP3-DRAFT-00") == 0) { + wf->wFormatTag = sh_audio->format = 0x55; + // Note: 0x55 is for layer III, but should work for I,II also + wf->nSamplesPerSec = 0; // sample rate is deduced from the data + flags |= RTPSTATE_IS_MPEG; + } else if (strcmp(subsession->codecName(), "AC3") == 0) { + wf->wFormatTag = sh_audio->format = 0x2000; + wf->nSamplesPerSec = 0; // sample rate is deduced from the data + } else if (strcmp(subsession->codecName(), "PCMU") == 0) { + wf->wFormatTag = sh_audio->format = 0x7; + wf->nChannels = 1; + wf->nAvgBytesPerSec = 8000; + wf->nBlockAlign = 1; + wf->wBitsPerSample = 8; + wf->cbSize = 0; + } else if (strcmp(subsession->codecName(), "PCMA") == 0) { + wf->wFormatTag = sh_audio->format = 0x6; + wf->nChannels = 1; + wf->nAvgBytesPerSec = 8000; + wf->nBlockAlign = 1; + wf->wBitsPerSample = 8; + wf->cbSize = 0; + } else if (strcmp(subsession->codecName(), "GSM") == 0) { + wf->wFormatTag = sh_audio->format = mmioFOURCC('a','g','s','m'); + wf->nChannels = 1; + wf->nAvgBytesPerSec = 1650; + wf->nBlockAlign = 33; + wf->wBitsPerSample = 16; + wf->cbSize = 0; + } else if (strcmp(subsession->codecName(), "QCELP") == 0) { + wf->wFormatTag = sh_audio->format = mmioFOURCC('Q','c','l','p'); + // The following settings for QCELP don't quite work right ##### + wf->nChannels = 1; + wf->nAvgBytesPerSec = 1750; + wf->nBlockAlign = 35; + wf->wBitsPerSample = 16; + wf->cbSize = 0; + } else if (strcmp(subsession->codecName(), "MP4A-LATM") == 0) { + wf->wFormatTag = sh_audio->format = mmioFOURCC('m','p','4','a'); +#ifndef HAVE_FAAD + fprintf(stderr, "WARNING: Playing MPEG-4 (AAC) Audio requires the \"faad\" library!\n"); +#endif +#if (LIVEMEDIA_LIBRARY_VERSION_INT < 1042761600) + fprintf(stderr, "WARNING: This audio stream might not play correctly. Please upgrade to version \"2003.01.17\" or later of the \"LIVE.COM Streaming Media\" libraries.\n"); +#else + // For the codec to work correctly, it needs "AudioSpecificConfig" + // data, which is parsed from the "StreamMuxConfig" string that + // was present (hopefully) in the SDP description: + unsigned codecdata_len; + sh_audio->codecdata + = parseStreamMuxConfigStr(subsession->fmtp_config(), + codecdata_len); + sh_audio->codecdata_len = codecdata_len; +#endif + flags |= RTPSTATE_IS_MPEG; + } else if (strcmp(subsession->codecName(), "X-QT") == 0 || + strcmp(subsession->codecName(), "X-QUICKTIME") == 0) { + // QuickTime generic RTP format, as described in + // http://developer.apple.com/quicktime/icefloe/dispatch026.html + + // We can't initialize this stream until we've received the first packet + // that has QuickTime "sdAtom" information in the header. So, keep + // reading packets until we get one: + unsigned char* packetData; unsigned packetDataLen; + QuickTimeGenericRTPSource* qtRTPSource + = (QuickTimeGenericRTPSource*)(subsession->rtpSource()); + unsigned fourcc, numChannels; + do { + if (!awaitRTPPacket(demuxer, 1 /*audio*/, packetData, packetDataLen)) { + return; + } + } while (!parseQTState_audio(qtRTPSource->qtState, fourcc, numChannels)); + + wf->wFormatTag = sh_audio->format = fourcc; + wf->nChannels = numChannels; + } else { + fprintf(stderr, + "Unknown MPlayer format code for MIME type \"audio/%s\"\n", + subsession->codecName()); + } +} + +static Boolean +parseQTState_video(QuickTimeGenericRTPSource::QTState const& qtState, + unsigned& fourcc) { + // qtState's "sdAtom" field is supposed to contain a QuickTime video + // 'sample description' atom. This atom's name is the 'fourcc' that we want: + char const* sdAtom = qtState.sdAtom; + if (sdAtom == NULL || qtState.sdAtomSize < 2*4) return False; + + fourcc = *(unsigned*)(&sdAtom[4]); // put in host order + return True; +} + +static Boolean +parseQTState_audio(QuickTimeGenericRTPSource::QTState const& qtState, + unsigned& fourcc, unsigned& numChannels) { + // qtState's "sdAtom" field is supposed to contain a QuickTime audio + // 'sample description' atom. This atom's name is the 'fourcc' that we want. + // Also, the top half of the 5th word following the atom name should + // contain the number of channels ("numChannels") that we want: + char const* sdAtom = qtState.sdAtom; + if (sdAtom == NULL || qtState.sdAtomSize < 7*4) return False; + + fourcc = *(unsigned*)(&sdAtom[4]); // put in host order + + char const* word7Ptr = &sdAtom[6*4]; + numChannels = (word7Ptr[0]<<8)|(word7Ptr[1]); + return True; +} diff --git a/libmpdemux/demux_rtp_internal.h b/libmpdemux/demux_rtp_internal.h new file mode 100644 index 0000000000..e9499bdb0f --- /dev/null +++ b/libmpdemux/demux_rtp_internal.h @@ -0,0 +1,36 @@ +#ifndef _DEMUX_RTP_INTERNAL_H +#define _DEMUX_RTP_INTERNAL_H + +#include + +extern "C" { +#ifndef __STREAM_H +#include "stream.h" +#endif +#ifndef __DEMUXER_H +#include "demuxer.h" +#endif +} + +#ifndef _LIVEMEDIA_HH +#include +#endif + +// Codec-specific initialization routines: +void rtpCodecInitialize_video(demuxer_t* demuxer, + MediaSubsession* subsession, unsigned& flags); +void rtpCodecInitialize_audio(demuxer_t* demuxer, + MediaSubsession* subsession, unsigned& flags); + +// Flags that may be set by the above routines: +#define RTPSTATE_IS_MPEG 0x1 // is an MPEG audio, video or transport stream + +// A routine to wait for the first packet of a RTP stream to arrive. +// (For some RTP payload formats, codecs cannot be fully initialized until +// we've started receiving data.) +Boolean awaitRTPPacket(demuxer_t* demuxer, unsigned streamType, + unsigned char*& packetData, unsigned& packetDataLen); + // "streamType": 0 => video; 1 => audio + // This routine returns False if the input stream has closed + +#endif -- cgit v1.2.3