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