[MPlayer-dev-eng] [PATCH]H264 over rtsp
Carl Eugen Hoyos
cehoyos at ag.or.at
Sun May 7 01:03:51 CEST 2006
Hi!
I wrote this ugly patch to watch rtsp streams containing H264 video, this fixes
Bugzilla #415.
Before rejecting everything, please consider the one line change in
needVideoFrameRate, I think it's useful.
Tested with MacWorld Keynote 2006:
rtsp://a2047.v1412b.c1412.g.vq.akamaistream.net/5/2047/1412/1_h264_350/
1a1a1ae555c531960166df4dbc3095c327960d7be756b71b49aa1576e344addb3ead1a
497aaedf11/mw_2006_1_350.mov
rtsp://a2047.v1411b.c1411.g.vq.akamaistream.net/5/2047/1411/1_h264_650/
1a1a1ae454c430950065de4cbb2f94c226950c7ae655b61a48a91475e243acda3dac19
4879adde0f/mw_2006_1_650.mov
rtsp://a2047.v1412b.c1412.g.vq.akamaistream.net/5/2047/1412/2_h264_350/
1a1a1ae555c531960166df4dbc3095c327960d7be756b71b49aa1576e344addb3ead1a
497aaedf11/mw_2006_3_350.mov
(needs -fps 25)
rtsp://a2047.v1411b.c1411.g.vq.akamaistream.net/5/2047/1411/2_h264_650/
1a1a1ae454c430950065de4cbb2f94c226950c7ae655b61a48a91475e243acda3dac19
4879adde0f/mw_2006_3_650.mov
rtsp://bmrc.berkeley.edu:8000/nossdav05/25-h264-600kbs.mov
I couldn't easily find other example streams. The one on bugzilla doesn't start
for me.
Regards, Carl Eugen Hoyos
(Code, especially parseH264ConfigStr, from vlc:
http://trac.videolan.org/vlc/changeset/11790)
Index: libmpdemux/demux_rtp_codec.cpp
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/demux_rtp_codec.cpp,v
retrieving revision 1.8
diff -u -r1.8 demux_rtp_codec.cpp
--- libmpdemux/demux_rtp_codec.cpp 23 Sep 2005 22:35:03 -0000 1.8
+++ libmpdemux/demux_rtp_codec.cpp 6 May 2006 22:54:47 -0000
@@ -15,6 +15,9 @@
parseQTState_audio(QuickTimeGenericRTPSource::QTState const& qtState,
unsigned& fourcc, unsigned& numChannels); // forward
+static unsigned char* parseH264ConfigStr( char const* configStr,
+ unsigned int& configSize );
+
void rtpCodecInitialize_video(demuxer_t* demuxer,
MediaSubsession* subsession,
unsigned& flags) {
@@ -46,6 +49,14 @@
bih->biCompression = sh_video->format
= mmioFOURCC('H','2','6','1');
needVideoFrameRate(demuxer, subsession);
+ } else if (strcmp(subsession->codecName(), "H264") == 0) {
+ bih->biCompression = sh_video->format
+ = mmioFOURCC('H','2','6','4');
+ unsigned int configLen = 0;
+ unsigned char* configData
+ = parseH264ConfigStr(subsession->fmtp_spropparametersets(), configLen);
+ insertRTPData(demuxer, demuxer->video, configData, configLen);
+ needVideoFrameRate(demuxer, subsession);
} else if (strcmp(subsession->codecName(), "JPEG") == 0) {
bih->biCompression = sh_video->format
= mmioFOURCC('M','J','P','G');
@@ -233,6 +244,7 @@
fps = (int)(1/(curPTS-lastPTS) + 0.5); // rounding
fprintf(stderr, "demux_rtp: Guessed the video frame rate as %d
frames-per-second.\n\t(If this is wrong, use the \"-fps <frame-rate>\" option
instead.)\n", fps);
sh_video->fps = fps;
+ sh_video->frametime=1.0f/fps;
return;
}
lastPTS = curPTS;
@@ -268,3 +280,97 @@
numChannels = (word7Ptr[0]<<8)|(word7Ptr[1]);
return True;
}
+
+static int b64_decode( char *dest, char *src )
+{
+ const char *dest_start = dest;
+ int i_level;
+ int last = 0;
+ int b64[256] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
+ };
+
+ for( i_level = 0; *src != '\0'; src++ )
+ {
+ int c;
+
+ c = b64[(unsigned int)*src];
+ if( c == -1 )
+ {
+ continue;
+ }
+
+ switch( i_level )
+ {
+ case 0:
+ i_level++;
+ break;
+ case 1:
+ *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
+ i_level++;
+ break;
+ case 2:
+ *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
+ i_level++;
+ break;
+ case 3:
+ *dest++ = ( ( last &0x03 ) << 6 ) | c;
+ i_level = 0;
+ }
+ last = c;
+ }
+
+ *dest = '\0';
+
+ return dest - dest_start;
+}
+
+static unsigned char* parseH264ConfigStr( char const* configStr,
+ unsigned int& configSize )
+{
+ char *dup, *psz;
+
+ if( configSize )
+ configSize = 0;
+
+ if( configStr == NULL || *configStr == '\0' )
+ return NULL;
+
+ psz = dup = strdup( configStr );
+
+ unsigned char *cfg = new unsigned char[5 * strlen(psz)];
+ for( ;; )
+ {
+ char *p = strchr( psz, ',' );
+ if( p )
+ *p++ = '\0';
+
+ cfg[configSize++] = 0x00;
+ cfg[configSize++] = 0x00;
+ cfg[configSize++] = 0x00;
+ cfg[configSize++] = 0x01;
+ configSize += b64_decode( (char*)&cfg[configSize], psz );
+
+ if( p == NULL )
+ break;
+ psz = p;
+ }
+
+ if( dup ) free( dup );
+ return cfg;
+}
Index: libmpdemux/demux_rtp.cpp
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/demux_rtp.cpp,v
retrieving revision 1.30
diff -u -r1.30 demux_rtp.cpp
--- libmpdemux/demux_rtp.cpp 19 Feb 2006 13:27:27 -0000 1.30
+++ libmpdemux/demux_rtp.cpp 6 May 2006 22:54:48 -0000
@@ -384,7 +384,7 @@
////////// Extra routines that help implement the above interface functions:
-#define MAX_RTP_FRAME_SIZE 50000
+#define MAX_RTP_FRAME_SIZE 100000
// >= the largest conceivable frame composed from one or more RTP packets
static void afterReading(void* clientData, unsigned frameSize,
@@ -453,6 +453,9 @@
bufferQueue->blockingFlag = ~0;
}
+demux_packet_t* seconddp = NULL;
+int packetsneeded = 0;
+
static demux_packet_t* getBuffer(demuxer_t* demuxer, demux_stream_t* ds,
Boolean mustGetNewData,
float& ptsBehind) {
@@ -461,8 +464,10 @@
// the demuxer's 'priv' field)
RTPState* rtpState = (RTPState*)(demuxer->priv);
ReadBufferQueue* bufferQueue = NULL;
+ sh_video_t* sh_video = NULL;
if (ds == demuxer->video) {
bufferQueue = rtpState->videoBufferQueue;
+ sh_video = (sh_video_t*)ds->sh;
} else if (ds == demuxer->audio) {
bufferQueue = rtpState->audioBufferQueue;
} else {
@@ -490,15 +495,64 @@
bufferQueue->dp = dp;
if (dp == NULL) return NULL;
+ TaskScheduler& scheduler
+ = bufferQueue->readSource()->envir().taskScheduler();
+
+ // Handle H264 video:
+ if (sh_video && sh_video->format == mmioFOURCC('H','2','6','4')) {
+ int length = 0, packet = 0;
+ char laststartchar;
+
+ if (seconddp) {
+ free (dp);
+ // reload first packet of second frame
+ bufferQueue->dp = dp = seconddp;
+ seconddp = NULL;
+ packet = 1;
+ length = dp->len+4;
+ }
+
+ do{
+ dp->buffer[length]=0x00;
+ dp->buffer[length+1]=0x00;
+ dp->buffer[length+2]=0x00;
+ dp->buffer[length+3]=0x01;
+
+ do{
+ bufferQueue->blockingFlag = 0;
+ // Read one packet
+ bufferQueue->readSource()->getNextFrame(&dp->buffer[length+4],
+ MAX_RTP_FRAME_SIZE-length,
+ afterReading, bufferQueue,
+ onSourceClosure, bufferQueue);
+ // and wait until it's ready
+ scheduler.doEventLoop(&bufferQueue->blockingFlag);
+//printf("Start%i: %#x %#x length: %d frame: %d \n", packet+1,
dp->buffer[length+4], dp->buffer[length+5], dp->len, dp->len+length+4);
+ // discard useless (?) packets and try to resync:
+ } while (((dp->buffer[4]==0x6)||(dp->buffer[4]==0xc))&&!(packet=0));
+ //At end of first frame: Find out how many packets are needed
+ if ((!packetsneeded)&&(packet)&&(dp->buffer[length+4]!=laststartchar)) {
+ packetsneeded=packet;
+ seconddp = new_demux_packet(MAX_RTP_FRAME_SIZE);
+ // save first packet of second frame:
+ memcpy(seconddp->buffer, &dp->buffer[length], dp->len+4);
+ seconddp->len=dp->len;
+ length-=dp->len+4;
+ }
+ laststartchar=dp->buffer[length+4];
+ length+=dp->len+4;
+ } while ((++packet<packetsneeded) || (!packetsneeded));
+ dp->len=length;
+ } else { // not H264:
+
// Schedule the read operation:
bufferQueue->blockingFlag = 0;
bufferQueue->readSource()->getNextFrame(dp->buffer, MAX_RTP_FRAME_SIZE,
afterReading, bufferQueue,
onSourceClosure, bufferQueue);
// Block ourselves until data becomes available:
- TaskScheduler& scheduler
- = bufferQueue->readSource()->envir().taskScheduler();
scheduler.doEventLoop(&bufferQueue->blockingFlag);
+ }
// Set the "ptsBehind" result parameter:
if (bufferQueue->prevPacketPTS != 0.0
More information about the MPlayer-dev-eng
mailing list