[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