blob: ecfefd6567195c99c70274fa169c33c0470bfa94 [file] [log] [blame]
Name
KHR_stream_cross_process_fd
Name Strings
EGL_KHR_stream_cross_process_fd
Contributors
Acorn Pooley
Ian Stewart
Contacts
Acorn Pooley, NVIDIA (apooley 'at' nvidia.com)
Notice
Copyright (c) 2011-2013 The Khronos Group Inc. Copyright terms at
http://www.khronos.org/registry/speccopyright.html
Status
Complete.
Approved by the EGL Working Group on June 6, 2012.
Approved by the Khronos Board of Promoters on July 27, 2012.
Version
Version 8 - June 5, 2012
Number
EGL Extension #41
Dependencies
Requires EGL 1.2.
Requires EGL_KHR_stream
This extension is written based on the wording of the EGL 1.2
specification.
This extension interacts with the following extensions if they are
also present:
EGL_KHR_stream_producer_eglsurface
EGL_KHR_stream_consumer_gltexture
EGL_KHR_stream_producer_aldatalocator
EGL_KHR_stream_fifo
Overview
This extension allows an EGLStreamKHR object handle to be
duplicated into another process so that the EGLStream producer can
be in one process while the EGLStream consumer can be in another
process.
Duplicating the EGLStreamKHR object handle into another process is
peformed in 3 steps
1) Get a file descriptor associated with the EGLStream.
2) Duplicate the file descriptor into another process.
3) Create an EGLStreamKHR from the duplicated file descriptor in
the other process.
The file descriptor is obtained by calling
eglGetStreamFileDescriptorKHR().
Duplicating the file descriptor into another process is outside
the scope of this extension. See issue #1 for an example of how
to do this on a Linux system.
The EGLStreamKHR object handle is created in the second process by
passing the file descriptor to the
eglCreateStreamFromFileDescriptorKHR() function. This must be
done while the EGLStream is in the EGL_STREAM_STATE_CREATED_KHR
state.
Once the EGLStreamKHR object handle is created in the second
process, it refers to the same EGLStream as the EGLStreamKHR
object handle in the original process. A consumer can be
associated with the EGLStream from either process. A producer can
be associated with the EGLStream from either process.
New Types
Represents a native OS file descriptor.
typedef int EGLNativeFileDescriptorKHR
New Procedures and Functions
EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
EGLDisplay dpy,
EGLStreamKHR stream);
EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
EGLDisplay dpy,
EGLNativeFileDescriptorKHR file_descriptor);
New Tokens
Returned from eglGetStreamFileDescriptorKHR on error.
#define EGL_NO_FILE_DESCRIPTOR_KHR ((EGLNativeFileDescriptorKHR)(-1))
Add a new section just after section "3.10.1 Creating an EGLStream" in
the EGL_KHR_stream extension
3.10.1.1 Duplicating an EGLStream from a file descriptor
Call
EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(
EGLDisplay dpy,
EGLStreamKHR stream);
to create a file descriptor that refers to the EGLStream.
<stream> must be an EGLStream in the EGL_STREAM_STATE_CREATED_KHR
state. eglGetStreamFileDescriptorKHR may be called at most once
for any <stream>.
On success a file descriptor is returned which can be used
to create a duplicate EGLStreamKHR handle which refers to the same
underlying EGLStream as <stream>. This file descriptor and file
descriptors duplicated from it should only be used in a call to
eglCreateStreamFromFileDescriptorKHR() and/or a call to close().
In particular reads, writes, and other operations on the file
descriptor result in undefined behavior.
On failure the functions returns EGL_NO_FILE_DESCRIPTOR_KHR and
generates an error
- EGL_BAD_DISPLAY is generated if <dpy> is not a valid
initialized EGLDisplay
- EGL_BAD_STREAM_KHR is generated if <stream> is not a valid
EGLStreamKHR handle created for <dpy>.
- EGL_BAD_STATE_KHR is generated if <stream> is not in the
EGL_STREAM_STATE_CREATED_KHR state or if
eglGetStreamFileDescriptorKHR() has previously been called
on this <stream>.
- EGL_BAD_STATE_KHR is generated if <stream> was not created
by eglCreateStreamKHR (e.g. if it was created by
eglCreateStreamFromFileDescriptorKHR).
The file descriptor returned by eglGetStreamFileDescriptorKHR can
be duplicated into a different process address space using system
specific mechanisms outside the scope of this specification. (For
example, on a Linux system it can be sent over a UNIX domain
socket using sendmsg/recvmsg.)
Call
EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(
EGLDisplay dpy,
EGLNativeFileDescriptorKHR file_descriptor);
to create an EGLStreamKHR handle. <file_descriptor> must be a
file descriptor returned by eglGetStreamFileDescriptorKHR or a
file descriptor duplicated from such a file descriptor (possibly
in a different process). The EGLStream must be in the
EGL_STREAM_STATE_CREATED_KHR or EGL_STREAM_STATE_CONNECTING_KHR
state.
On success an EGLStreamKHR handle is returned. This EGLStreamKHR
handle refers to the same EGLStream which was used to create the
<file_descriptor> or the file descriptor from which
<file_descriptor> was duplicated.
After the file descriptor is passed to
eglCreateStreamFromFileDescriptorKHR it may no longer be used to
create a new EGLStream.
On failure EGL_NO_STREAM_KHR is returned and an error is
generated.
- EGL_BAD_DISPLAY is generated if <dpy> is not a valid
initialized EGLDisplay
- EGL_BAD_ATTRIBUTE is generated if <file_descriptor> is
EGL_NO_FILE_DESCRIPTOR_KHR.
- EGL_BAD_ATTRIBUTE is generated if <file_descriptor> is
not an open file descriptor referring to an EGLStream
created on the same Native Display as <dpy>.
- EGL_BAD_ATTRIBUTE is generated if <file_descriptor> has
already been used to create a stream handle via a previous
call to eglCreateStreamFromFileDescriptorKHR.
- EGL_BAD_STATE_KHR is generated if <stream> is not in the
EGL_STREAM_STATE_CREATED_KHR or
EGL_STREAM_STATE_CONNECTING_KHR state.
The application should close the file descriptor and any file
descriptors duplicated from it once
eglCreateStreamFromFileDescriptorKHR has returned. Open file
descriptors will consume resources until they are closed or until
all processes that hold them open have terminated. Closing the
file descriptors after calling
eglCreateStreamFromFileDescriptorKHR will not affect the
associated EGLStream. If an application calls
eglGetStreamFileDescriptorKHR and then determines that the file
descriptor and/or the EGLStream is no longer needed then it may
(and should) close the file descriptor and destroy the EGLStream
(this is not considered an error).
If a process which has successfully connected a consumer or
producer to the EGLStream terminates (normally or abnormally) then
the EGLStream state becomes EGL_STREAM_STATE_DISCONNECTED_KHR.
If a process has created an EGLStreamKHR handle either with
eglCreateStreamKHR or eglCreateStreamFromFileDescriptorKHR but has
not connected a producer or consumer to the stream, and this
process terminates (normally or abnormally) then this has no
effect on the EGLStream.
Interactions with the EGL_KHR_stream_producer_eglsurface extension.
The eglCreateStreamProducerSurfaceKHR() function can be called
from either the process that created the original EGLStreamKHR, or
from the process which called eglCreateStreamFromFileDescriptorKHR.
Interactions with the EGL_KHR_stream_consumer_gltexture extension.
The eglStreamConsumerGLTextureExternalKHR() function can be called
from either the process that created the original EGLStreamKHR, or
from the process which called
eglCreateStreamFromFileDescriptorKHR. The
eglStreamConsumerAcquireKHR() and eglStreamConsumerReleaseKHR()
functions must be called from the same process that calls
eglStreamConsumerGLTextureExternalKHR() (or else they will fail
and generate an EGL_BAD_ACCESS error).
Interactions with the EGL_KHR_stream_producer_aldatalocator extension.
The CreateMediaPlayer() method can be called from either the
process that created the original EGLStreamKHR, or from the
process which called eglCreateStreamFromFileDescriptorKHR.
Interactions with the EGL_KHR_stream_fifo extension.
The queries for EGL_STREAM_FIFO_LENGTH_KHR,
EGL_STREAM_TIME_NOW_KHR, EGL_STREAM_TIME_CONSUMER_KHR, and
EGL_STREAM_TIME_PRODUCER_KHR can be made from either process. The
time values returned by the EGL_STREAM_TIME_NOW_KHR query will be
consistent between the two processes (i.e. if queried at the same
time from both processes, the same value (plus or minus some
margin of error) will be returned).
Interactions with the EGL_NV_stream_cross_process_fd extension.
These extensions may both exist on the same implementation and
are functionally equivalent. Mixing and matching file descriptors
from one extension with functions from the other is allowed.
Interactions with the EGL_NV_stream_sync extension.
The eglCreateStreamSyncNV() function may only be called from a
process which has successfully connected a consumer to the
EGLStream. Otherwise eglCreateStreamSyncNV generates a
EGL_BAD_ACCESS error.
Issues
1. How does the application transfer the file descriptor to
another process?
RESOLVED: This is outside the scope of this extension. The
application can use existing operating system mechanisms for
duplicating the file descriptor into another process. For
example on Linux a file descriptor can be sent over a UNIX
domain socket using the following code (call send_fd() to
send the file descriptor, and receive_fd() in the other
process to receive the file descriptor). (The following code
is placed into the public domain by its author, Acorn Pooley)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define FATAL_ERROR() exit(1)
#define SOCKET_NAME "/tmp/example_socket"
/* Send <fd_to_send> (a file descriptor) to another process */
/* over a unix domain socket named <socket_name>. */
/* <socket_name> can be any nonexistant filename. */
void send_fd(const char *socket_name, int fd_to_send)
{
int sock_fd;
struct sockaddr_un sock_addr;
struct msghdr msg;
struct iovec iov[1];
char ctrl_buf[CMSG_SPACE(sizeof(int))];
struct cmsghdr *cmsg = NULL;
sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) FATAL_ERROR();
memset(&sock_addr, 0, sizeof(struct sockaddr_un));
sock_addr.sun_family = AF_UNIX;
strncpy(sock_addr.sun_path,
socket_name,
sizeof(sock_addr.sun_path)-1);
while (connect(sock_fd,
(const struct sockaddr*)&sock_addr,
sizeof(struct sockaddr_un))) {
printf("Waiting for reciever\n");
sleep(1);
}
memset(&msg, 0, sizeof(msg));
iov[0].iov_len = 1; // must send at least 1 byte
iov[0].iov_base = "x"; // any byte value (value ignored)
msg.msg_iov = iov;
msg.msg_iovlen = 1;
memset(ctrl_buf, 0, sizeof(ctrl_buf));
msg.msg_control = ctrl_buf;
msg.msg_controllen = sizeof(ctrl_buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*((int *) CMSG_DATA(cmsg)) = fd_to_send;
msg.msg_controllen = cmsg->cmsg_len;
if (sendmsg(sock_fd, &msg, 0) <= 0) FATAL_ERROR();
close(sock_fd);
}
/* Listen on a unix domain socket named <socket_name> and */
/* receive a file descriptor from another process. */
/* Returns the file descriptor. Note: the integer value */
/* of the file descriptor may be different from the */
/* integer value in the other process, but the file */
/* descriptors in each process will refer to the same file */
/* object in the kernel. */
int receive_fd(const char *socket_name)
{
int listen_fd;
struct sockaddr_un sock_addr;
int connect_fd;
struct sockaddr_un connect_addr;
socklen_t connect_addr_len = 0;
struct msghdr msg;
struct iovec iov[1];
char msg_buf[1];
char ctrl_buf[CMSG_SPACE(sizeof(int))];
struct cmsghdr *cmsg;
listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (listen_fd < 0) FATAL_ERROR();
unlink(socket_name);
memset(&sock_addr, 0, sizeof(struct sockaddr_un));
sock_addr.sun_family = AF_UNIX;
strncpy(sock_addr.sun_path,
socket_name,
sizeof(sock_addr.sun_path)-1);
if (bind(listen_fd,
(const struct sockaddr*)&sock_addr,
sizeof(struct sockaddr_un)))
FATAL_ERROR();
if (listen(listen_fd, 1)) FATAL_ERROR();
connect_fd = accept(
listen_fd,
(struct sockaddr *)&connect_addr,
&connect_addr_len);
close(listen_fd);
unlink(socket_name);
if (connect_fd < 0) FATAL_ERROR();
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = msg_buf;
iov[0].iov_len = sizeof(msg_buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ctrl_buf;
msg.msg_controllen = sizeof(ctrl_buf);
if (recvmsg(connect_fd, &msg, 0) <= 0) FATAL_ERROR();
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg) FATAL_ERROR();
if (cmsg->cmsg_level != SOL_SOCKET) FATAL_ERROR();
if (cmsg->cmsg_type != SCM_RIGHTS) FATAL_ERROR();
return *(int *) CMSG_DATA(cmsg);
}
2. Does this extension work with all consumers and all producers?
RESOLVED: This extension is compatible with
EGL_KHR_stream_producer_eglsurface
EGL_KHR_stream_consumer_gltexture
EGL_KHR_stream_producer_aldatalocator
EGL_KHR_stream_fifo
as described in the Interactions sections. Whether an
EGLStream that has been duplicated into another process will
work with other types of consumers and producers should be
mentioned in the description of those consumers and producers.
3. Does EGL create a file descriptor for every EGLStream when the
EGLStream is created, or is the file descriptor be created
when eglGetStreamFileDescriptorKHR is called?
RESOLVED: This is implementation dependent. However,
recommended behavior is to create the file descriptor when
eglGetStreamFileDescriptorKHR is called. This avoids
polluting the file descriptor namespace (which may have a
limited size on some systems) with descriptors for EGLStreams
which will only be used inside a single process. The
eglGetStreamFileDescriptorKHR function will fail and generate
an EGL_BAD_ALLOC error if it is unable to allocate a file
descriptor for the EGLStream.
4. Should the EGLStream be created from the file descriptor with
the existing eglCreateStreamKHR function or with a new
function dedicated to that purpose?
The advantage of creating a new function is that a new
parameter can be added with a specific type. This is not
really necessary for this extension since a file descriptor is
a small integer which can fit into the EGLint in the
eglCreateStreamKHR attrib_list. However, other similar
extensions may be invented that use other types of handles
(not file descriptors) which may not fit into an EGLint.
Creating a dedicated function allows these other extensions to
use a similar function.
RESOLVED: Use a different function.
5. How does this extension interact with the
EGL_NV_stream_cross_process_fd extension?
RESOLVED: These extensions may both exist on the same
implementation and are functionally equivalent. Mixing and
matching file descriptors from one extension with functions
from the other is allowed.
6. Who should close the file descriptors and when?
There is no way for the EGL implementation to safely close all
the file descriptors associated with an EGLStream because some
of them may have been created using OS specific duping
mechanisms. Also, the app may need to close a descriptor if
it runs into an error before it is able to call
eglCreateStreamFromFileDescriptorKHR. Therefore the
application will need to close at least some of the created
file descriptors. To make things simple and clear it is
therefore left up to the app to close all the file
descriptors. The app is not *required* to do this, but not
doing so will "leak" file descriptors which will consume
resources until the process terminates.
Allowing the app to close all file descriptors as soon as
eglCreateStreamFromFileDescriptorKHR returns simplifies the
app (no need to keep track of open file descriptors).
RESOLVED: Application is responsible for closing all file
descriptors. They can be safely closed as soon as
eglCreateStreamFromFileDescriptorKHR returns.
7. What happens when an invalid file descriptor is passed to
eglCreateStreamFromFileDescriptorKHR()?
RESOLVED: The implementation must detect this and generate an
error. If the file descriptor refers to a file then the
implementation may not modify the file, change the seek
location, or otherwise modify the file descriptor.
8. What happens if one process hangs or crashes?
RESOLVED: If either the consumer's or producer's process
terminates (normally or abnormally) the EGL implementation
must notice this and place the EGLStream in
EGL_STREAM_STATE_DISCONNECTED_KHR state. If the consumer is
blocked in a eglStreamConsumerAcquireKHR() call, the call will
generate an EGL_BAD_STATE_KHR message and return EGL_FALSE.
If the consumer process has created a reusable sync object with
eglCreateStreamSyncNV() and is blocking in a
eglClientWaitSyncKHR() call, the call will block until the
timeout runs out.
If the producer process "hangs" (e.g. enters an infinite loop,
blocks in a kernel call, etc) then the consumer process will
continue to function. The consumer will continue to use the
last frame that the producer produced. If the producer has
not yet produced a frame then the EGLStream will be in
EGL_STREAM_STATE_EMPTY_KHR state and no frame will be
available. The consumer process can block in some situations:
- If a EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR is set then
eglStreamConsumerAcquireKHR() will block until the
timeout runs out (or indefinitely if timeout is
negative).
- eglClientWaitSyncKHR() will block until the timeout runs
out.
If the consumer process "hangs" then the producer process will
continue to function. If the EGLStream has had
EGL_STREAM_FIFO_LENGTH_KHR set to a nonzero value then the
producer will block indefinitely when it fills the fifo and
tries to insert another frame. Otherwise the producer will
not block (as new frames are inserted into the EGLStream old
ones will be discarded).
Revision History
#8 (June 5, 2012) Acorn Pooley
- rename from XXX to KHR
#7 (June 5, 2012) Acorn Pooley
- Add issue 8.
- Better define EGLStream behavior when a process terminates.
- Add Interactions with the EGL_NV_stream_sync extension.
#6 (April 20, 2012) Ian Stewart
- Fix extension/function names in interactions
- Removed references to NV_stream_sync.
- Changed interactions with NV_stream_cross_process_fd such
that they are interchangeable.
#5 (April 18, 2012) Acorn Pooley
- Add issue 7
- define errors generated when passing invalid file descriptors
#4 (January 29, 2012) Acorn Pooley
- Fork EGL_XXX_stream_cross_process_fd.txt from
EGL_NV_stream_cross_process_fd.txt to make changes suggested
by working group.
- add issues 4, 5, and 6.
#3 (January 6, 2012) Acorn Pooley
- fix typos (EGLImage -> EGLStream)
#2 (December 7, 2011) Acorn Pooley
- Upload to Khronos for review
#1 (September 27, 2011) Acorn Pooley
- Initial draft
# vim:ai:ts=4:sts=4:expandtab:textwidth=70