blob: 4917a8f5e126937908805963c0d488cae3e0b393 [file] [log] [blame]
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>
#include <poll.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <libavformat/avformat.h>
#include "aml_driver.h"
#include "demux.h"
#include "drm.h"
#include "v4l2-dec.h"
#ifdef CONFIG_SECMEM
#include "secmem.h"
#endif
#include "vp9.h"
static const char* video_dev_name = "/dev/video26";
static int video_fd;
static bool get_1st_data = false;
static int cur_output_index = -1;
static decode_finish_fn decode_finish_cb;
static bool res_evt_pending;
static bool eos_evt_pending;
static bool eos_received;
extern int g_dw_mode;
static pthread_mutex_t res_lock;
static enum v4l2_memory sInMemMode;
static uint8_t* es_buf;
//#define DEBUG_FRAME
#ifdef DEBUG_FRAME
static int frame_checksum;
#endif
#define ES_BUF_SIZE (4*1024*1024)
static pthread_t dec_thread;
bool quit_thread;
extern int config_sys_node(const char* path, const char* value);
extern int span(struct timeval* t1, struct timeval* t2);
struct frame_buffer {
struct v4l2_buffer v4lbuf;
struct v4l2_plane v4lplane[2];
uint8_t *vaddr[2];
int gem_fd[2];
bool queued;
/* output only */
uint32_t used;
struct secmem* smem;
/* capture only */
bool free_on_recycle;
struct drm_frame *drm_frame;
};
struct port_config {
enum v4l2_buf_type type;
uint32_t pixelformat;
struct v4l2_format sfmt;
int buf_num;
int plane_num;
struct frame_buffer** buf;
pthread_mutex_t lock;
pthread_cond_t wait;
};
#define OUTPUT_BUF_CNT 4
static struct port_config output_p;
static struct port_config capture_p;
static int d_o_push_num;
static int d_o_rec_num;
static int d_c_push_num;
static int d_c_rec_num;
struct profile {
bool res_change;
struct timeval last_flag;
struct timeval buffer_done;
struct timeval first_frame;
};
static struct profile res_profile;
static enum vtype v4l2_fourcc_to_vtype(uint32_t fourcc) {
switch (fourcc) {
case V4L2_PIX_FMT_H264:
return VIDEO_TYPE_H264;
case V4L2_PIX_FMT_HEVC:
return VIDEO_TYPE_H265;
case V4L2_PIX_FMT_VP9:
return VIDEO_TYPE_VP9;
case V4L2_PIX_FMT_MPEG2:
return VIDEO_TYPE_MPEG2;
}
return VIDEO_TYPE_MAX;
}
static uint32_t get_driver_min_buffers (int fd, bool capture_port)
{
struct v4l2_control control = { 0, };
if (!capture_port)
control.id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT;
else
control.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
if (!ioctl(fd, VIDIOC_G_CTRL, &control))
return control.value;
return 0;
}
static int setup_output_port(int fd)
{
int i,ret;
struct v4l2_requestbuffers req;
req.count = get_driver_min_buffers(fd, false);
if (!req.count) {
printf("fail to get min buffers for output, use default\n");
req.count = OUTPUT_BUF_CNT;
}
req.memory = sInMemMode;
req.type = output_p.type;
ret = ioctl(fd, VIDIOC_REQBUFS, &req);
if (ret < 0) {
printf("output VIDIOC_REQBUFS fail ret:%d\n", ret);
return 1;
}
output_p.buf_num = req.count;
printf("output gets %d buf\n", req.count);
output_p.buf = calloc(req.count, sizeof(struct frame_buffer *));
if (!output_p.buf) {
printf("%d oom\n", __LINE__);
return 2;
}
for (i = 0 ; i < req.count ; i++)
output_p.buf[i] = calloc(1, sizeof(struct frame_buffer));
for (i = 0; i < req.count; i++) {
int j;
struct frame_buffer* pb = output_p.buf[i];
pb->v4lbuf.index = i;
pb->v4lbuf.type = output_p.type;
pb->v4lbuf.memory = sInMemMode;
pb->v4lbuf.length = output_p.plane_num;
pb->v4lbuf.m.planes = pb->v4lplane;
ret = ioctl(fd, VIDIOC_QUERYBUF, &pb->v4lbuf);
if (ret) {
printf("VIDIOC_QUERYBUF %dth buf fail ret:%d\n", i, ret);
return 3;
}
if (sInMemMode != V4L2_MEMORY_MMAP)
continue;
for (j = 0; j < output_p.plane_num; j++) {
void *vaddr;
vaddr = mmap(NULL, pb->v4lplane[j].length,
PROT_READ | PROT_WRITE, MAP_SHARED,
fd, pb->v4lplane[j].m.mem_offset);
if (vaddr == MAP_FAILED) {
printf("%s mmap faild len:%d offset:%x\n", __func__,
pb->v4lplane[j].length, pb->v4lplane[j].m.mem_offset);
return 4;
}
pb->vaddr[j] = (uint8_t *)vaddr;
}
}
pthread_mutex_init(&output_p.lock, NULL);
pthread_cond_init(&output_p.wait, NULL);
if (sInMemMode == V4L2_MEMORY_DMABUF) {
es_buf = malloc(ES_BUF_SIZE);
if (!es_buf) {
printf("%d OOM\n", __LINE__);
return 5;
}
}
return 0;
}
static int destroy_output_port(int fd) {
int i;
struct v4l2_requestbuffers req = {
.memory = sInMemMode,
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
.count = 0,
};
pthread_mutex_destroy(&output_p.lock);
pthread_cond_destroy(&output_p.wait);
ioctl(fd, VIDIOC_REQBUFS, &req);
for (i = 0 ; i < req.count ; i++) {
struct frame_buffer *buf = output_p.buf[i];
#ifdef CONFIG_SECMEM
if (sInMemMode == V4L2_MEMORY_DMABUF)
secmem_free(buf->smem);
#endif
free(buf);
}
free(output_p.buf);
return 0;
}
static int setup_capture_port(int fd)
{
int i,ret;
uint32_t coded_w, coded_h;
struct v4l2_requestbuffers req;
/* coded size should be ready now */
memset(&capture_p.sfmt, 0, sizeof(struct v4l2_format));
capture_p.sfmt.type = capture_p.type;
ret = ioctl(video_fd, VIDIOC_G_FMT, &capture_p.sfmt);
if (ret) {
printf("%d VIDIOC_G_FMT fail :%d\n", __LINE__, ret);
return -1;
}
coded_w = capture_p.sfmt.fmt.pix_mp.width;
coded_h = capture_p.sfmt.fmt.pix_mp.height;
printf("capture port (%dx%d)\n", coded_w, coded_h);
if (g_dw_mode != VDEC_DW_AFBC_ONLY)
capture_p.sfmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
else
capture_p.sfmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
ret = ioctl(video_fd, VIDIOC_S_FMT, &capture_p.sfmt);
if (ret) {
printf("VIDIOC_S_FMT fail %d\n", ret);
return -1;
}
printf("set capture port to %s\n",
(g_dw_mode == VDEC_DW_AFBC_ONLY)?"NV12":"NV12M");
capture_p.plane_num = capture_p.sfmt.fmt.pix_mp.num_planes;
printf("number of capture plane: %d\n", capture_p.plane_num);
memset(&req, 0 , sizeof(req));
req.memory = V4L2_MEMORY_DMABUF;
req.type = capture_p.type;
req.count = get_driver_min_buffers(fd, true);
if (!req.count) {
printf("get min buffers fail\n");
return -1;
}
printf("req capture count:%d\n", req.count);
ret = ioctl(fd, VIDIOC_REQBUFS, &req);
if (ret < 0) {
printf("capture VIDIOC_REQBUFS fail ret:%d\n", ret);
return 1;
}
capture_p.buf_num = req.count;
printf("capture gets %d buf\n", req.count);
capture_p.buf = calloc(req.count, sizeof(struct frame_buffer *));
if (!capture_p.buf) {
printf("%d oom\n", __LINE__);
return 2;
}
for (i = 0 ; i < req.count ; i++) {
capture_p.buf[i] = calloc(1, sizeof(struct frame_buffer));
if (!capture_p.buf[i]) {
printf("%d oom\n", __LINE__);
return 2;
}
}
for (i = 0; i < req.count; i++) {
struct frame_buffer* pb = capture_p.buf[i];
int fd[4] = {0}, j;
pb->v4lbuf.index = i;
pb->v4lbuf.type = capture_p.type;
pb->v4lbuf.memory = V4L2_MEMORY_DMABUF;
pb->v4lbuf.length = capture_p.plane_num;
pb->v4lbuf.m.planes = pb->v4lplane;
/* allocate DRM-GEM buffers */
pb->drm_frame = display_create_buffer(coded_w, coded_h,
(g_dw_mode == VDEC_DW_AFBC_ONLY)? FRAME_FMT_AFBC:FRAME_FMT_NV12,
capture_p.plane_num);
if (ret) {
printf("display_create_buffer %dth fail\n", i);
return 2;
}
ret = ioctl(video_fd, VIDIOC_QUERYBUF, &pb->v4lbuf);
if (ret) {
printf("VIDIOC_QUERYBUF %dth buf fail ret:%d\n", i, ret);
return 3;
}
ret = display_get_buffer_fds(pb->drm_frame, fd, 2);
if (ret) {
printf("display get %dth fds fail %d\n", i, ret);
return 4;
}
/* bind GEM-fd with V4L2 buffer */
for (j = 0; j < capture_p.plane_num; j++) {
pb->vaddr[j] = NULL;
pb->gem_fd[j] = fd[j];
pb->v4lbuf.m.planes[j].m.fd = pb->gem_fd[j];
}
ret = ioctl(video_fd, VIDIOC_QBUF, &capture_p.buf[i]->v4lbuf);
if (ret) {
printf("VIDIOC_QBUF %dth buf fail ret:%d\n", i, ret);
return 5;
}
capture_p.buf[i]->queued = true;
}
gettimeofday(&res_profile.buffer_done, NULL);
return 0;
}
static int destroy_capture_port(int fd) {
int i;
struct v4l2_requestbuffers req = {
.memory = V4L2_MEMORY_DMABUF,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
.count = 0,
};
pthread_mutex_destroy(&capture_p.lock);
pthread_cond_destroy(&capture_p.wait);
ioctl(fd, VIDIOC_REQBUFS, &req);
for (i = 0 ; i < req.count ; i++) {
struct frame_buffer *buf = capture_p.buf[i];
/* release GEM buf */
buf->drm_frame->destroy(buf->drm_frame);
free(capture_p.buf[i]);
}
free(capture_p.buf);
return 0;
}
static void detect_res_change(struct v4l2_format *old, struct v4l2_format *new)
{
if (old->fmt.pix_mp.width != new->fmt.pix_mp.width ||
old->fmt.pix_mp.height != new->fmt.pix_mp.height) {
printf("%dx%d --> %dx%d\n", old->fmt.pix_mp.width,
old->fmt.pix_mp.height, new->fmt.pix_mp.width,
new->fmt.pix_mp.height);
}
}
static void handle_res_change()
{
int i, ret;
int free_cnt = 0, delay_cnt = 0;
struct v4l2_format out_fmt = { 0 };
struct v4l2_requestbuffers req = {
.memory = V4L2_MEMORY_DMABUF,
.type = capture_p.type,
.count = 0,
};
out_fmt.type = output_p.type;
ret = ioctl(video_fd, VIDIOC_G_FMT, &out_fmt);
if (ret) {
printf("output VIDIOC_G_FMT fail :%d\n",ret);
goto error;
}
detect_res_change(&output_p.sfmt, &out_fmt);
/* stop capture port */
ret = ioctl(video_fd, VIDIOC_STREAMOFF, &capture_p.type);
if (ret) {
printf("VIDIOC_STREAMOFF fail ret:%d\n",ret);
goto error;
}
pthread_mutex_lock(&res_lock);
/* return all DRM-GEM buffers */
ret = ioctl(video_fd, VIDIOC_REQBUFS, &req);
if (ret) {
printf("VIDIOC_REQBUFS fail ret:%d\n",ret);
pthread_mutex_unlock(&res_lock);
goto error;
}
for (i = 0 ; i < capture_p.buf_num; i++) {
if (capture_p.buf[i]->queued) {
struct drm_frame *drm_f = capture_p.buf[i]->drm_frame;
drm_f->destroy(drm_f);
free(capture_p.buf[i]);
free_cnt++;
} else {
capture_p.buf[i]->free_on_recycle = true;
delay_cnt++;
}
}
pthread_mutex_unlock(&res_lock);
free(capture_p.buf);
printf("f/d/total: (%d/%d/%d)\n", free_cnt, delay_cnt, capture_p.buf_num);
/* setup capture port again */
ret = setup_capture_port(video_fd);
if (ret) {
printf(" setup_capture_port fail ret:%d\n",ret);
goto error;
}
/* start capture again */
ret = ioctl(video_fd, VIDIOC_STREAMON, &capture_p.type);
if (ret) {
printf("VIDIOC_STREAMON fail ret:%d\n",ret);
goto error;
}
return;
error:
printf("debug...\n");
while (1)
sleep(1);
}
static uint32_t v4l2_get_event(int fd)
{
int ret;
struct v4l2_event evt = { 0 };
ret = ioctl(fd, VIDIOC_DQEVENT, &evt);
if (ret) {
printf("VIDIOC_DQEVENT fail, ret:%d\n", ret);
return 0;
}
return evt.type;
}
static void *dec_thread_func(void * arg) {
int ret = 0;
struct pollfd pfd = {
/* default blocking capture */
.events = POLLIN | POLLRDNORM | POLLPRI | POLLOUT | POLLWRNORM,
.fd = video_fd,
};
while (!quit_thread) {
struct v4l2_buffer buf;
struct v4l2_plane planes[2];
for (;;) {
ret = poll(&pfd, 1, 10);
if (ret > 0)
break;
if (quit_thread)
goto exit;
if (errno == EINTR)
continue;
}
/* error handling */
if (pfd.revents & POLLERR)
printf("POLLERR received\n");
/* res change */
if (pfd.revents & POLLPRI) {
uint32_t evt;
evt = v4l2_get_event(video_fd);
if (evt == V4L2_EVENT_SOURCE_CHANGE)
res_evt_pending = true;
else if (evt == V4L2_EVENT_EOS)
eos_evt_pending = true;
printf("res_evt_pending:%d eos_evt_pending:%d\n",
res_evt_pending, eos_evt_pending);
}
/* dqueue output buf */
if (pfd.revents & (POLLOUT | POLLWRNORM)) {
memset(&buf, 0, sizeof(buf));
memset(planes, 0, sizeof(planes));
buf.memory = sInMemMode;
buf.type = output_p.type;
buf.length = 2;
buf.m.planes = planes;
ret = ioctl(video_fd, VIDIOC_DQBUF, &buf);
if (ret) {
printf("output VIDIOC_DQBUF fail %d\n", ret);
} else {
struct frame_buffer *fb = output_p.buf[buf.index];
#ifdef DEBUG_FRAME
printf("dqueue output %d\n", buf.index);
#endif
pthread_mutex_lock(&output_p.lock);
fb->queued = false;
pthread_mutex_unlock(&output_p.lock);
fb->used = 0;
d_o_rec_num++;
#ifdef CONFIG_SECMEM
if (sInMemMode == V4L2_MEMORY_DMABUF) {
secmem_free(fb->smem);
}
#endif
pthread_cond_signal(&output_p.wait);
}
}
/* dqueue capture buf */
if (pfd.revents & (POLLIN | POLLRDNORM)) {
memset(&buf, 0, sizeof(buf));
memset(planes, 0, sizeof(planes));
buf.memory = V4L2_MEMORY_DMABUF;
buf.type = capture_p.type;
buf.length = 2;
buf.m.planes = planes;
ret = ioctl(video_fd, VIDIOC_DQBUF, &buf);
if (ret) {
printf("capture VIDIOC_DQBUF fail %d\n", ret);
} else {
#ifdef DEBUG_FRAME
printf("dqueue cap %d\n", buf.index);
#endif
capture_p.buf[buf.index]->queued = false;
d_c_push_num++;
/* ignore res change for 1st frame */
if (d_c_push_num == 1 && res_evt_pending) {
printf("ignore res change for 1st frame\n");
res_evt_pending = false;
}
if (!(buf.flags & V4L2_BUF_FLAG_LAST)) {
struct drm_frame *drm_f = capture_p.buf[buf.index]->drm_frame;
uint64_t pts_us;
AVRational us_r = {1,DMX_SECOND};
AVRational ninty_k_r = {1,90000};
/* display capture buf */
if (res_profile.res_change) {
res_profile.res_change = false;
gettimeofday(&res_profile.first_frame, NULL);
printf("--event to buffer done %dms--\n",
span(&res_profile.last_flag, &res_profile.buffer_done));
printf("--event to 1st frame %dms--\n",
span(&res_profile.last_flag, &res_profile.first_frame));
}
drm_f->pri_dec = capture_p.buf[buf.index];
pts_us = buf.timestamp.tv_sec * DMX_SECOND;
pts_us += buf.timestamp.tv_usec;
drm_f->pts = av_rescale_q(pts_us, us_r, ninty_k_r);
drm_f->last_flag = false;
display_engine_show(drm_f);
} else {
uint32_t evt = 0;
if (!res_evt_pending && !eos_evt_pending)
evt = v4l2_get_event(video_fd);
if (evt == V4L2_EVENT_SOURCE_CHANGE || res_evt_pending) {
printf("res changed\n");
res_evt_pending = false;
gettimeofday(&res_profile.last_flag, NULL);
res_profile.res_change = true;
/* this buffer will be freed in handle_res_change */
capture_p.buf[buf.index]->queued = true;
handle_res_change();
d_c_rec_num++;
continue;
} else if (evt == V4L2_EVENT_EOS || eos_evt_pending) {
struct drm_frame *drm_f = capture_p.buf[buf.index]->drm_frame;
/* flush DRM */
drm_f->last_flag = true;
display_engine_show(drm_f);
printf("EOS received\n");
eos_received = true;
eos_evt_pending = false;
display_wait_for_display();
d_c_rec_num++;
dump_v4l2_decode_state();
decode_finish_cb();
break;
}
}
}
}
}
exit:
/* stop output port */
ret = ioctl(video_fd, VIDIOC_STREAMOFF, &output_p.type);
if (ret) {
printf("VIDIOC_STREAMOFF fail ret:%d\n",ret);
}
/* stop capture port */
ret = ioctl(video_fd, VIDIOC_STREAMOFF, &capture_p.type);
if (ret) {
printf("VIDIOC_STREAMOFF fail ret:%d\n",ret);
}
return NULL;
}
static int config_decoder(int fd, enum vtype type)
{
int ret = -1;
struct v4l2_streamparm para;
struct v4l2_event_subscription sub;
struct aml_dec_params *dec_p = (struct aml_dec_params *)para.parm.raw_data;
memset(&para, 0 , sizeof(para));
para.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
dec_p->parms_status = V4L2_CONFIG_PARM_DECODE_CFGINFO;
dec_p->cfg.double_write_mode = g_dw_mode;
/* number of extra buffer for display pipelien to run */
/* MPEG will use hardcoded size in driver */
if (type != VIDEO_TYPE_MPEG2)
dec_p->cfg.ref_buf_margin = 7;
//optional adjust dec_p->hdr for hdr support
if ((ret = ioctl(video_fd, VIDIOC_S_PARM, &para))) {
printf("VIDIOC_S_PARM fails ret:%d\n", ret);
return ret;
}
/* subscribe to resolution change and EOS event */
memset(&sub, 0, sizeof(sub));
sub.type = V4L2_EVENT_SOURCE_CHANGE;
ret = ioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
if (ret) {
printf("subscribe V4L2_EVENT_SOURCE_CHANGE fail\n");
return ret;
}
memset(&sub, 0, sizeof(sub));
sub.type = V4L2_EVENT_EOS;
ret = ioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
if (ret) {
printf("subscribe V4L2_EVENT_EOS fail\n");
return ret;
}
return 0;
}
#define V4L2_CID_USER_AMLOGIC_BASE (V4L2_CID_USER_BASE + 0x1100)
#define AML_V4L2_SET_DRMMODE (V4L2_CID_USER_AMLOGIC_BASE + 0)
static int v4l2_dec_set_drmmode(bool drm_enable)
{
int rc = 0;
struct v4l2_queryctrl queryctrl;
struct v4l2_control control;
memset (&queryctrl, 0, sizeof (queryctrl));
queryctrl.id = AML_V4L2_SET_DRMMODE;
if (-1 == ioctl (video_fd, VIDIOC_QUERYCTRL, &queryctrl)) {
printf ("AML_V4L2_SET_DRMMODE is not supported\n");
} else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
printf ("AML_V4L2_SET_DRMMODE is disable\n");
} else {
memset (&control, 0, sizeof (control));
control.id = AML_V4L2_SET_DRMMODE;
control.value = drm_enable;
rc = ioctl (video_fd, VIDIOC_S_CTRL, &control);
if (rc) {
printf ("AML_V4L2_SET_DRMMODE fail\n");
}
}
return rc;
}
int v4l2_dec_init(enum vtype type, int secure, decode_finish_fn cb)
{
int ret = -1;
struct v4l2_capability cap;
struct v4l2_fmtdesc fdesc;
bool supported;
if (!cb)
return 1;
decode_finish_cb = cb;
if (secure) {
#ifdef CONFIG_SECMEM
sInMemMode = V4L2_MEMORY_DMABUF;
ret = secmem_init();
if (ret) {
printf("secmem_init fail %d\n",ret);
return 1;
}
#endif
} else
sInMemMode = V4L2_MEMORY_MMAP;
/* check decoder mode */
if ((type == VIDEO_TYPE_H264 ||
type == VIDEO_TYPE_MPEG2) &&
g_dw_mode != 16) {
printf("mpeg2/h264 only support DW 16 mode\n");
goto error;
}
/* config sys nodes */
if (config_sys_node("/sys/module/amvdec_ports/parameters/multiplanar", "1")) {
printf("set multiplanar fails\n");
goto error;
}
if (type == VIDEO_TYPE_VP9 && !secure) {
if (config_sys_node("/sys/module/amvdec_ports/parameters/vp9_need_prefix", "1")) {
printf("set vp9_need_prefix fails\n");
goto error;
}
}
if (type == VIDEO_TYPE_MPEG2) {
//TODO: delete after ucode updated for mpeg2
if (config_sys_node("/sys/module/amvdec_ports/parameters/param_sets_from_ucode", "0")) {
printf("set param_sets_from_ucode 0 fails\n");
goto error;
}
}
pthread_mutex_init(&res_lock, NULL);
video_fd = open(video_dev_name, O_RDWR | O_NONBLOCK, 0);
if (video_fd < 0) {
printf("Unable to open video node: %s\n",
strerror(errno));
goto error;
}
if (type != VIDEO_TYPE_MPEG2) {
ret = v4l2_dec_set_drmmode(secure);
if (ret) {
printf("aml_v4l2_dec_set_drmmode fail\n");
goto error;
}
}
if ((ret = ioctl(video_fd, VIDIOC_QUERYCAP, &cap))) {
printf("VIDIOC_QUERYCAP fails ret:%d\n", ret);
goto error;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)) {
printf("V4L2_CAP_VIDEO_M2M_MPLANE fails\n");
goto error;
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
printf("V4L2_CAP_STREAMING fails\n");
goto error;
}
/* output fmt */
output_p.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
supported = false;
memset(&fdesc, 0, sizeof(fdesc));
fdesc.type = output_p.type;
for (;;) {
ret = ioctl(video_fd, VIDIOC_ENUM_FMT, &fdesc);
if (ret)
break;
if (v4l2_fourcc_to_vtype(fdesc.pixelformat) == type) {
supported = true;
output_p.pixelformat = fdesc.pixelformat;
break;
}
fdesc.index++;
}
if (!supported) {
printf("output format not supported:%d\n", type);
goto error;
}
if (config_decoder(video_fd, type)) {
printf("config_decoder error\n");
goto error;
}
memset(&output_p.sfmt, 0, sizeof(struct v4l2_format));
output_p.sfmt.type = output_p.type;
ret = ioctl(video_fd, VIDIOC_G_FMT, &output_p.sfmt);
if (ret) {
printf("VIDIOC_G_FMT fail\n");
goto error;
}
output_p.sfmt.fmt.pix_mp.pixelformat = output_p.pixelformat;
/* 4K frame should fit into 2M */
output_p.sfmt.fmt.pix_mp.plane_fmt[0].sizeimage = ES_BUF_SIZE;
ret = ioctl(video_fd, VIDIOC_S_FMT, &output_p.sfmt);
if (ret) {
printf("VIDIOC_S_FMT 0x%x fail\n", output_p.pixelformat);
goto error;
}
output_p.plane_num = output_p.sfmt.fmt.pix_mp.num_planes;
/* capture fmt */
capture_p.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
supported = false;
memset(&fdesc, 0, sizeof(fdesc));
fdesc.type = capture_p.type;
for (;;) {
ret = ioctl(video_fd, VIDIOC_ENUM_FMT, &fdesc);
if (ret)
break;
if ((fdesc.pixelformat == V4L2_PIX_FMT_NV12M &&
g_dw_mode != VDEC_DW_AFBC_ONLY) ||
(fdesc.pixelformat == V4L2_PIX_FMT_NV12 &&
g_dw_mode == VDEC_DW_AFBC_ONLY)) {
supported = true;
capture_p.pixelformat = fdesc.pixelformat;
break;
}
fdesc.index++;
}
if (!supported) {
printf("capture format not supported\n");
goto error;
}
/* setup output port */
ret = setup_output_port(video_fd);
if (ret) {
printf("setup_output_port fail\n");
goto error;
}
/* start output port */
ret = ioctl(video_fd, VIDIOC_STREAMON, &output_p.type);
if (ret) {
printf("VIDIOC_STREAMON fail ret:%d\n",ret);
goto error;
}
printf("output stream on\n");
return 0;
error:
close(video_fd);
video_fd = 0;
return ret;
}
static int handle_first_frame()
{
int ret;
/* Wait for 1st video ES data and setup capture port */
ret = setup_capture_port(video_fd);
if (ret) {
printf(" setup_capture_port fail ret:%d\n",ret);
goto error;
}
pthread_mutex_init(&capture_p.lock, NULL);
pthread_cond_init(&capture_p.wait, NULL);
/* start capture port */
ret = ioctl(video_fd, VIDIOC_STREAMON, &capture_p.type);
if (ret) {
printf("VIDIOC_STREAMON fail ret:%d\n",ret);
goto error;
}
printf("capture stream on\n");
if (pthread_create(&dec_thread, NULL, dec_thread_func, NULL)) {
printf("dec thread fail\n");
return -1;
}
return 0;
error:
printf("handle_first_frame fatal\n");
while (1) sleep(1);
exit(1);
}
int v4l2_dec_destroy()
{
quit_thread = true;
pthread_cond_signal(&output_p.wait);
pthread_join(dec_thread, NULL);
destroy_output_port(video_fd);
destroy_capture_port(video_fd);
get_1st_data = false;
pthread_mutex_destroy(&res_lock);
close(video_fd);
#ifdef CONFIG_SECMEM
secmem_destroy();
#endif
if (es_buf)
free(es_buf);
return 0;
}
static int get_free_output_buf()
{
int i;
pthread_mutex_lock(&output_p.lock);
do {
for (i = 0; i < output_p.buf_num; i++) {
if (!output_p.buf[i]->queued) {
break;
}
}
if (i == output_p.buf_num) {
pthread_cond_wait(&output_p.wait, &output_p.lock);
if (quit_thread)
break;
} else {
break;
}
} while (1);
pthread_mutex_unlock(&output_p.lock);
if (i == output_p.buf_num)
return -1;
return i;
}
#if 0
static void dump(const char* path, const uint8_t *data, int size) {
FILE* fd = fopen(path, "ab");
if (!fd)
return;
fwrite(data, 1, size, fd);
fclose(fd);
}
#endif
int v4l2_dec_write_es(const uint8_t *data, int size)
{
struct frame_buffer *p;
if (cur_output_index == -1)
cur_output_index = get_free_output_buf();
if (cur_output_index < 0) {
printf("%s %d can not get output buf\n", __func__, __LINE__);
return 0;
}
p = output_p.buf[cur_output_index];
if ((sInMemMode == V4L2_MEMORY_MMAP &&
(p->used + size) > p->v4lplane[0].length) ||
(sInMemMode == V4L2_MEMORY_DMABUF &&
(p->used + size) > ES_BUF_SIZE)) {
printf("fatal frame too big %d\n",
size + p->used);
return 0;
}
if (sInMemMode == V4L2_MEMORY_MMAP)
memcpy(p->vaddr[0] + p->used, data, size);
else
memcpy(es_buf + p->used, data, size);
p->used += size;
#ifdef DEBUG_FRAME
int i;
for (i = 0 ; i < size ; i++)
frame_checksum += data[i];
#endif
//dump("/data/es.h265", data, size);
//
return size;
}
int v4l2_dec_frame_done(int64_t pts)
{
int ret;
struct frame_buffer *p;
if (quit_thread)
return 0;
if (cur_output_index < 0 || cur_output_index >= output_p.buf_num) {
printf("BUG %s %d idx:%d\n", __func__, __LINE__, cur_output_index);
return 0;
}
p = output_p.buf[cur_output_index];
#ifdef CONFIG_SECMEM
if (sInMemMode == V4L2_MEMORY_DMABUF) {
int frame_size = p->used;
int offset = 0;
struct vp9_superframe_split s;
if (output_p.pixelformat == V4L2_PIX_FMT_VP9) {
s.data = es_buf;
s.data_size = p->used;
ret = vp9_superframe_split_filter(&s);
if (ret) {
printf("parse vp9 superframe fail\n");
return 0;
}
frame_size = s.size + s.nb_frames * 16;
}
p->smem = secmem_alloc(frame_size);
if (!p->smem) {
printf("%s %d oom:%d\n", __func__, __LINE__, p->used);
return 0;
}
/* copy to secmem */
if (output_p.pixelformat == V4L2_PIX_FMT_VP9) {
int stream_offset = s.size;
offset = frame_size;
for (int i = s.nb_frames; i > 0; i--) {
uint8_t header[16];
uint32_t sub_size = s.sizes[i - 1];
/* frame body */
offset -= sub_size;
stream_offset -= sub_size;
secmem_fill(p->smem, es_buf + stream_offset, offset, sub_size);
/* prepend header */
offset -= 16;
fill_vp9_header(header, sub_size);
secmem_fill(p->smem, header, offset, 16);
}
} else
secmem_fill(p->smem, es_buf, offset, p->used);
p->v4lbuf.m.planes[0].m.fd = p->smem->fd;
p->v4lbuf.m.planes[0].length = frame_size;
p->v4lbuf.m.planes[0].data_offset = 0;
}
#endif
p->v4lbuf.m.planes[0].bytesused = p->used;
/* convert from ns to timeval */
p->v4lbuf.timestamp.tv_sec = pts / DMX_SECOND;
p->v4lbuf.timestamp.tv_usec = pts % DMX_SECOND;
pthread_mutex_lock(&output_p.lock);
p->queued = true;
pthread_mutex_unlock(&output_p.lock);
ret = ioctl(video_fd, VIDIOC_QBUF, &p->v4lbuf);
if (ret) {
printf("write es VIDIOC_QBUF %dth buf fail ret:%d\n",
cur_output_index, ret);
return 0;
}
#ifdef DEBUG_FRAME
printf("%s queue output %d frame_checksum:%x\n", __func__, cur_output_index, frame_checksum);
frame_checksum = 0;
#endif
d_o_push_num++;
cur_output_index = -1;
/* can setup capture port now */
if (!get_1st_data) {
printf("%s 1st frame done\n", __func__);
get_1st_data = true;
handle_first_frame();
} else {
}
return ret;
}
int capture_buffer_recycle(void* handle)
{
int ret = 0;
struct frame_buffer *frame = handle;
d_c_rec_num++;
pthread_mutex_lock(&res_lock);
if (frame->free_on_recycle) {
printf("free index:%d\n", frame->v4lbuf.index);
frame->drm_frame->destroy(frame->drm_frame);
free(frame);
goto exit;
}
if (eos_received)
goto exit;
ret = ioctl(video_fd, VIDIOC_QBUF, &frame->v4lbuf);
if (ret) {
printf("VIDIOC_QBUF %dth buf fail ret:%d\n", frame->v4lbuf.index, ret);
goto exit;
} else {
#ifdef DEBUG_FRAME
printf("queue cap %d\n", frame->v4lbuf.index);
#endif
frame->queued = true;
}
exit:
pthread_mutex_unlock(&res_lock);
return ret;
}
int v4l2_dec_eos()
{
int ret;
struct v4l2_decoder_cmd cmd = {
.cmd = V4L2_DEC_CMD_STOP,
.flags = 0,
};
printf("%s EOS received\n", __func__);
/* flush decoder */
ret = ioctl(video_fd, VIDIOC_DECODER_CMD, &cmd);
if (ret)
printf("V4L2_DEC_CMD_STOP output fail ret:%d\n",ret);
return ret;
}
void dump_v4l2_decode_state()
{
printf("-----------------------\n");
printf("output port: push(%d) pop(%d)\n", d_o_push_num, d_o_rec_num);
printf("capture port: push(%d) pop(%d)\n", d_c_push_num, d_c_rec_num);
printf("-----------------------\n");
}