[Merge] lp:~alfonsosanchezbeato/media-hub/video-desktop-support into lp:media-hub
Jim Hodapp
jim.hodapp at canonical.com
Fri Feb 17 15:46:38 UTC 2017
Review: Needs Fixing
One minor typo to fix.
Diff comments:
>
> === added file 'src/core/media/video/egl_sink.cpp'
> --- src/core/media/video/egl_sink.cpp 1970-01-01 00:00:00 +0000
> +++ src/core/media/video/egl_sink.cpp 2017-02-16 10:17:14 +0000
> @@ -0,0 +1,326 @@
> +/*
> + * Copyright © 2017 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Alfonso Sanchez-Beato <alfonso.sanchez-beato at canonical.com>
> + */
> +
> +#include "core/media/logger/logger.h"
> +
> +#include <core/media/video/egl_sink.h>
> +#include <core/media/video/socket_types.h>
> +
> +#include <EGL/egl.h>
> +#include <EGL/eglext.h>
> +#include <GLES2/gl2.h>
> +#include <GLES2/gl2ext.h>
> +
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +
> +#include <sstream>
> +#include <thread>
> +#include <future>
> +#include <cstring>
> +#include <unistd.h>
> +
> +namespace media = core::ubuntu::media;
> +namespace video = core::ubuntu::media::video;
> +
> +using namespace std;
> +
> +struct video::EglSink::Private
> +{
> +
> + static bool receive_buff(int socket, BufferData *data)
> + {
> + struct msghdr msg{};
> + struct iovec io = { .iov_base = &data->meta,
> + .iov_len = sizeof data->meta };
> + char c_buffer[256];
> + ssize_t res;
> +
> + msg.msg_iov = &io;
> + msg.msg_iovlen = 1;
> +
> + msg.msg_control = c_buffer;
> + msg.msg_controllen = sizeof c_buffer;
> +
> + if ((res = recvmsg(socket, &msg, 0)) == -1) {
> + MH_ERROR("Failed to receive message");
> + return false;
> + } else if (res == 0) {
> + MH_ERROR("Socket shutdown while receiving buffer data");
> + return false;
> + }
> +
> + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
> +
> + memmove(&data->fd, CMSG_DATA(cmsg), sizeof data->fd);
> +
> + MH_DEBUG("Extracted fd %d", data->fd);
> + MH_DEBUG("width %d", data->meta.width);
> + MH_DEBUG("height %d", data->meta.height);
> + MH_DEBUG("fourcc 0x%X", data->meta.fourcc);
> + MH_DEBUG("stride %d", data->meta.stride);
> + MH_DEBUG("offset %d", data->meta.offset);
> +
> + return true;
> + }
> +
> + static void read_sock_events(const media::Player::PlayerKey key,
> + int sock_fd,
> + promise<BufferData>& prom_buff,
> + core::Signal<void>& frame_available)
> + {
> + static const char *consumer_socket = "media-consumer";
> +
> + struct sockaddr_un local;
> + int len;
> + BufferData buff_data;
> +
> + if (sock_fd == -1) {
> + MH_ERROR("Cannot create buffer consumer socket: %s (%d)",
> + strerror(errno), errno);
> + return;
> + }
> +
> + ostringstream sock_name_ss;
> + sock_name_ss << consumer_socket << key;
> + local.sun_family = AF_UNIX;
> + local.sun_path[0] = '\0';
> + strcpy(local.sun_path + 1, sock_name_ss.str().c_str());
> + len = sizeof(local.sun_family) + sock_name_ss.str().length() + 1;
> + if (bind(sock_fd, (struct sockaddr *) &local, len) == -1) {
> + MH_ERROR("Cannot bind consumer socket: %s (%d)",
> + strerror(errno), errno);
> + return;
> + }
> +
> + // Wait for buffer descriptions, pass them to rendering thread
> + if (!receive_buff(sock_fd, &buff_data))
> + return;
> +
> + prom_buff.set_value(buff_data);
> +
> + // Now signal frame syncs
> + while(true) {
> + ssize_t res;
> + char c;
> +
> + res = recv(sock_fd, &c, sizeof c, 0);
> + if (res == -1) {
> + MH_ERROR("while waiting sync: %s (%d)",
> + strerror(errno), errno);
> + return;
> + } else if (res == 0) {
> + MH_DEBUG("Socket shutdown");
> + return;
> + }
> +
> + frame_available();
> + }
> + }
> +
> + bool find_extension(const string& extensions, const string& ext)
> + {
> + size_t len_all = extensions.length();
> + size_t len = ext.length();
> + size_t pos = 0;
> +
> + while ((pos = extensions.find(ext, pos)) != string::npos) {
> + if (pos + len == len_all || extensions[pos + len] == ' ')
> + return true;
> +
> + pos = pos + len;
> + }
> +
> + return false;
> + }
> +
> + Private(uint32_t gl_texture, const media::Player::PlayerKey key)
> + : gl_texture{gl_texture},
> + prom_buff{},
> + fut_buff{prom_buff.get_future()},
> + sock_fd{socket(AF_UNIX, SOCK_DGRAM, 0)},
> + sock_thread{read_sock_events, key, sock_fd,
> + ref(prom_buff), ref(frame_available)},
> + egl_image{EGL_NO_IMAGE_KHR},
> + buf_fd{-1}
> + {
> + const char *extensions;
> + const char *egl_needed[] = {"EGL_KHR_image_base",
> + "EGL_EXT_image_dma_buf_import"};
> + EGLDisplay egl_display = eglGetCurrentDisplay();
> + size_t i;
> +
> + // Retrieve EGL extensions from current display, then make sure the ones
> + // we need are present.
> + extensions = eglQueryString (egl_display, EGL_EXTENSIONS);
> + if (!extensions)
> + throw runtime_error {"Error querying EGL extensions"};
> +
> + for (i = 0; i < sizeof(egl_needed)/sizeof(egl_needed[0]); ++i) {
> + if (!find_extension(extensions, egl_needed[i])) {
> + MH_DEBUG("%s not supported", egl_needed[i]);
> + //ostringstream oss;
> + //oss << egl_needed[i] << " not supported";
> + // TODO: The returned extensions do not really reflect what is
> + // supported by the system, and do not include the ones we need.
> + // It is probably related to how qt initializes EGL, because
> + // mirsink does not show this problem. So we need to
> + // check why extensions is different from es2_info output.
> + //throw runtime_error {oss.str().c_str()};
> + }
> + }
> +
> + // TODO this returns a NULL pointer, probably same issue as with eglQueryString
> + // extensions = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
> + // if (!extensions)
> + // throw runtime_error {"Error querying OpenGL ES extensions"};
> +
> + // if (!find_extension(extensions, "GL_OES_EGL_image_external"))
> + // throw runtime_error {"GL_OES_EGL_image_external is not supported"};
> +
> + // Dinamically load functions from extensions (they are not
s/Dinamically/Dynamically/
> + // automatically exported by EGL library).
> + _eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)
> + eglGetProcAddress("eglCreateImageKHR");
> + _eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)
> + eglGetProcAddress("eglDestroyImageKHR");
> + _glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
> + eglGetProcAddress("glEGLImageTargetTexture2DOES");
> +
> + if (_eglCreateImageKHR == nullptr || _eglDestroyImageKHR == nullptr ||
> + _glEGLImageTargetTexture2DOES == nullptr)
> + throw runtime_error {"Error when loading extensions"};
> + }
> +
> + ~Private()
> + {
> + if (sock_fd != -1) {
> + shutdown(sock_fd, SHUT_RDWR);
> + sock_thread.join();
> + close(sock_fd);
> + }
> +
> + if (buf_fd != -1)
> + close(buf_fd);
> +
> + if (egl_image != EGL_NO_IMAGE_KHR)
> + _eglDestroyImageKHR(eglGetCurrentDisplay(), egl_image);
> + }
> +
> + // This imports dma_buf buffers by using the EGL_EXT_image_dma_buf_import
> + // extension. The buffers have been previously exported in mirsink, using
> + // EGL_MESA_image_dma_buf_export extension. After that, we bind the buffer
> + // to the app texture by using GL_OES_EGL_image_external extension.
> + bool import_buffer(const BufferData *buf_data)
> + {
> + GLenum err;
> + EGLDisplay egl_display = eglGetCurrentDisplay();
> + EGLint image_attrs[] = {
> + EGL_WIDTH, buf_data->meta.width,
> + EGL_HEIGHT, buf_data->meta.height,
> + EGL_LINUX_DRM_FOURCC_EXT, buf_data->meta.fourcc,
> + EGL_DMA_BUF_PLANE0_FD_EXT, buf_data->fd,
> + EGL_DMA_BUF_PLANE0_OFFSET_EXT, buf_data->meta.offset,
> + EGL_DMA_BUF_PLANE0_PITCH_EXT, buf_data->meta.stride,
> + EGL_NONE
> + };
> +
> + buf_fd = buf_data->fd;
> + egl_image = _eglCreateImageKHR(egl_display, EGL_NO_CONTEXT,
> + EGL_LINUX_DMA_BUF_EXT, NULL, image_attrs);
> + if (egl_image == EGL_NO_IMAGE_KHR) {
> + MH_ERROR("eglCreateImageKHR error 0x%X", eglGetError());
> + return false;
> + }
> +
> + // TODO Do this when swapping if we end up importing more than one buffer
> + glBindTexture(GL_TEXTURE_2D, gl_texture);
> + _glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image);
> +
> + while((err = glGetError()) != GL_NO_ERROR)
> + MH_WARNING("OpenGL error 0x%X", err);
> +
> + MH_DEBUG("Image successfully imported");
> +
> + return true;
> + }
> +
> + uint32_t gl_texture;
> + promise<BufferData> prom_buff;
> + future<BufferData> fut_buff;
> + core::Signal<void> frame_available;
> + int sock_fd;
> + thread sock_thread;
> + EGLImageKHR egl_image;
> + int buf_fd;
> + PFNEGLCREATEIMAGEKHRPROC _eglCreateImageKHR;
> + PFNEGLDESTROYIMAGEKHRPROC _eglDestroyImageKHR;
> + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC _glEGLImageTargetTexture2DOES;
> +};
> +
> +function<video::Sink::Ptr(uint32_t)>
> +video::EglSink::factory_for_key(const media::Player::PlayerKey& key)
> +{
> + return [key](uint32_t texture)
> + {
> + return video::Sink::Ptr{new video::EglSink{texture, key}};
> + };
> +}
> +
> +video::EglSink::EglSink(uint32_t gl_texture,
> + const media::Player::PlayerKey key)
> + : d{new Private{gl_texture, key}}
> +{
> +}
> +
> +video::EglSink::~EglSink()
> +{
> +}
> +
> +const core::Signal<void>& video::EglSink::frame_available() const
> +{
> + return d->frame_available;
> +}
> +
> +bool video::EglSink::transformation_matrix(float *matrix) const
> +{
> + // TODO: Can we get orientation on unity8 desktop somehow?
> + static const float identity_4x4[] = { 1, 0, 0, 0,
> + 0, 1, 0, 0,
> + 0, 0, 1, 0,
> + 0, 0, 0, 1 };
> +
> + memcpy(matrix, identity_4x4, sizeof identity_4x4);
> + return true;
> +}
> +
> +bool video::EglSink::swap_buffers() const
> +{
> + // First time called, import buffers
> + if (d->egl_image == EGL_NO_IMAGE_KHR) {
> + BufferData buf_data = d->fut_buff.get();
> + if (!d->import_buffer(&buf_data))
> + return false;
> + }
> +
> + // We need to do nothing here, as the only buffer has already been mapped.
> + // TODO Change when we implement a buffer queue.
> +
> + return true;
> +}
--
https://code.launchpad.net/~alfonsosanchezbeato/media-hub/video-desktop-support/+merge/317181
Your team Ubuntu Phablet Team is subscribed to branch lp:media-hub.
More information about the Ubuntu-reviews
mailing list