gst-decoder.c: improve buffer_to_image() function
* Make EGL image attribute specification code more generic, and not
specific to certain pixel formats, implicitely gaining support for YUY2
* Better handling of gstbuffers with multiple memory blocks
* Print out more information about the stream
* Use the GST_VIDEO_INFO_* macros instead of directly accessing the
GstVideoInfo fields; this is what the GStreamer documentation recommends
Signed-off-by: Carlos Rafael Giani <dv@pseudoterminal.org>
Carlos Rafael Giani authored 7 years ago
Rob Clark committed 7 years ago
0 | 0 | /* |
1 | 1 | * Copyright (c) 2017 Rob Clark <rclark@redhat.com> |
2 | * Copyright (c) 2017 Carlos Rafael Giani <dv@pseudoterminal.org> | |
2 | 3 | * |
3 | 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
4 | 5 | * copy of this software and associated documentation files (the "Software"), |
43 | 44 | |
44 | 45 | #define MAX_NUM_PLANES 3 |
45 | 46 | |
47 | inline static const char * | |
48 | yesno(int yes) | |
49 | { | |
50 | return yes ? "yes" : "no"; | |
51 | } | |
52 | ||
46 | 53 | struct decoder { |
47 | 54 | GMainLoop *loop; |
48 | 55 | GstElement *pipeline; |
98 | 105 | GST_ERROR("unknown format\n"); |
99 | 106 | return GST_PAD_PROBE_OK; |
100 | 107 | } |
101 | ||
102 | GST_DEBUG("got: %ux%u@%4.4s\n", dec->info.width, dec->info.height, | |
103 | (char *)&dec->format); | |
104 | 108 | |
105 | 109 | return GST_PAD_PROBE_OK; |
106 | 110 | } |
355 | 359 | struct { int fd, offset, stride; } planes[MAX_NUM_PLANES]; |
356 | 360 | GstVideoMeta *meta = gst_buffer_get_video_meta(buf); |
357 | 361 | EGLImage image; |
358 | unsigned nmems = gst_buffer_n_memory(buf); | |
359 | unsigned nplanes = (dec->format == DRM_FORMAT_YUV420) ? 3 : 2; | |
360 | unsigned i; | |
361 | ||
362 | if (nmems == nplanes) { | |
363 | // XXX TODO.. | |
364 | } else if (nmems == 1) { | |
365 | GstMemory *mem = gst_buffer_peek_memory(buf, 0); | |
366 | int fd; | |
367 | ||
368 | if (dec->frame == 0) { | |
369 | printf("%s zero-copy\n", gst_is_dmabuf_memory(mem) ? "using" : "not"); | |
362 | guint nmems = gst_buffer_n_memory(buf); | |
363 | guint nplanes = GST_VIDEO_INFO_N_PLANES(&(dec->info)); | |
364 | guint i; | |
365 | guint width, height; | |
366 | gboolean is_dmabuf_mem; | |
367 | GstMemory *mem; | |
368 | int dmabuf_fd = -1; | |
369 | ||
370 | static const EGLint egl_dmabuf_plane_fd_attr[MAX_NUM_PLANES] = { | |
371 | EGL_DMA_BUF_PLANE0_FD_EXT, | |
372 | EGL_DMA_BUF_PLANE1_FD_EXT, | |
373 | EGL_DMA_BUF_PLANE2_FD_EXT, | |
374 | }; | |
375 | static const EGLint egl_dmabuf_plane_offset_attr[MAX_NUM_PLANES] = { | |
376 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, | |
377 | EGL_DMA_BUF_PLANE1_OFFSET_EXT, | |
378 | EGL_DMA_BUF_PLANE2_OFFSET_EXT, | |
379 | }; | |
380 | static const EGLint egl_dmabuf_plane_pitch_attr[MAX_NUM_PLANES] = { | |
381 | EGL_DMA_BUF_PLANE0_PITCH_EXT, | |
382 | EGL_DMA_BUF_PLANE1_PITCH_EXT, | |
383 | EGL_DMA_BUF_PLANE2_PITCH_EXT, | |
384 | }; | |
385 | ||
386 | /* Query gst_is_dmabuf_memory() here, since the gstmemory | |
387 | * block might get merged below by gst_buffer_map(), meaning | |
388 | * that the mem pointer would become invalid */ | |
389 | mem = gst_buffer_peek_memory(buf, 0); | |
390 | is_dmabuf_mem = gst_is_dmabuf_memory(mem); | |
391 | ||
392 | if (nmems > 1) { | |
393 | if (is_dmabuf_mem) { | |
394 | /* this case currently is not defined */ | |
395 | ||
396 | GST_FIXME("gstbuffers with multiple memory blocks and DMABUF " | |
397 | "memory currently are not supported"); | |
398 | return EGL_NO_IMAGE_KHR; | |
370 | 399 | } |
371 | 400 | |
372 | if (gst_is_dmabuf_memory(mem)) { | |
373 | fd = dup(gst_dmabuf_memory_get_fd(mem)); | |
374 | } else { | |
375 | GstMapInfo info; | |
376 | gst_memory_map(mem, &info, GST_MAP_READ); | |
377 | fd = buf_to_fd(dec->gbm, info.size, info.data); | |
378 | gst_memory_unmap(mem, &info); | |
401 | /* if this is not DMABUF memory, then the gst_buffer_map() | |
402 | * call below will automatically merge the memory blocks | |
403 | */ | |
404 | } | |
405 | ||
406 | if (is_dmabuf_mem) { | |
407 | dmabuf_fd = dup(gst_dmabuf_memory_get_fd(mem)); | |
408 | } else { | |
409 | GstMapInfo map_info; | |
410 | gst_buffer_map(buf, &map_info, GST_MAP_READ); | |
411 | dmabuf_fd = buf_to_fd(dec->gbm, map_info.size, map_info.data); | |
412 | gst_buffer_unmap(buf, &map_info); | |
413 | } | |
414 | ||
415 | if (dmabuf_fd < 0) { | |
416 | GST_ERROR("could not obtain DMABUF FD"); | |
417 | return EGL_NO_IMAGE_KHR; | |
418 | } | |
419 | ||
420 | /* Usually, a videometa should be present, since by using the internal kmscube | |
421 | * video_appsink element instead of the regular appsink, it is guaranteed that | |
422 | * video meta support is declared in the video_appsink's allocation query. | |
423 | * However, this assumes that upstream elements actually look at the allocation | |
424 | * query's contents properly, or that they even send a query at all. If this | |
425 | * is not the case, then upstream might decide to push frames without adding | |
426 | * a meta. It can happen, and in this case, look at the video info data as | |
427 | * a fallback (it is computed out of the input caps). | |
428 | */ | |
429 | if (meta) { | |
430 | for (i = 0; i < nplanes; i++) { | |
431 | planes[i].fd = dmabuf_fd; | |
432 | planes[i].offset = meta->offset[i]; | |
433 | planes[i].stride = meta->stride[i]; | |
379 | 434 | } |
380 | ||
381 | // XXX why don't we get meta?? | |
382 | if (meta) { | |
383 | for (i = 0; i < nplanes; i++) { | |
384 | planes[i].fd = fd; | |
385 | planes[i].offset = meta->offset[i]; | |
386 | planes[i].stride = meta->stride[i]; | |
387 | } | |
388 | } else { | |
389 | int offset = 0, stride = dec->info.width, height = dec->info.height; | |
390 | ||
391 | for (i = 0; i < nplanes; i++) { | |
392 | ||
393 | if (i == 1) { | |
394 | height /= 2; | |
395 | if (nplanes == 3) | |
396 | stride /= 2; | |
397 | } | |
398 | ||
399 | planes[i].fd = fd; | |
400 | planes[i].offset = offset; | |
401 | planes[i].stride = stride; | |
402 | ||
403 | offset += stride * height; | |
404 | } | |
435 | } else { | |
436 | for (i = 0; i < nplanes; i++) { | |
437 | planes[i].fd = dmabuf_fd; | |
438 | planes[i].offset = GST_VIDEO_INFO_PLANE_OFFSET(&(dec->info), i); | |
439 | planes[i].stride = GST_VIDEO_INFO_PLANE_STRIDE(&(dec->info), i); | |
405 | 440 | } |
406 | 441 | } |
407 | 442 | |
408 | if (dec->format == DRM_FORMAT_NV12) { | |
409 | const EGLint attr[] = { | |
410 | EGL_WIDTH, dec->info.width, | |
411 | EGL_HEIGHT, dec->info.height, | |
412 | EGL_LINUX_DRM_FOURCC_EXT, dec->format, | |
413 | EGL_DMA_BUF_PLANE0_FD_EXT, planes[0].fd, | |
414 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, planes[0].offset, | |
415 | EGL_DMA_BUF_PLANE0_PITCH_EXT, planes[0].stride, | |
416 | EGL_DMA_BUF_PLANE1_FD_EXT, planes[1].fd, | |
417 | EGL_DMA_BUF_PLANE1_OFFSET_EXT, planes[1].offset, | |
418 | EGL_DMA_BUF_PLANE1_PITCH_EXT, planes[1].stride, | |
419 | EGL_NONE | |
443 | width = GST_VIDEO_INFO_WIDTH(&(dec->info)); | |
444 | height = GST_VIDEO_INFO_HEIGHT(&(dec->info)); | |
445 | ||
446 | /* output some information at the beginning (= when the first frame is handled) */ | |
447 | if (dec->frame == 0) { | |
448 | GstVideoFormat pixfmt; | |
449 | const char *pixfmt_str; | |
450 | ||
451 | pixfmt = GST_VIDEO_INFO_FORMAT(&(dec->info)); | |
452 | pixfmt_str = gst_video_format_to_string(pixfmt); | |
453 | ||
454 | printf("===================================\n"); | |
455 | printf("GStreamer video stream information:\n"); | |
456 | printf(" size: %u x %u pixel\n", width, height); | |
457 | printf(" pixel format: %s number of planes: %u\n", pixfmt_str, nplanes); | |
458 | printf(" can use zero-copy: %s\n", yesno(is_dmabuf_mem)); | |
459 | printf(" video meta found: %s\n", yesno(meta != NULL)); | |
460 | printf("===================================\n"); | |
461 | } | |
462 | ||
463 | { | |
464 | /* Initialize the first 6 attributes with values that are | |
465 | * plane invariant (width, height, format) */ | |
466 | EGLint attr[6 + 6*(MAX_NUM_PLANES) + 1] = { | |
467 | EGL_WIDTH, width, | |
468 | EGL_HEIGHT, height, | |
469 | EGL_LINUX_DRM_FOURCC_EXT, dec->format | |
420 | 470 | }; |
421 | 471 | |
422 | image = dec->egl->eglCreateImageKHR(dec->egl->display, EGL_NO_CONTEXT, | |
423 | EGL_LINUX_DMA_BUF_EXT, NULL, attr); | |
424 | } else { | |
425 | const EGLint attr[] = { | |
426 | EGL_WIDTH, dec->info.width, | |
427 | EGL_HEIGHT, dec->info.height, | |
428 | EGL_LINUX_DRM_FOURCC_EXT, dec->format, | |
429 | EGL_DMA_BUF_PLANE0_FD_EXT, planes[0].fd, | |
430 | EGL_DMA_BUF_PLANE0_OFFSET_EXT, planes[0].offset, | |
431 | EGL_DMA_BUF_PLANE0_PITCH_EXT, planes[0].stride, | |
432 | EGL_DMA_BUF_PLANE1_FD_EXT, planes[1].fd, | |
433 | EGL_DMA_BUF_PLANE1_OFFSET_EXT, planes[1].offset, | |
434 | EGL_DMA_BUF_PLANE1_PITCH_EXT, planes[1].stride, | |
435 | EGL_DMA_BUF_PLANE2_FD_EXT, planes[2].fd, | |
436 | EGL_DMA_BUF_PLANE2_OFFSET_EXT, planes[2].offset, | |
437 | EGL_DMA_BUF_PLANE2_PITCH_EXT, planes[2].stride, | |
438 | EGL_NONE | |
439 | }; | |
472 | for (i = 0; i < nplanes; i++) { | |
473 | attr[6 + 6*i + 0] = egl_dmabuf_plane_fd_attr[i]; | |
474 | attr[6 + 6*i + 1] = planes[i].fd; | |
475 | attr[6 + 6*i + 2] = egl_dmabuf_plane_offset_attr[i]; | |
476 | attr[6 + 6*i + 3] = planes[i].offset; | |
477 | attr[6 + 6*i + 4] = egl_dmabuf_plane_pitch_attr[i]; | |
478 | attr[6 + 6*i + 5] = planes[i].stride; | |
479 | } | |
480 | ||
481 | attr[6 + 6*nplanes] = EGL_NONE; | |
440 | 482 | |
441 | 483 | image = dec->egl->eglCreateImageKHR(dec->egl->display, EGL_NO_CONTEXT, |
442 | 484 | EGL_LINUX_DMA_BUF_EXT, NULL, attr); |