[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