/*
* Copyright (C) 2014 Intel Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "v4l2_wrapper.h"
#if defined(__ENABLE_V4L2_OPS__)
#include "v4l2codec_device_ops.h"
#endif
#include "v4l2_codecbase.h"
#include <stdlib.h>
#include "common/log.h"
#include "common/lock.h"
#include <map>
typedef SharedPtr < V4l2CodecBase > V4l2CodecPtr;
/** <pre>
v4l2_wrapper implements a wrapper library for yami encoder/decoder, it translates v4l2 ioctl to yami APIs.
There are four threads in the wrapper library.
Two threads for v4l2 interface: one for device pool (pool thread), one for device operation (device thread: deque, enque etc)
Two threads are internal worker to drive data input (input thread) and output (output thread) respectively.
- Device thread owns yami encoder before input/output thread launch and after input/output thread exit . encoder->stop() defers to device _close() instead of STREAMOFF ioctl.
- Dynamic encoder parameter change (bitrate/framerate etc) are accepted in device operation thread, and executed in input thread, with mutex lock
Input thread keeps runing until: no input buffer available (from device enque buffer) or encode() fail (yami/dirver is busy).
Input Thread is woken up by: enqued a new input buffer, output thread run once successfully, or input stream stops.
Output thread keeps runing until: no output buffer available (from device enque buffer) or getOutput() fail (no encoded frame available from yami/driver).
Output Thread is woken up by: enqued a new output buffer, input thread run once successfully, or output stream stops.
Initial buffers status are at client side.
</pre> */
typedef std::map<int /* fd */, V4l2CodecPtr> CodecPtrFdMap;
static CodecPtrFdMap s_codecMap;
static YamiMediaCodec::Lock s_codecMapLock;
V4l2CodecPtr _findCodecFromFd(int fd)
{
V4l2CodecPtr v4l2Codec;
YamiMediaCodec::AutoLock locker(s_codecMapLock);
CodecPtrFdMap::iterator it = s_codecMap.find(fd);
if (it != s_codecMap.end()) {
v4l2Codec = it->second;
ASSERT(fd == v4l2Codec->fd());
}
return v4l2Codec;
}
int32_t YamiV4L2_Open(const char* name, int32_t flags)
{
V4l2CodecPtr v4l2Codec = V4l2CodecBase::createCodec(name, flags);
{
YamiMediaCodec::AutoLock locker(s_codecMapLock);
s_codecMap[v4l2Codec->fd()] = v4l2Codec;
}
INFO("add encoder(fd: %d) to list", v4l2Codec->fd());
#if 0 // if necessary, some pre-sandbox operation goes here
// used for chrome sandbox
// preloadDriverHandle = dlopen( preloadDriverName, RTLD_NOW| RTLD_GLOBAL | RTLD_NODELETE); // RTLD_NODELETE??
// create VaapiDisplay to make sure vaapi driver are loaded before sandbox, the display can be reused by future request
NativeDisplay nativeDisplay;
nativeDisplay.type = NATIVE_DISPLAY_DRM;
s_display = YamiMediaCodec::VaapiDisplay::create(nativeDisplay);
#endif
return v4l2Codec->fd();
}
int32_t YamiV4L2_Close(int32_t fd)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
bool ret = true;
ASSERT(v4l2Codec);
ret &= v4l2Codec->close();
ASSERT(ret);
{
YamiMediaCodec::AutoLock locker(s_codecMapLock);
ret &= s_codecMap.erase(fd);
}
INFO("remove encoder(fd:%d) from list, ret: %d", fd, ret);
ASSERT(ret);
return ret ? 0 : -1;
}
// FIXME, if chromeos change to SetParameter as well, we can drop this func
int32_t YamiV4L2_FrameMemoryType(int32_t fd, VideoDataMemoryType memory_type)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->setFrameMemoryType(memory_type);
}
int32_t YamiV4L2_SvcT(int32_t fd, bool enable)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->setSvcT(enable);
}
int32_t YamiV4L2_SetThumbnailMode(int32_t fd, bool enable)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->setThumbnailMode(enable);
}
int32_t YamiV4L2_Ioctl(int32_t fd, int command, void* arg)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->ioctl(command, arg);
}
int32_t YamiV4L2_Poll(int32_t fd, bool poll_device, bool* event_pending)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->poll(poll_device, event_pending);
}
int32_t YamiV4L2_SetDevicePollInterrupt(int32_t fd)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->setDeviceEvent(1);
}
int32_t YamiV4L2_ClearDevicePollInterrupt(int32_t fd)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->clearDeviceEvent(1);
}
void* YamiV4L2_Mmap(void* addr, size_t length,
int prot, int flags, int fd, unsigned int offset)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->mmap(addr, length, prot, flags, offset);
}
int32_t YamiV4L2_Munmap(void* addr, size_t length)
{
return 0;
}
#if defined(__ENABLE_WAYLAND__)
int32_t YamiV4L2_SetWaylandDisplay(int32_t fd, struct wl_display* wlDisplay)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
bool ret = true;
ASSERT(v4l2Codec);
ret &= v4l2Codec->setWaylandDisplay(wlDisplay);
return ret;
}
#endif //__ENABLE_WAYLAND__
#if defined(__ENABLE_X11__)
int32_t YamiV4L2_SetXDisplay(int32_t fd, Display *x11Display)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
bool ret = true;
ASSERT(v4l2Codec);
DEBUG("x11display: %p", x11Display);
ret &= v4l2Codec->setXDisplay(x11Display);
return ret;
}
#endif //__ENABLE_X11__
#if defined(__ENABLE_EGL__)
int32_t YamiV4L2_UseEglImage(int fd, /*EGLDisplay*/void* eglDisplay, /*EGLContext*/void* eglContext, unsigned int bufferIndex, void* eglImage)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
return v4l2Codec->useEglImage(eglDisplay, eglContext, bufferIndex, eglImage);
}
#endif //__ENABLE_EGL__
int32_t YamiV4L2_SetDrmFd(int32_t fd, int drm_fd)
{
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
bool ret = true;
ASSERT(v4l2Codec);
ret &= v4l2Codec->setDrmFd(drm_fd);
return ret;
}
#if defined(__ENABLE_V4L2_OPS__)
extern "C" int32_t YamiV4L2_SetParameter(int32_t fd, const char* key, const char* value);
int32_t YamiV4L2_SetParameter(int32_t fd, const char* key, const char* value)
{
int ret = -1;
if (!key || !value) {
ERROR("invalue parameter\n");
return -1;
}
V4l2CodecPtr v4l2Codec = _findCodecFromFd(fd);
ASSERT(v4l2Codec);
// FIXME, usually, it can be detected by v4l2_requestbuffers.memory
// however, chrome always set it to MMAP even for egl/texture usage
if (!strcmp(key, "frame-memory-type")) {
VideoDataMemoryType memoryType;
if (!strcmp(value, "raw-data")) {
memoryType = VIDEO_DATA_MEMORY_TYPE_RAW_COPY;
} else if (!strcmp(value, "drm-name")) {
memoryType = VIDEO_DATA_MEMORY_TYPE_DRM_NAME;
} else if (!strcmp(value, "dma-buf")) {
memoryType = VIDEO_DATA_MEMORY_TYPE_DMA_BUF;
} else if (!strcmp(value, "surface-id")) {
memoryType = VIDEO_DATA_MEMORY_TYPE_SURFACE_ID;
} else if (!strcmp(value, "android-buffer-handle")) {
memoryType = VIDEO_DATA_MEMORY_TYPE_ANDROID_BUFFER_HANDLE;
}
else if (!strcmp(value, "external-dma-buf")) {
memoryType = VIDEO_DATA_MEMORY_TYPE_EXTERNAL_DMA_BUF;
} else {
ERROR("unknow output frame memory type: %s\n", value);
return -1;
}
ret = v4l2Codec->setFrameMemoryType(memoryType);
#ifdef __ENABLE_WAYLAND__
}
else if (!strcmp(key, "wayland-display")) {
uintptr_t ptr = (uintptr_t)atoll(value);
struct wl_display* wlDisplay = (struct wl_display*)ptr;
DEBUG("wlDisplay: %p", wlDisplay);
ret = v4l2Codec->setWaylandDisplay(wlDisplay);
#endif //__ENABLE_WAYLAND__
#if defined(__ENABLE_X11__)
} else if (!strcmp(key, "x11-display")) {
uintptr_t ptr = (uintptr_t)atoll(value);
Display* x11Display = (Display*)ptr;
DEBUG("x11Display: %p", x11Display);
ret = v4l2Codec->setXDisplay(x11Display);
#endif //__ENABLE_X11__
} else if(!(strcmp(key, "encode-mode"))) {
if (!strcmp(value, "svct")) {
ret = v4l2Codec->setSvcT(true);
}
} else if (!(strcmp(key, "decode-mode"))) {
if (!strcmp(value, "thumbnail")) {
ret = v4l2Codec->setThumbnailMode(true);
}
} else {
ERROR("unsupported parameter key: %s\n", key);
}
return ret;
}
bool v4l2codecOperationInit(V4l2CodecOps *opFuncs)
{
if (!opFuncs)
return false;
int isVersionMatch = 0;
IS_V4L2CODEC_OPS_VERSION_MATCH(opFuncs->mVersion, isVersionMatch);
if (!isVersionMatch) {
ERROR("V4l2CodecOps interface version doesn't match\n");
return false;
}
ASSERT(opFuncs->mSize == sizeof(V4l2CodecOps));
memset(opFuncs->mVendorString, 0, V4L2CODEC_VENDOR_STRING_SIZE);
strncpy(opFuncs->mVendorString, "yami", V4L2CODEC_VENDOR_STRING_SIZE-1);
#define V4L2_DLSYM_OR_RETURN_ON_ERROR(name) opFuncs->m##name##Func = YamiV4L2_##name
V4L2_DLSYM_OR_RETURN_ON_ERROR(Open);
V4L2_DLSYM_OR_RETURN_ON_ERROR(Close);
V4L2_DLSYM_OR_RETURN_ON_ERROR(Ioctl);
V4L2_DLSYM_OR_RETURN_ON_ERROR(Poll);
V4L2_DLSYM_OR_RETURN_ON_ERROR(SetDevicePollInterrupt);
V4L2_DLSYM_OR_RETURN_ON_ERROR(ClearDevicePollInterrupt);
V4L2_DLSYM_OR_RETURN_ON_ERROR(Mmap);
V4L2_DLSYM_OR_RETURN_ON_ERROR(Munmap);
V4L2_DLSYM_OR_RETURN_ON_ERROR(SetParameter);
#if defined(__ENABLE_EGL__)
V4L2_DLSYM_OR_RETURN_ON_ERROR(UseEglImage);
#endif
#undef V4L2_DLSYM_OR_RETURN_ON_ERROR
return true;
}
#endif // __ENABLE_V4L2_OPS__