[Ffmpeg-devel] [PATCH] proof of concept to fix RTSP problem

Michael A. Kohn mike
Wed May 17 22:02:02 CEST 2006


NOTE:  This is a proof of concept and should not be applied to
to the main source tree.  Unfortunately, I don't know where to
get all the information from the FFMPEG structures to make this
a good patch, but this at least shows what's wrong and how to fix
it.

The problem is:  When an ffmpeg is acting as an RTSP client connecting
to an RTSP server (I've tested this both with an Axis 213 camera and
a program called Spook (http://www.litech.org/spook/).  The original
ffmpeg before this patch didn't work.  It works from my laptop after
this patch.  ffmpeg is not sending RTCP RR packets back to the RTSP
server, so after 1 minute an Axis 213 camera will assume the client is
dead and stop sending data.  Spook will last about 20 seconds.

This patch will fix the problem, but it's done wrong for the following
reasons:

1) CNAME is hardcoded to be my laptop name.  I wasn't sure how to get
the client's name from FFMPEG.

2) The SSRC of the client is always 0x00060606.  This should be a
randomly generated unique ID for the client.  Every time the client
sends an RTCP packet, this number should stay the same.  I wasn't sure
where in the RTPContext structure this should be stored so I hardcoded it.
Btw, in rtp.c, around line 540 it says:

s->ssrc = 0; /* FIXME: was random(), what should this be? */

This really should have been a random number.  I think time() would
be an even better value personally, but it doesn't matter.  This number
is supposed to be a unique ID.

3) sequence_number is a global variable.  When the client sends an RTCP
RR packet back to the server, it needs to tell the server what was the
last data RTP sequence number it got.  This sequence number is stored
in a structure but I don't know how to get to it.  I modified rtp.c
to write to this global variable :(.

4) Interarrival jitter is just a guessed number here since I don't know
where to get RTP info.  Fraction lost and cumulative number of packets
lost is set to 0 since again, I don't know where to get this data.

5) I have it so it sends an RR packet back to the server only when it
receives an SR packet from the server.  This is wrong.  The client should
send RR packets at steady intervals (every 3 seconds would be nice) but
I wasn't sure how to tie that in to FFMPEG.  Sending it every time an
SR packet comes in will work in every case probably tho.. unless the
server is set up to send SR packets only when an RR packet comes in.

6) I manually set s->rtcp_hd->flags to URL_RDWR.  I could probably find
quickly how to fix that, but I was in a hurry to get a proof of concept
for this.

7) I wrote single bytes to the buf[] array instead of using put_be32()
for example.

Well, I hope someone who knows more about FFMPEG's internals can either
take what I did and make the code work nicely, or at least point me in
a direction so I could do it myself.

Thanks!

/mike


-------------- next part --------------
diff -Naur ffmpeg/libavformat/rtp.c ffmpeg-proof-of-concept-rtsp/libavformat/rtp.c
--- ffmpeg/libavformat/rtp.c	2006-03-30 10:44:32.000000000 -0600
+++ ffmpeg-proof-of-concept-rtsp/libavformat/rtp.c	2006-05-17 13:38:06.000000000 -0500
@@ -33,6 +33,8 @@
 
 //#define DEBUG
 
+/* last sequence number is needed to be known in rtpproto.c */
+int sequence_number;
 
 /* TODO: - add RTCP statistics reporting (should be optional).
 
@@ -396,6 +398,7 @@
     }
     payload_type = buf[1] & 0x7f;
     seq  = (buf[2] << 8) | buf[3];
+sequence_number=seq;
     timestamp = decode_be32(buf + 4);
     ssrc = decode_be32(buf + 8);
 
diff -Naur ffmpeg/libavformat/rtpproto.c ffmpeg-proof-of-concept-rtsp/libavformat/rtpproto.c
--- ffmpeg/libavformat/rtpproto.c	2006-01-12 16:43:25.000000000 -0600
+++ ffmpeg-proof-of-concept-rtsp/libavformat/rtpproto.c	2006-05-17 14:03:14.000000000 -0500
@@ -34,6 +34,9 @@
 #define RTP_TX_BUF_SIZE  (64 * 1024)
 #define RTP_RX_BUF_SIZE  (128 * 1024)
 
+/* global variable :( */
+extern int sequence_number;
+
 typedef struct RTPContext {
     URLContext *rtp_hd, *rtcp_hd;
     int rtp_fd, rtcp_fd;
@@ -210,6 +213,7 @@
                         continue;
                     return AVERROR_IO;
                 }
+rtpc_send_rr(h,buf,len);
                 break;
             }
             /* then RTP */
@@ -256,6 +260,79 @@
     return ret;
 }
 
+/* http://www.ietf.org/rfc/rfc1889.txt   SECTION 6 explains rtpc_send_rr() */
+
+static int rtpc_send_rr(URLContext *h, uint8_t *buf, int size)
+{
+RTPContext *s = h->priv_data;
+uint8_t buf_rr[56];
+uint8_t cname[24]= { 0x81,            /* V=2, P=0, number of packets=1 */
+                     202,             /* 202 means it's a SDES packet */
+                     0x00, 0x05,      /* Length of packet is 5  */
+                     0, 6, 6, 6,      /* SSRC is hardcoded to 666 */
+                     0x01,            /* 1 means it's a CNAME */
+                     0x0c,            /* CNAME is 12 bytes */
+                     0x73, 0x75, 0x70, 0x65, 0x72, 0x2d, /* My laptop's name */
+                     0x6b, 0x6e, 0x75, 0x6c, 0x6c, 0x61, /* 12 bytes ascii */
+                     0x00, 0x00 };    /* PADDING */
+
+  buf_rr[0]=0x81;          /* 2 bit version, 1 bit padding, RC count */
+                           /* V=2, Padding=0, 1 packet is here so RC=1 */
+  buf_rr[1]=201;           /* 1 byte, 201 means this is an RR packet */
+
+  buf_rr[2]=0;             /* 2 bytes, number of 32 bit words in packet -1 */
+  buf_rr[3]=7;             /* So this packet has (7 + 1) * 4 bytes */
+
+  buf_rr[4]=0;             /* SSRC - I believe this number is randomly */
+  buf_rr[5]=6;             /* generated by the client and associated to */
+  buf_rr[6]=6;             /* it by the CNAME packet.  I hard coded this  */
+  buf_rr[7]=6;             /* as 0,6,6,6.  Probably this should be random. */
+                           /* or based on time() */
+
+  buf_rr[8]=buf[4];        /* This is the SSRC of the server.  I got this */
+  buf_rr[9]=buf[5];        /* number from the servers SR packet */
+  buf_rr[10]=buf[6];
+  buf_rr[11]=buf[7];
+
+  buf_rr[12]=0;            /* 1 byte, fraction lost */
+  buf_rr[13]=0;            /* 3 bytes, cumulative number of packets lost */
+  buf_rr[14]=0;            /* I set these 4 bytes to 0 since I'm not sure */
+  buf_rr[15]=0;            /* where in ffmpeg to get the real stats */
+
+  buf_rr[16]=(sequence_number>>24)&255; /* 4 bytes, sequence number */
+  buf_rr[17]=(sequence_number>>16)&255; /* global variable.. GROSS */
+  buf_rr[18]=(sequence_number>>8)&255;
+  buf_rr[19]=sequence_number&255;
+
+  buf_rr[20]=0;            /* 4 bytes, interarrival jitter */
+  buf_rr[21]=0;            /* Again, I don't know where to get real data */
+  buf_rr[22]=0;            /* to put here */
+  buf_rr[23]=0x68;
+
+  buf_rr[24]=buf[10];      /* 4 bytes, last sr.. the SR packets have */
+  buf_rr[25]=buf[11];      /* NTP time as a 64 bit number.  These 4 */
+  buf_rr[26]=buf[12];      /* bytes are the middle 4 bytes from this */
+  buf_rr[27]=buf[13];      /* 64 bit number */
+
+  buf_rr[28]=0;            /* 4 bytes, delay since last SR in 1/65536 sec */
+  buf_rr[29]=0;            /* Since I'm sending the RR packet immediately */
+  buf_rr[30]=0;            /* after getting an SR packet, I set this to */
+  buf_rr[31]=0x1;          /* 1/65536 seconds */
+
+  memcpy(buf_rr+32,cname,24); /* This appends the SDES packet as described */
+                              /* above to this packet so the server also */
+                              /* gets the CNAME of the client */
+
+  s->rtcp_hd->flags=URL_RDWR; /* This socket was originally set READ ONLY */
+                              /* I change it to read/write here.  This is */
+                              /* bad but was the quickest way for me to test */
+
+  rtp_write(h,buf_rr,56);     /* send the 56 bytes.. it's not always 56.. */
+                              /* depends on the size of the CNAME */
+
+  return 0;
+}
+
 static int rtp_close(URLContext *h)
 {
     RTPContext *s = h->priv_data;



More information about the ffmpeg-devel mailing list