[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