[FFmpeg-trac] #2764(undetermined:new): FFMPEG is blocked when multicast group membership is lost (on linux kernel < 2.6.27)

FFmpeg trac at avcodec.org
Tue Jul 9 17:04:50 CEST 2013


#2764: FFMPEG is blocked when multicast group membership is lost (on linux kernel
< 2.6.27)
-------------------------------------+-------------------------------------
             Reporter:  asif         |                     Type:
               Status:  new          |  enhancement
            Component:               |                 Priority:  normal
  undetermined                       |                  Version:  0.10.7
             Keywords:  udp          |               Blocked By:
  multicast blocked lost group       |  Reproduced by developer:  0
  membership                         |
             Blocking:               |
Analyzed by developer:  0            |
-------------------------------------+-------------------------------------
 Summary of the bug:
 FFmpeg is blocking until it receives the data packet or there is some
 socket error. The issue is group membership of the socket is being flushed
 from kernel group membership table whenever there is some disruption in
 the network. It does happen on centos as it is using an older version of
 the kernel (older version: “2.6.18”). As the ffmpeg udp.c is waiting on
 receiving packets and not checking whether there is any problem with its
 group membership, it will continue to wait for packets.

 The Issue was Reproduced on:
 CentOS kernel: 2.6.18
 ffmpeg version: 0.10

 How to reproduce:
 1. Start ffmpeg stream transmission:
 $ ./ffmpeg -i INPUT -f mpegts udp://233.19.204.1:5501

 2. Start ffmpeg stream receiver:
 $ ./ffplay udp://233.19.204.1:5501

 3. The multicast group member can be lost due to disruption in the
 network. One way is also to  restarting network service:
 $ /etc/init.d/network restart

 The receiver will keep on waiting for the udp packets without checking the
 multicast group membership.

 Note:
 The issue happens only on kernel versions < 2.6.27. The issue in the
 multicast group membership seemed to be fixed by linux kerenl in version
 2.6.27:
 http://mirror.linux.org.au/linux/kernel/v2.6/ChangeLog-2.6.27

 However, some linux flavors including CentOS 5.7 still uses 2.6.18 which
 has this issue.

 Possible Fix:

 in libavformat/udp.c Check the multicastgroupmembership as below:

 {{{
 #ifdef _MULTICAST_HANDLELOSTMEMBERSHIP

 #define PATH_PROCNET_IGMP              "/proc/net/igmp"
 //TODO: Support ipv6
 static int udp_check_multicastgroupmembership(void *_URLContext, struct
 sockaddr *addr)
 {
     FILE *f_igmp;
     char igmp_line[8192];
     char target_addr[10];
     int result = -1;
     URLContext *h = _URLContext;

     if(addr->sa_family != AF_INET)
     {
         av_log(h, AV_LOG_INFO, "udp.c: IPPROTO_IPV6 NOT SUPPORTED\n");
                 return -1; //NOT SUPPORTED
     }


     if(NULL == (f_igmp = fopen(PATH_PROCNET_IGMP, "r")))
     {
         av_log(h, AV_LOG_ERROR, "udp.c: Unable to open %s\n",
 PATH_PROCNET_IGMP);
         return -1;
     }

     snprintf(target_addr, 9, "%X", ((struct sockaddr_in
 *)addr)->sin_addr.s_addr);


     if(fgets(igmp_line, sizeof(igmp_line), f_igmp)) {
         if(strstr(igmp_line, "Device") == NULL) {
                 av_log(h, AV_LOG_INFO, "udp.c: IPPROTO_IPV6 NOT
 SUPPORTED\n");
             fclose(f_igmp);
             return -1;
         }
     }

     result = 0;
     while (!feof(f_igmp)) {
        if(fgets(igmp_line, sizeof(igmp_line), f_igmp)){
            if(NULL != strstr(igmp_line, target_addr)){
             result = 1;
             break;
            }
        }

     };

     fclose(f_igmp);
     return result;
 }
 #endif
 }}}


 The function can be called from circular_buffer_task function as below:
 {{{
 #if HAVE_PTHREADS
 static void *circular_buffer_task( void *_URLContext)
 {
     URLContext *h = _URLContext;
     UDPContext *s = h->priv_data;
     fd_set rfds;
     struct timeval tv;

 #ifdef _MULTICAST_HANDLELOSTMEMBERSHIP
     int timeout_count = 0;
         const int timeout_max_value = 5;        // 5 second timeout
 #endif
     while(!s->exit_thread) {
         int left;
         int ret;
         int len;

         if (ff_check_interrupt(&h->interrupt_callback)) {
             s->circular_buffer_error = EIO;
             goto end;
         }

         FD_ZERO(&rfds);
         FD_SET(s->udp_fd, &rfds);
         tv.tv_sec = 1;
         tv.tv_usec = 0;
         ret = select(s->udp_fd + 1, &rfds, NULL, NULL, &tv);
         if (ret < 0) {
             if (ff_neterrno() == AVERROR(EINTR)) {
                 av_log(h, AV_LOG_INFO, "Got ERROR EINTR");
                 continue;
             }
             s->circular_buffer_error = EIO;
             goto end;
         }
 #ifdef _MULTICAST_HANDLELOSTMEMBERSHIP
         else if(ret == 0 && s->is_multicast && (h->flags &
 AVIO_FLAG_READ))
         {
             if(++timeout_count >= timeout_max_value)
             {
                 av_log(h, AV_LOG_DEBUG, "No Packet for %d seconds\n",
 timeout_max_value);
                 //check if the problem is due to lost group membership
                 if(0 == udp_check_multicastgroupmembership(h, (struct
 sockaddr *)&s->dest_addr))
                 {
                     //if the problem is due to lost multicast group
 membership, reinitialize group membership
                     av_log(h, AV_LOG_DEBUG, "Restoring group
 membership\n");
                     udp_leave_multicast_group(s->udp_fd, (struct sockaddr
 *)&s->dest_addr);
                     if (udp_join_multicast_group(s->udp_fd, (struct
 sockaddr *)&s->dest_addr) < 0)
                     {
                         av_log(h, AV_LOG_ERROR, "udp.c: groupmembership
 retry failed\n");
                     }
                 }

                 timeout_count = 0;
             }
         }
 #endif

         if (!(ret > 0 && FD_ISSET(s->udp_fd, &rfds)))
             continue;


         /* How much do we have left to the end of the buffer */
         /* Whats the minimum we can read so that we dont comletely fill
 the buffer */
         left = av_fifo_space(s->fifo);

         /* No Space left, error, what do we do now */
         if(left < UDP_MAX_PKT_SIZE + 4) {
             av_log(h, AV_LOG_ERROR, "circular_buffer: OVERRUN\n");
             s->circular_buffer_error = EIO;
             goto end;
         }
         left = FFMIN(left, s->fifo->end - s->fifo->wptr);
         len = recv(s->udp_fd, s->tmp+4, sizeof(s->tmp)-4, 0);
         if (len < 0) {
             if (ff_neterrno() != AVERROR(EAGAIN) && ff_neterrno() !=
 AVERROR(EINTR)) {
                 s->circular_buffer_error = EIO;
                 goto end;
             }
             continue;
         }
         AV_WL32(s->tmp, len);
         pthread_mutex_lock(&s->mutex);
         av_fifo_generic_write(s->fifo, s->tmp, len+4, NULL);
         pthread_cond_signal(&s->cond);
         pthread_mutex_unlock(&s->mutex);
     }

 end:
     pthread_mutex_lock(&s->mutex);
     pthread_cond_signal(&s->cond);
     pthread_mutex_unlock(&s->mutex);
     return NULL;
 }
 #endif

 }}}

-- 
Ticket URL: <https://ffmpeg.org/trac/ffmpeg/ticket/2764>
FFmpeg <http://ffmpeg.org>
FFmpeg issue tracker


More information about the FFmpeg-trac mailing list