[FFmpeg-devel] [PATCH] ffserver rtsp bug fixes

Howard Chu hyc
Mon May 17 23:59:31 CEST 2010


Howard Chu wrote:
> Howard Chu wrote:
>> Howard Chu wrote:
>>> Howard Chu wrote:
>>>
>>>> Unfortunately I still haven't had any success actually playing the stream. It
>>>> seems that ffserver isn't sending any RTP packets at all.
>>>
>>> The cur_pts in the stream starts out with an absolute timestamp, while for all
>>> subsequent packets it only carries a relative timestamp. The check for whether
>>> it's time to transmit or not thus compares the huge cur_pts against the
>>> current server time (which is also relative) and decides it's not time to send
>>> anything yet.
>>>
>>> This patch gets things working for a single stream (audio only or video only).
>>> Still not working for both audio and video; in that case I only receive the audio.
>>
>> Finally found that problem too, it was a simple error in looking up the stream
>> index. The attached patch includes the two previous ones. I can now
>> successfully play ffserver's rtsp streams using both ffplayer and mplayer.
>> Unfortunately my G1 phone refuses to play the stream, so I still need to find
>> out what's wrong there.
>
> Again, it turned out to be something really simple - the sdp response was
> missing a required c= (connection) record. I've fixed this by using "0.0.0.0"
> when no IP address was provided by the calling app.
>
> Also re-sending the ffserver patch, slightly simplified. (When comparing the
> feed file to the current config, we only need to check that the codec_id
> matches, don't need to actually open the codec on each check.)
>
> So after spending a day and a half poking at this, I now have ffserver
> streaming live H264/AAC videos to my G1 phone via RTSP/RTP. I've also attached
> the ffserver.conf file I'm using. I invoke ffmpeg as
>
> ffmpeg -i<input>  -threads 0 http://localhost:8090/feed1.ffm

Martin suggested I repost the explanations of each patch, so I'm doing this here.

>
> Index: ffserver.c
> ===================================================================
> --- ffserver.c	(revision 22902)
> +++ ffserver.c	(working copy)
> @@ -2210,14 +2210,18 @@
>  static int64_t get_packet_send_clock(HTTPContext *c)
>  {
>      int bytes_left, bytes_sent, frame_bytes;
> +    int64_t pts = c->cur_pts;
>
> +    if (pts >= c->first_pts)
> +        pts -= c->first_pts;
> +
>      frame_bytes = c->cur_frame_bytes;
>      if (frame_bytes <= 0)
> -        return c->cur_pts;
> +        return pts;
>      else {
>          bytes_left = c->buffer_end - c->buffer_ptr;
>          bytes_sent = frame_bytes - bytes_left;
> -        return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
> +        return pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
>      }
>  }
>
When I was streaming an flv from ffmpeg to ffserver I saw that the first 
frames had absolute timestamps of the current real time. All subsequent 
packets only had relative timestamps. In http_send_data() a packet will only 
get sent if the packet timestamp is older than the current server time, but 
the server time is also checked as a relative timestamp. This check turned 
into (1234567890000 > 0) so no RTP packets were ever sent. This patch makes 
sure that only relative timestamps are returned from get_packet_send_clock.

> @@ -2342,7 +2346,7 @@
>                          }
>                      }
>                      for(i=0;i<c->stream->nb_streams;i++) {
> -                        if (c->feed_streams[i] == pkt.stream_index) {
> +                        if (c->stream->feed_streams[i] == pkt.stream_index) {
>                              AVStream *st = c->fmt_in->streams[source_index];
>                              pkt.stream_index = i;
>                              if (pkt.flags & AV_PKT_FLAG_KEY &&

With everything else fixed, streaming was only working for streams with a 
single track - audio-only or video-only. The problem in this test is that 
c->feed_streams[] is always zero-filled because c is a single RTP stream and 
RTP streams only ever carry one channel. You need to use c->stream which 
actually carries the information about all of the related channels for the 
overall stream. This patch gets audio+video working.

> @@ -3679,7 +3683,7 @@
>                              ccs = ss->codec;
>  #define CHECK_CODEC(x)  (ccf->x != ccs->x)
>
> -                            if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
> +                            if (CHECK_CODEC(codec_id) || CHECK_CODEC(codec_type)) {
>                                  http_log("Codecs do not match for stream %d\n", i);
>                                  matches = 0;
>                              } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {

The check here is comparing a stream that was just opened with 
av_open_input_file(). That function doesn't initialize any codecs, so 
ccs->codec is always NULL. Checking the codec_id is the correct step.

> @@ -3803,6 +3807,7 @@
>  static void add_codec(FFStream *stream, AVCodecContext *av)
>  {
>      AVStream *st;
> +    AVCodec *codec;
>
>      /* compute default parameters */
>      switch(av->codec_type) {
> @@ -3842,7 +3847,8 @@
>              av->nsse_weight = 8;
>
>          av->frame_skip_cmp = FF_CMP_DCTMAX;
> -        av->me_method = ME_EPZS;
> +        if (!av->me_method)
> +            av->me_method = ME_EPZS;
>          av->rc_buffer_aggressivity = 1.0;
>
>          if (!av->rc_eq)

There are a lot of unconditional option settings here. I'm not sure why 
they're being done, since these options can be set using AVOptionVideo in 
ffserver.conf. This fixes just one case of add_codec overriding what was set 
in the config file; there are others too but I don't know how important any of 
it is overall.

> @@ -3872,6 +3878,15 @@
>      st->codec = avcodec_alloc_context();
>      stream->streams[stream->nb_streams++] = st;
>      memcpy(st->codec, av, sizeof(AVCodecContext));
> +    codec = avcodec_find_encoder(st->codec->codec_id);
> +    if (!codec) {
> +        http_log("Encoder (codec id %d) not found\n", st->codec->codec_id);
> +        exit(1);
> +    }
> +    if (avcodec_open(st->codec, codec) < 0) {
> +        http_log("Error opening encoder\n");
> +        exit(1);
> +    }
>  }
>
>  static enum CodecID opt_audio_codec(const char *arg)

This was the first fix I posted. The codec must actually be opened/initialized 
to give it a chance to set up its extradata when global headers are 
configured. Otherwise the config option has no effect. Without this patch RTSP 
always fails when trying to use AAC; you get the message "AAC with no global 
headers is currently not supported."

-- 
   -- Howard Chu
   CTO, Symas Corp.           http://www.symas.com
   Director, Highland Sun     http://highlandsun.com/hyc/
   Chief Architect, OpenLDAP  http://www.openldap.org/project/



More information about the ffmpeg-devel mailing list