• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Name
2
3    KHR_stream_cross_process_fd
4
5Name Strings
6
7    EGL_KHR_stream_cross_process_fd
8
9Contributors
10
11    Acorn Pooley
12    Ian Stewart
13
14Contacts
15
16    Acorn Pooley, NVIDIA  (apooley 'at' nvidia.com)
17
18Notice
19
20    Copyright (c) 2011-2013 The Khronos Group Inc. Copyright terms at
21        http://www.khronos.org/registry/speccopyright.html
22
23Status
24
25    Complete.
26    Approved by the EGL Working Group on June 6, 2012.
27    Approved by the Khronos Board of Promoters on July 27, 2012.
28
29Version
30
31    Version 8 - June 5, 2012
32
33Number
34
35    EGL Extension #41
36
37Dependencies
38
39    Requires EGL 1.2.
40    Requires EGL_KHR_stream
41
42    This extension is written based on the wording of the EGL 1.2
43    specification.
44
45    This extension interacts with the following extensions if they are
46    also present:
47        EGL_KHR_stream_producer_eglsurface
48        EGL_KHR_stream_consumer_gltexture
49        EGL_KHR_stream_producer_aldatalocator
50        EGL_KHR_stream_fifo
51
52Overview
53
54    This extension allows an EGLStreamKHR object handle to be
55    duplicated into another process so that the EGLStream producer can
56    be in one process while the EGLStream consumer can be in another
57    process.
58
59    Duplicating the EGLStreamKHR object handle into another process is
60    peformed in 3 steps
61
62        1) Get a file descriptor associated with the EGLStream.
63        2) Duplicate the file descriptor into another process.
64        3) Create an EGLStreamKHR from the duplicated file descriptor in
65            the other process.
66
67    The file descriptor is obtained by calling
68    eglGetStreamFileDescriptorKHR().
69
70    Duplicating the file descriptor into another process is outside
71    the scope of this extension.  See issue #1 for an example of how
72    to do this on a Linux system.
73
74    The EGLStreamKHR object handle is created in the second process by
75    passing the file descriptor to the
76    eglCreateStreamFromFileDescriptorKHR() function.  This must be
77    done while the EGLStream is in the EGL_STREAM_STATE_CREATED_KHR
78    state.
79
80    Once the EGLStreamKHR object handle is created in the second
81    process, it refers to the same EGLStream as the EGLStreamKHR
82    object handle in the original process.  A consumer can be
83    associated with the EGLStream from either process.  A producer can
84    be associated with the EGLStream from either process.
85
86New Types
87
88    Represents a native OS file descriptor.
89
90    typedef int EGLNativeFileDescriptorKHR
91
92New Procedures and Functions
93
94    EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
95        EGLDisplay dpy,
96        EGLStreamKHR stream);
97
98    EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
99        EGLDisplay dpy,
100        EGLNativeFileDescriptorKHR file_descriptor);
101
102New Tokens
103
104    Returned from eglGetStreamFileDescriptorKHR on error.
105
106    #define EGL_NO_FILE_DESCRIPTOR_KHR  ((EGLNativeFileDescriptorKHR)(-1))
107
108Add a new section just after section "3.10.1 Creating an EGLStream" in
109the EGL_KHR_stream extension
110
111    3.10.1.1 Duplicating an EGLStream from a file descriptor
112
113    Call
114
115        EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
116            EGLDisplay dpy,
117            EGLStreamKHR stream);
118
119    to create a file descriptor that refers to the EGLStream.
120    <stream> must be an EGLStream in the EGL_STREAM_STATE_CREATED_KHR
121    state.  eglGetStreamFileDescriptorKHR may be called at most once
122    for any <stream>.
123
124    On success a file descriptor is returned which can be used
125    to create a duplicate EGLStreamKHR handle which refers to the same
126    underlying EGLStream as <stream>.  This file descriptor and file
127    descriptors duplicated from it should only be used in a call to
128    eglCreateStreamFromFileDescriptorKHR() and/or a call to close().
129    In particular reads, writes, and other operations on the file
130    descriptor result in undefined behavior.
131
132    On failure the functions returns EGL_NO_FILE_DESCRIPTOR_KHR and
133    generates an error
134
135        - EGL_BAD_DISPLAY is generated if <dpy> is not a valid
136          initialized EGLDisplay
137
138        - EGL_BAD_STREAM_KHR is generated if <stream> is not a valid
139          EGLStreamKHR handle created for <dpy>.
140
141        - EGL_BAD_STATE_KHR is generated if <stream> is not in the
142          EGL_STREAM_STATE_CREATED_KHR state or if
143          eglGetStreamFileDescriptorKHR() has previously been called
144          on this <stream>.
145
146        - EGL_BAD_STATE_KHR is generated if <stream> was not created
147          by eglCreateStreamKHR (e.g. if it was created by
148          eglCreateStreamFromFileDescriptorKHR).
149
150    The file descriptor returned by eglGetStreamFileDescriptorKHR can
151    be duplicated into a different process address space using system
152    specific mechanisms outside the scope of this specification.  (For
153    example, on a Linux system it can be sent over a UNIX domain
154    socket using sendmsg/recvmsg.)
155
156    Call
157
158        EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
159            EGLDisplay dpy,
160            EGLNativeFileDescriptorKHR file_descriptor);
161
162    to create an EGLStreamKHR handle.  <file_descriptor> must be a
163    file descriptor returned by eglGetStreamFileDescriptorKHR or a
164    file descriptor duplicated from such a file descriptor (possibly
165    in a different process).  The EGLStream must be in the
166    EGL_STREAM_STATE_CREATED_KHR or EGL_STREAM_STATE_CONNECTING_KHR
167    state.
168
169    On success an EGLStreamKHR handle is returned.  This EGLStreamKHR
170    handle refers to the same EGLStream which was used to create the
171    <file_descriptor> or the file descriptor from which
172    <file_descriptor> was duplicated.
173
174    After the file descriptor is passed to
175    eglCreateStreamFromFileDescriptorKHR it may no longer be used to
176    create a new EGLStream.
177
178    On failure EGL_NO_STREAM_KHR is returned and an error is
179    generated.
180
181        - EGL_BAD_DISPLAY is generated if <dpy> is not a valid
182          initialized EGLDisplay
183
184        - EGL_BAD_ATTRIBUTE is generated if <file_descriptor> is
185          EGL_NO_FILE_DESCRIPTOR_KHR.
186
187        - EGL_BAD_ATTRIBUTE is generated if <file_descriptor> is
188          not an open file descriptor referring to an EGLStream
189          created on the same Native Display as <dpy>.
190
191        - EGL_BAD_ATTRIBUTE is generated if <file_descriptor> has
192          already been used to create a stream handle via a previous
193          call to eglCreateStreamFromFileDescriptorKHR.
194
195        - EGL_BAD_STATE_KHR is generated if <stream> is not in the
196          EGL_STREAM_STATE_CREATED_KHR or
197          EGL_STREAM_STATE_CONNECTING_KHR state.
198
199    The application should close the file descriptor and any file
200    descriptors duplicated from it once
201    eglCreateStreamFromFileDescriptorKHR has returned.  Open file
202    descriptors will consume resources until they are closed or until
203    all processes that hold them open have terminated.  Closing the
204    file descriptors after calling
205    eglCreateStreamFromFileDescriptorKHR will not affect the
206    associated EGLStream.  If an application calls
207    eglGetStreamFileDescriptorKHR and then determines that the file
208    descriptor and/or the EGLStream is no longer needed then it may
209    (and should) close the file descriptor and destroy the EGLStream
210    (this is not considered an error).
211
212    If a process which has successfully connected a consumer or
213    producer to the EGLStream terminates (normally or abnormally) then
214    the EGLStream state becomes EGL_STREAM_STATE_DISCONNECTED_KHR.
215
216    If a process has created an EGLStreamKHR handle either with
217    eglCreateStreamKHR or eglCreateStreamFromFileDescriptorKHR but has
218    not connected a producer or consumer to the stream, and this
219    process terminates (normally or abnormally) then this has no
220    effect on the EGLStream.
221
222Interactions with the EGL_KHR_stream_producer_eglsurface extension.
223
224    The eglCreateStreamProducerSurfaceKHR() function can be called
225    from either the process that created the original EGLStreamKHR, or
226    from the process which called eglCreateStreamFromFileDescriptorKHR.
227
228Interactions with the EGL_KHR_stream_consumer_gltexture extension.
229
230    The eglStreamConsumerGLTextureExternalKHR() function can be called
231    from either the process that created the original EGLStreamKHR, or
232    from the process which called
233    eglCreateStreamFromFileDescriptorKHR.  The
234    eglStreamConsumerAcquireKHR() and eglStreamConsumerReleaseKHR()
235    functions must be called from the same process that calls
236    eglStreamConsumerGLTextureExternalKHR() (or else they will fail
237    and generate an EGL_BAD_ACCESS error).
238
239Interactions with the EGL_KHR_stream_producer_aldatalocator extension.
240
241    The CreateMediaPlayer() method can be called from either the
242    process that created the original EGLStreamKHR, or from the
243    process which called eglCreateStreamFromFileDescriptorKHR.
244
245Interactions with the EGL_KHR_stream_fifo extension.
246
247    The queries for EGL_STREAM_FIFO_LENGTH_KHR,
248    EGL_STREAM_TIME_NOW_KHR, EGL_STREAM_TIME_CONSUMER_KHR, and
249    EGL_STREAM_TIME_PRODUCER_KHR can be made from either process.  The
250    time values returned by the EGL_STREAM_TIME_NOW_KHR query will be
251    consistent between the two processes (i.e. if queried at the same
252    time from both processes, the same value (plus or minus some
253    margin of error) will be returned).
254
255Interactions with the EGL_NV_stream_cross_process_fd extension.
256
257    These extensions may both exist on the same implementation and
258    are functionally equivalent. Mixing and matching file descriptors
259    from one extension with functions from the other is allowed.
260
261Interactions with the EGL_NV_stream_sync extension.
262
263    The eglCreateStreamSyncNV() function may only be called from a
264    process which has successfully connected a consumer to the
265    EGLStream.  Otherwise eglCreateStreamSyncNV generates a
266    EGL_BAD_ACCESS error.
267
268Issues
269    1.  How does the application transfer the file descriptor to
270        another process?
271
272        RESOLVED: This is outside the scope of this extension.  The
273        application can use existing operating system mechanisms for
274        duplicating the file descriptor into another process.  For
275        example on Linux a file descriptor can be sent over a UNIX
276        domain socket using the following code (call send_fd() to
277        send the file descriptor, and receive_fd() in the other
278        process to receive the file descriptor).  (The following code
279        is placed into the public domain by its author, Acorn Pooley)
280
281            #include <stdio.h>
282            #include <stdlib.h>
283            #include <unistd.h>
284            #include <sys/types.h>
285            #include <sys/socket.h>
286            #include <sys/un.h>
287
288            #define FATAL_ERROR() exit(1)
289            #define SOCKET_NAME "/tmp/example_socket"
290
291            /* Send <fd_to_send> (a file descriptor) to another process */
292            /* over a unix domain socket named <socket_name>.           */
293            /* <socket_name> can be any nonexistant filename.           */
294            void send_fd(const char *socket_name, int fd_to_send)
295            {
296                int sock_fd;
297                struct sockaddr_un sock_addr;
298                struct msghdr msg;
299                struct iovec iov[1];
300                char ctrl_buf[CMSG_SPACE(sizeof(int))];
301                struct cmsghdr *cmsg = NULL;
302
303                sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
304                if (sock_fd < 0) FATAL_ERROR();
305
306                memset(&sock_addr, 0, sizeof(struct sockaddr_un));
307                sock_addr.sun_family = AF_UNIX;
308                strncpy(sock_addr.sun_path,
309                        socket_name,
310                        sizeof(sock_addr.sun_path)-1);
311
312                while (connect(sock_fd,
313                            (const struct sockaddr*)&sock_addr,
314                            sizeof(struct sockaddr_un))) {
315                    printf("Waiting for reciever\n");
316                    sleep(1);
317                }
318
319                memset(&msg, 0, sizeof(msg));
320
321                iov[0].iov_len  = 1;    // must send at least 1 byte
322                iov[0].iov_base = "x";  // any byte value (value ignored)
323                msg.msg_iov = iov;
324                msg.msg_iovlen = 1;
325
326                memset(ctrl_buf, 0, sizeof(ctrl_buf));
327                msg.msg_control = ctrl_buf;
328                msg.msg_controllen = sizeof(ctrl_buf);
329
330                cmsg = CMSG_FIRSTHDR(&msg);
331                cmsg->cmsg_level = SOL_SOCKET;
332                cmsg->cmsg_type = SCM_RIGHTS;
333                cmsg->cmsg_len = CMSG_LEN(sizeof(int));
334                *((int *) CMSG_DATA(cmsg)) = fd_to_send;
335
336                msg.msg_controllen = cmsg->cmsg_len;
337
338                if (sendmsg(sock_fd, &msg, 0) <= 0) FATAL_ERROR();
339
340                close(sock_fd);
341            }
342
343            /* Listen on a unix domain socket named <socket_name> and  */
344            /* receive a file descriptor from another process.         */
345            /* Returns the file descriptor.  Note: the integer value   */
346            /* of the file descriptor may be different from the        */
347            /* integer value in the other process, but the file        */
348            /* descriptors in each process will refer to the same file */
349            /* object in the kernel.                                   */
350            int receive_fd(const char *socket_name)
351            {
352                int listen_fd;
353                struct sockaddr_un sock_addr;
354                int connect_fd;
355                struct sockaddr_un connect_addr;
356                socklen_t connect_addr_len = 0;
357                struct msghdr msg;
358                struct iovec iov[1];
359                char msg_buf[1];
360                char ctrl_buf[CMSG_SPACE(sizeof(int))];
361                struct cmsghdr *cmsg;
362
363                listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
364                if (listen_fd < 0) FATAL_ERROR();
365
366                unlink(socket_name);
367
368                memset(&sock_addr, 0, sizeof(struct sockaddr_un));
369                sock_addr.sun_family = AF_UNIX;
370                strncpy(sock_addr.sun_path,
371                        socket_name,
372                        sizeof(sock_addr.sun_path)-1);
373
374                if (bind(listen_fd,
375                         (const struct sockaddr*)&sock_addr,
376                         sizeof(struct sockaddr_un)))
377                    FATAL_ERROR();
378
379                if (listen(listen_fd, 1)) FATAL_ERROR();
380
381                connect_fd = accept(
382                                listen_fd,
383                                (struct sockaddr *)&connect_addr,
384                                &connect_addr_len);
385                close(listen_fd);
386                unlink(socket_name);
387                if (connect_fd < 0) FATAL_ERROR();
388
389                memset(&msg, 0, sizeof(msg));
390
391                iov[0].iov_base = msg_buf;
392                iov[0].iov_len  = sizeof(msg_buf);
393                msg.msg_iov = iov;
394                msg.msg_iovlen = 1;
395
396                msg.msg_control = ctrl_buf;
397                msg.msg_controllen = sizeof(ctrl_buf);
398
399                if (recvmsg(connect_fd, &msg, 0) <= 0) FATAL_ERROR();
400
401                cmsg = CMSG_FIRSTHDR(&msg);
402                if (!cmsg) FATAL_ERROR();
403                if (cmsg->cmsg_level != SOL_SOCKET) FATAL_ERROR();
404                if (cmsg->cmsg_type != SCM_RIGHTS) FATAL_ERROR();
405
406                return *(int *) CMSG_DATA(cmsg);
407            }
408
409    2.  Does this extension work with all consumers and all producers?
410
411        RESOLVED: This extension is compatible with
412            EGL_KHR_stream_producer_eglsurface
413            EGL_KHR_stream_consumer_gltexture
414            EGL_KHR_stream_producer_aldatalocator
415            EGL_KHR_stream_fifo
416        as described in the Interactions sections.  Whether an
417        EGLStream that has been duplicated into another process will
418        work with other types of consumers and producers should be
419        mentioned in the description of those consumers and producers.
420
421    3.  Does EGL create a file descriptor for every EGLStream when the
422        EGLStream is created, or is the file descriptor be created
423        when eglGetStreamFileDescriptorKHR is called?
424
425        RESOLVED: This is implementation dependent.  However,
426        recommended behavior is to create the file descriptor when
427        eglGetStreamFileDescriptorKHR is called.  This avoids
428        polluting the file descriptor namespace (which may have a
429        limited size on some systems) with descriptors for EGLStreams
430        which will only be used inside a single process.  The
431        eglGetStreamFileDescriptorKHR function will fail and generate
432        an EGL_BAD_ALLOC error if it is unable to allocate a file
433        descriptor for the EGLStream.
434
435    4.  Should the EGLStream be created from the file descriptor with
436        the existing eglCreateStreamKHR function or with a new
437        function dedicated to that purpose?
438
439        The advantage of creating a new function is that a new
440        parameter can be added with a specific type.  This is not
441        really necessary for this extension since a file descriptor is
442        a small integer which can fit into the EGLint in the
443        eglCreateStreamKHR attrib_list.  However, other similar
444        extensions may be invented that use other types of handles
445        (not file descriptors) which may not fit into an EGLint.
446        Creating a dedicated function allows these other extensions to
447        use a similar function.
448
449        RESOLVED: Use a different function.
450
451    5.  How does this extension interact with the
452        EGL_NV_stream_cross_process_fd extension?
453
454        RESOLVED: These extensions may both exist on the same
455        implementation and are functionally equivalent. Mixing and
456        matching file descriptors from one extension with functions
457        from the other is allowed.
458
459    6.  Who should close the file descriptors and when?
460
461        There is no way for the EGL implementation to safely close all
462        the file descriptors associated with an EGLStream because some
463        of them may have been created using OS specific duping
464        mechanisms.  Also, the app may need to close a descriptor if
465        it runs into an error before it is able to call
466        eglCreateStreamFromFileDescriptorKHR.  Therefore the
467        application will need to close at least some of the created
468        file descriptors.  To make things simple and clear it is
469        therefore left up to the app to close all the file
470        descriptors.  The app is not *required* to do this, but not
471        doing so will "leak" file descriptors which will consume
472        resources until the process terminates.
473
474        Allowing the app to close all file descriptors as soon as
475        eglCreateStreamFromFileDescriptorKHR returns simplifies the
476        app (no need to keep track of open file descriptors).
477
478        RESOLVED: Application is responsible for closing all file
479        descriptors.  They can be safely closed as soon as
480        eglCreateStreamFromFileDescriptorKHR returns.
481
482    7.  What happens when an invalid file descriptor is passed to
483        eglCreateStreamFromFileDescriptorKHR()?
484
485        RESOLVED: The implementation must detect this and generate an
486        error.  If the file descriptor refers to a file then the
487        implementation may not modify the file, change the seek
488        location, or otherwise modify the file descriptor.
489
490    8.  What happens if one process hangs or crashes?
491
492        RESOLVED: If either the consumer's or producer's process
493        terminates (normally or abnormally) the EGL implementation
494        must notice this and place the EGLStream in
495        EGL_STREAM_STATE_DISCONNECTED_KHR state.  If the consumer is
496        blocked in a eglStreamConsumerAcquireKHR() call, the call will
497        generate an EGL_BAD_STATE_KHR message and return EGL_FALSE.
498        If the consumer process has created a reusable sync object with
499        eglCreateStreamSyncNV() and is blocking in a
500        eglClientWaitSyncKHR() call, the call will block until the
501        timeout runs out.
502
503        If the producer process "hangs" (e.g. enters an infinite loop,
504        blocks in a kernel call, etc) then the consumer process will
505        continue to function.  The consumer will continue to use the
506        last frame that the producer produced.  If the producer has
507        not yet produced a frame then the EGLStream will be in
508        EGL_STREAM_STATE_EMPTY_KHR state and no frame will be
509        available.  The consumer process can block in some situations:
510            - If a EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR is set then
511                eglStreamConsumerAcquireKHR() will block until the
512                timeout runs out (or indefinitely if timeout is
513                negative).
514            - eglClientWaitSyncKHR() will block until the timeout runs
515                out.
516
517        If the consumer process "hangs" then the producer process will
518        continue to function.  If the EGLStream has had
519        EGL_STREAM_FIFO_LENGTH_KHR set to a nonzero value then the
520        producer will block indefinitely when it fills the fifo and
521        tries to insert another frame.  Otherwise the producer will
522        not block (as new frames are inserted into the EGLStream old
523        ones will be discarded).
524
525Revision History
526
527    #8  (June 5, 2012) Acorn Pooley
528        - rename from XXX to KHR
529
530    #7  (June 5, 2012) Acorn Pooley
531        - Add issue 8.
532        - Better define EGLStream behavior when a process terminates.
533        - Add Interactions with the EGL_NV_stream_sync extension.
534
535    #6  (April 20, 2012) Ian Stewart
536        - Fix extension/function names in interactions
537        - Removed references to NV_stream_sync.
538        - Changed interactions with NV_stream_cross_process_fd such
539          that they are interchangeable.
540
541    #5  (April 18, 2012) Acorn Pooley
542        - Add issue 7
543        - define errors generated when passing invalid file descriptors
544
545    #4  (January 29, 2012) Acorn Pooley
546        - Fork EGL_XXX_stream_cross_process_fd.txt from
547          EGL_NV_stream_cross_process_fd.txt to make changes suggested
548          by working group.
549        - add issues 4, 5, and 6.
550
551    #3  (January 6, 2012) Acorn Pooley
552        - fix typos (EGLImage -> EGLStream)
553
554    #2  (December 7, 2011) Acorn Pooley
555        - Upload to Khronos for review
556
557    #1  (September 27, 2011) Acorn Pooley
558        - Initial draft
559
560# vim:ai:ts=4:sts=4:expandtab:textwidth=70
561