[vlc-devel] [PATCH] Flaschen Taschen output: Support for large(r) video sizes

Johannes Lechner git at fudgy.de
Sun Apr 26 17:00:40 CEST 2020


Hey Rémi,


> On 25. Apr 2020, at 11:17, Rémi Denis-Courmont <remi at remlab.net> wrote:
> 
> Le lauantaina 25. huhtikuuta 2020, 11.06.20 EEST Johannes Lechner a écrit :
>> This patches contains two modifications to make Flaschen Taschen support
>> large(r) video sizes: (1) Increase of UDP send buffer, allowing
>> maximum-sized UDP packets to be sent
> 
> Increasing the send buffer seems pointless here. Not like it'll magically 
> increase bandwidth. Just wait for buffer space to send the next packet. Either 
> leave the socket in blocking mode, or poll for output.


Thanks so much for your guidance, since I am by no means a network guy:
Just wanted to get my video wall working and contribute the fixes back.

The initial reason why I increased the send buffer size was to get rid of "Message too long” errors during sendmsg.
Just now I realised that maxing out to the UDP payload will lead to other problems, especially slow downs due to fragmentation on the IP layer.

Please have a look at the updated attached patch:
- It now splits the frames into multiple UDP packets based on the Ethernet MTU of 1500 bytes (instead of the maximum UDP packet size).
- Further I’ve implemented poll() in order to be able to drop frames in case the send buffer is full.


>> (2) Splitting a frame into multiple
>> UDP packets in case one frame exceeds the maximum UDP packet size
> 
> That's good, but I don't really follow how it works here. How does the 
> receiver know which packet belongs to which frame?


The Flaschen Taschen protocol is basically “last received packet” wins, i.e. it’s contents remain displayed until a further package is received:
And the protocol allows you to either update the whole frame displayed or just a sub-section of the frame displayed as specificed through an offset!


This offset is sent along in the p6_header (see "snprintf(p6_header…”).


e.g. for a 3-by-4 frame where everything fits into one UDP packet:

[UDP Packet #1: Row width=9 (3xRGB), number of rows=4, offset=(0/0)]
Row #1: XXX-XXX-XXX
Row #2: XXX-XXX-XXX
Row #3: XXX-XXX-XXX
Row #4: XXX-XXX-XXX


e.g. for a 3-by-6 frame where two UDP packets would need to be sent:

[UDP Packet #1: Row width=9 (3xRGB), number of rows=4, offset=(0/0)]
Row #0: XXX-XXX-XXX
Row #1: XXX-XXX-XXX
Row #2: XXX-XXX-XXX
Row #3: XXX-XXX-XXX

[UDP Packet #2: Row width=9 (3xRGB), number of rows=2, offset=(4/0)]
Row #4: XXX-XXX-XXX
Row #5: XXX-XXX-XXX


Hope this helps?

Johannes


-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Flaschen-Taschen-output-Support-for-large-r-video-si.patch
Type: application/octet-stream
Size: 6209 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20200426/2f3353f0/attachment.obj>
-------------- next part --------------



>> 
>> The implemention for (2) draws heavily from the "official"
>> UDPFlaschenTaschen::Send implemention:
>> https://github.com/hzeller/flaschen-taschen/blob/master/api/lib/udp-flasche
>> n-taschen.cc ---
>> modules/video_output/flaschen.c | 113 +++++++++++++++++++-------------
>> 1 file changed, 66 insertions(+), 47 deletions(-)
>> 
>> diff --git a/modules/video_output/flaschen.c
>> b/modules/video_output/flaschen.c index 00e1e66a34..b9f541dc30 100644
>> --- a/modules/video_output/flaschen.c
>> +++ b/modules/video_output/flaschen.c
>> @@ -134,8 +134,9 @@ static int Open(vout_display_t *vd, const
>> vout_display_cfg_t *cfg,
>> 
>>  /* Ignore any unexpected incoming packet */
>>  setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0 }, sizeof (int));
>> -
>> -
>> +    /* Increase send buffer size to allow queuing of several "full" UDP
>> packets  */ +    setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &(int){ 16 * 65536
>> }, sizeof (int)); +
>>  *fmtp = fmt;
>> 
>>  vd->prepare = NULL;
>> @@ -155,55 +156,73 @@ static void Close(vout_display_t *vd)
>>  free(sys);
>> }
>> 
>> +/*
>> + * Adaptation of UDPFlaschenTaschen::Send implemention
>> + * (https://github.com/hzeller/flaschen-taschen/)
>> + */
>> static void Display(vout_display_t *vd, picture_t *picture)
>> {
>> -#ifdef IOV_MAX
>> -    const long iovmax = IOV_MAX;
>> -#else
>> -    const long iovmax = sysconf(_SC_IOV_MAX);
>> -#endif
>> -    vout_display_sys_t *sys = vd->sys;
>> -    video_format_t *fmt = &picture->format;
>> -    int result;
>> -
>> -    char buffer[64];
>> -    int header_len = snprintf(buffer, sizeof(buffer), "P6\n%d %d\n255\n",
>> -                              fmt->i_width, fmt->i_height);
>> -    /* TODO: support offset_{x,y,z}? (#FT:...) */
>> -    /* Note the protocol doesn't include any picture order field. */
>> -    /* (maybe add as comment?) */
>> -
>> -    int iovcnt = 1 + fmt->i_height;
>> -    if (unlikely(iovcnt > iovmax))
>> -        return;
>> -
>> -    struct iovec iov[iovcnt];
>> -    iov[0].iov_base = buffer;
>> -    iov[0].iov_len = header_len;
>> -
>> -    uint8_t *src = picture->p->p_pixels;
>> -    for (int i = 1; i < iovcnt; i++)
>> +    video_format_t *format = &picture->format;
>> +    uint8_t *pixels = picture->p->p_pixels;
>> +
>> +    const int max_data_packet_length = 65507 - 64;  // Leave some space for
>> the P6 header +    const size_t row_size = format->i_width * 3;
>> +    const int max_send_height = max_data_packet_length / row_size;
>> +
>> +    int rows = format->i_height;
>> +    int row_offset = 0;
>> +
>> +    while(rows)
>>  {
>> -        iov[i].iov_base = src;
>> -        iov[i].iov_len = fmt->i_width * 3;
>> -        src += picture->p->i_pitch;
>> +        // Send as many rows as can fit within one UDP packet
>> +        const int send_height = (rows < max_send_height) ? rows :
>> max_send_height; +
>> +        char p6_header[64];
>> +        const int p6_header_len = snprintf(p6_header, sizeof(p6_header),
>> +                                           "P6\n%d %d\n#FT: %d %d
>> %d\n255\n", +                                           format->i_width,
>> send_height, +                                           0, row_offset, 0);
>> +        //msg_Dbg(vd, "p6_header: %s", p6_header`);
>> +
>> +        const int p6_data_len = send_height * row_size;
>> +
>> +        // Create and send UDP packet
>> +        {
>> +            struct iovec iov[2];
>> +            iov[0].iov_base = p6_header;
>> +            iov[0].iov_len = p6_header_len;
>> +            iov[1].iov_base = pixels;
>> +            iov[1].iov_len = p6_data_len;
>> +
>> +            struct msghdr hdr = {
>> +                .msg_name = NULL,
>> +                .msg_namelen = 0,
>> +                .msg_iov = iov,
>> +                .msg_iovlen = 2,
>> +                .msg_control = NULL,
>> +                .msg_controllen = 0,
>> +                .msg_flags = 0 };
>> +
>> +            int result;
>> +            result = sendmsg(vd->sys->fd, &hdr, 0);
>> +
>> +            if (result < 0) {
>> +                msg_Err(vd, "sendmsg: error %s in vout display flaschen,
>> dropping remainder of frame", vlc_strerror_c(errno)); +                //
>> dropping remainder of frame
>> +                break;
>> +            }
>> +            else if (result < (int)(p6_header_len + p6_data_len)) {
>> +                msg_Err(vd, "sendmsg only sent %d bytes in vout display
>> flaschen, dropping remainder of frame", result); +                //
>> dropping remainder of frame
>> +                break;
>> +            }
>> +        }
>> +
>> +        pixels += p6_data_len;
>> +
>> +        rows -= send_height;
>> +        row_offset += send_height;
>>  }
>> -
>> -    struct msghdr hdr = {
>> -        .msg_name = NULL,
>> -        .msg_namelen = 0,
>> -        .msg_iov = iov,
>> -        .msg_iovlen = iovcnt,
>> -        .msg_control = NULL,
>> -        .msg_controllen = 0,
>> -        .msg_flags = 0 };
>> -
>> -    result = sendmsg(sys->fd, &hdr, 0);
>> -    if (result < 0)
>> -        msg_Err(vd, "sendmsg: error %s in vout display flaschen",
>> vlc_strerror_c(errno)); -    else if (result < (int)(header_len +
>> fmt->i_width * fmt->i_height * 3)) -        msg_Err(vd, "sendmsg only sent
>> %d bytes in vout display flaschen", result); -        /* we might want to
>> drop some frames? */
>> }
>> 
>> /**
> 
> 
> -- 
> Rémi Denis-Courmont
> http://www.remlab.net/



-- 
Johannes Lechner · +49 (174) 314 1778 · Skype: thefudgybeaver · Twitter: @fudgybeaver








More information about the vlc-devel mailing list