[MPlayer-dev-eng] Re: [PATCH]H264 over rtsp

Carl Eugen Hoyos cehoyos at ag.or.at
Sun Jun 25 21:15:07 CEST 2006


Hi Tomas!

> mplayer team, please, any news about this? I think H264 in RTSP is a 
> must-have feature.

It seems, we are the only ones who feel this way. (Although I understand
depacketizing should be done in live555.)

This new version of the patch works for all H264 rtsp streams I found:
I hope it works for you too. (parseH264ConfigStr() copied from vlc)

Greetings, Carl Eugen

Index: demux_rtp_codec.cpp
===================================================================
--- demux_rtp_codec.cpp (Revision 18819)
+++ demux_rtp_codec.cpp (Arbeitskopie)
@@ -4,6 +4,7 @@
 #include "demux_rtp_internal.h"
 extern "C" {
 #include "stheader.h"
+#include "math.h"
 }

 static void
@@ -15,6 +16,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 +50,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');
@@ -213,6 +225,7 @@
   int fps = (int)(subsession->videoFPS());
   if (fps != 0) {
     sh_video->fps = fps;
+    sh_video->frametime=1.0f/fps;
     return;
   }

@@ -221,19 +234,24 @@
   unsigned char* packetData; unsigned packetDataLen;
   float lastPTS = 0.0, curPTS;
   unsigned const maxNumFramesToWaitFor = 300;
+  int lastfps = 0;
   for (unsigned i = 0; i < maxNumFramesToWaitFor; ++i) {
     if (!awaitRTPPacket(demuxer, d_video, packetData, packetDataLen, curPTS)) {
       break;
     }

-    if (curPTS > lastPTS && lastPTS != 0.0) {
+    if (curPTS != lastPTS && lastPTS != 0.0) {
       // Use the difference between these two "pts"s to guess the frame rate.
       // (should really check that there were no missing frames inbetween)#####
       // Guess the frame rate as an integer.  If it's not, use "-fps" instead.
-      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;
-      return;
+      fps = (int)(1/fabs(curPTS-lastPTS) + 0.5); // rounding
+      if (fps == lastfps) {
+        mp_msg(MSGT_DEMUX, MSGL_INFO, "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;
+      }
+      if (fps>lastfps) lastfps = fps;
     }
     lastPTS = curPTS;
   }
@@ -268,3 +286,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: demux_rtp.cpp
===================================================================
--- demux_rtp.cpp       (Revision 18819)
+++ demux_rtp.cpp       (Arbeitskopie)
@@ -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,10 @@
   bufferQueue->blockingFlag = ~0;
 }

+static demux_packet_t* seconddp = NULL;
+static int packetsneeded = 0;
+static float lastpts;
+
 static demux_packet_t* getBuffer(demuxer_t* demuxer, demux_stream_t* ds,
                                 Boolean mustGetNewData,
                                 float& ptsBehind) {
@@ -461,8 +465,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 {
@@ -486,19 +492,69 @@
   }

   // Allocate a new packet buffer, and arrange to read into it:
-  dp = new_demux_packet(MAX_RTP_FRAME_SIZE);
-  bufferQueue->dp = dp;
-  if (dp == NULL) return NULL;
+  if (!seconddp || !sh_video) {
+    dp = new_demux_packet(MAX_RTP_FRAME_SIZE);
+    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;
+
+    if (seconddp) {
+      // 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);
+      mp_msg(MSGT_DEMUX, MSGL_DBG2, "Packet %i: %#x %#x PTS: %f length: %d
frame-length: %d \n", packet+1, dp->buffer[length+4], dp->buffer[length+5],
dp->pts, dp->len, dp->len+length+4);
+      // discard useless (?) packets and resync
+      } while ((dp->buffer[4]==0x6||dp->buffer[4]==0xc)&& !(packet=0) &&
!(length=0));
+      //At end of first frame we know how many packets are needed
+      if (!packetsneeded && packet && dp->pts!=lastpts ) {
+        seconddp = new_demux_packet(MAX_RTP_FRAME_SIZE);
+        // save first packet of next frame:
+        memcpy(seconddp->buffer, &dp->buffer[length], dp->len+4);
+        seconddp->len=dp->len;
+        packetsneeded=packet;
+        mp_msg(MSGT_DEMUX, MSGL_V, "H264 over RTSP: %i packets per frame\n",
packetsneeded);
+        length-=dp->len+4;
+      }
+      lastpts=dp->pts;
+      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