Support SOURCE_CHANGE event
authorJeongmo Yang <jm80.yang@samsung.com>
Thu, 13 Feb 2025 02:49:11 +0000 (11:49 +0900)
committerJeongmo Yang <jm80.yang@samsung.com>
Thu, 13 Feb 2025 02:49:11 +0000 (11:49 +0900)
Signed-off-by: Jeongmo Yang <jm80.yang@samsung.com>
src/hal_backend_codec_v4l2.c

index 67d284ffa634d6c7bf92c18f46f59f9365606b61..128518deed9f423273654bce11b23d66790f21a1 100644 (file)
@@ -499,6 +499,9 @@ static void __codec_v4l2_message_send(codec_hal_handle_s *handle, hal_codec_mess
                        message, message->buffer->index, message->buffer);
                break;
        case HAL_CODEC_MESSAGE_TYPE_RESOLUTION_CHANGED:
+               message->resolution.width = ((hal_codec_resolution_s *)value)->width;
+               message->resolution.height = ((hal_codec_resolution_s *)value)->height;
+               LOGI("ResolutionChanged[%dx%d]", message->resolution.width, message->resolution.height);
                break;
        case HAL_CODEC_MESSAGE_TYPE_ERROR:
                message->error_code = GPOINTER_TO_INT(value);
@@ -561,6 +564,21 @@ static int __codec_v4l2_s_ctrl(int device_fd, int cid, int value)
 }
 
 
+static int __codec_v4l2_subscribe_event(int device_fd, uint32_t event, uint32_t input_id)
+{
+       struct v4l2_event_subscription subscription = {.type = event, .id = input_id, };
+
+       LOGI("subscribe event[%u], input_id[%u]", subscription.type, subscription.id);
+
+       if (ioctl(device_fd, VIDIOC_SUBSCRIBE_EVENT, &subscription) < 0) {
+               LOGW("failed[%s]", __codec_v4l2_get_error_string(errno));
+               return HAL_CODEC_ERROR_INTERNAL;
+       }
+
+       return HAL_CODEC_ERROR_NONE;
+}
+
+
 static int __codec_v4l2_set_config(codec_hal_buffer_control_s *buffer_control,
        enum v4l2_buf_type buf_type, enum v4l2_memory memory,
        hal_codec_format_e format, int width, int height, int max_width, int max_height)
@@ -1038,19 +1056,16 @@ static int __codec_v4l2_stop_stream(codec_hal_handle_s *handle)
 
        LOGI("start");
 
-       /* stream off */
-       for (i = 0 ; i < CODEC_HAL_BUFFER_CONTROL_TYPE_MAX ; i++) {
-               ret = __codec_v4l2_stream(handle->device_fd,
-                       handle->buffer_control[i].config.buf_type, FALSE);
-               if (ret != HAL_CODEC_ERROR_NONE) {
-                       LOGE("buffer_control[%d] STREAMOFF failed[0x%x]", i, ret);
-                       return ret;
-               }
-       }
-
-       /* wait for output buffer */
+       /* output */
        buffer_control = &handle->buffer_control[CODEC_HAL_BUFFER_CONTROL_TYPE_OUTPUT];
 
+       ret = __codec_v4l2_stream(handle->device_fd,
+               buffer_control->config.buf_type, FALSE);
+       if (ret != HAL_CODEC_ERROR_NONE) {
+               LOGE("[OUTPUT] STREAMOFF failed[0x%x]", ret);
+               return ret;
+       }
+
        g_mutex_lock(&buffer_control->lock);
 
        while (buffer_control->buffers.queued_count < buffer_control->buffers.count) {
@@ -1071,7 +1086,6 @@ static int __codec_v4l2_stop_stream(codec_hal_handle_s *handle)
        g_mutex_unlock(&buffer_control->lock);
 
        /* release output buffer */
-       buffer_control = &handle->buffer_control[CODEC_HAL_BUFFER_CONTROL_TYPE_OUTPUT];
        buffers = &buffer_control->buffers;
 
        for (i = 0 ; i < buffers->count ; i++) {
@@ -1100,10 +1114,17 @@ static int __codec_v4l2_stop_stream(codec_hal_handle_s *handle)
                return ret;
        }
 
-       /* release input buffer */
+       /* input */
        buffer_control = &handle->buffer_control[CODEC_HAL_BUFFER_CONTROL_TYPE_INPUT];
        buffers = &buffer_control->buffers;
 
+       ret = __codec_v4l2_stream(handle->device_fd,
+               buffer_control->config.buf_type, FALSE);
+       if (ret != HAL_CODEC_ERROR_NONE) {
+               LOGE("[INPUT] STREAMOFF failed[0x%x]", ret);
+               return ret;
+       }
+
        for (i = 0 ; i < buffers->count ; i++) {
                codec_buffer = &buffers->buffer[i];
 
@@ -1662,6 +1683,7 @@ int codec_v4l2_configure(void *codec_handle, int width, int height,
        int i = 0;
        int ret = 0;
        int device_fd = CODEC_HAL_INITIAL_FD;
+       uint32_t input_id = 0;
        hal_codec_resolution_s in_max_resolution = {0, 0};
        hal_codec_resolution_s out_max_resolution = {0, 0};
        char *node_path = NULL;
@@ -1728,6 +1750,15 @@ int codec_v4l2_configure(void *codec_handle, int width, int height,
                return HAL_CODEC_ERROR_DEVICE_OPEN;
        }
 
+       /* subscribe SOURCE_CHANGE event */
+       if (ioctl(device_fd, VIDIOC_G_INPUT, &input_id) < 0)
+               LOGW("G_INPUT failed, use default input id[%d]", input_id);
+
+       if (__codec_v4l2_subscribe_event(device_fd, V4L2_EVENT_SOURCE_CHANGE, input_id) != HAL_CODEC_ERROR_NONE) {
+               LOGW("subscribe event[SOURCE_CHANGE] failed");
+               return HAL_CODEC_ERROR_DEVICE_READ;
+       }
+
        /* set format for input */
        ret = __codec_v4l2_set_config(&handle->buffer_control[CODEC_HAL_BUFFER_CONTROL_TYPE_INPUT],
                in_buf_type, V4L2_MEMORY_MMAP, in_format, width, height, in_max_resolution.width, in_max_resolution.height);
@@ -1833,6 +1864,119 @@ static int __codec_v4l2_poll(codec_hal_handle_s *handle, short events, int timeo
 }
 
 
+static void __codec_v4l2_restart_output_stream(codec_hal_handle_s *handle)
+{
+       int i = 0;
+       int k = 0;
+       int ret = 0;
+       int new_width = 0;
+       int new_height = 0;
+       gint64 end_time = 0;
+       struct v4l2_format v4l2_fmt;
+       hal_codec_memory_s *codec_memory = NULL;
+       hal_codec_buffer_s *codec_buffer = NULL;
+       codec_hal_buffer_control_s *buffer_control = NULL;
+       codec_hal_buffers_s *buffers = NULL;
+
+       if (!handle) {
+               LOGE("NULL handle");
+               return;
+       }
+
+       LOGI("start");
+
+       buffer_control = &handle->buffer_control[CODEC_HAL_BUFFER_CONTROL_TYPE_OUTPUT];
+
+       ret = __codec_v4l2_stream(handle->device_fd,
+               buffer_control->config.buf_type, FALSE);
+       if (ret != HAL_CODEC_ERROR_NONE) {
+               LOGE("[OUTPUT] STREAMOFF failed[0x%x]", ret);
+               return;
+       }
+
+       g_mutex_lock(&buffer_control->lock);
+
+       while (buffer_control->buffers.queued_count < buffer_control->buffers.count) {
+               LOGW("wait for output buffer[queued:%d vs count:%u]",
+                       buffer_control->buffers.queued_count, buffer_control->buffers.count);
+
+               end_time = g_get_monotonic_time() + G_TIME_SPAN_SECOND;
+
+               if (!g_cond_wait_until(&buffer_control->cond, &buffer_control->lock, end_time)) {
+                       LOGE("timeout: wait for output buffer");
+                       break;
+               }
+
+               LOGW("signal received, check it again[queued:%d,count:%u]",
+                       buffer_control->buffers.queued_count, buffer_control->buffers.count);
+       }
+
+       g_mutex_unlock(&buffer_control->lock);
+
+       /* release output buffer */
+       buffers = &buffer_control->buffers;
+
+       for (i = 0 ; i < buffers->count ; i++) {
+               codec_buffer = &buffers->buffer[i];
+               codec_memory = &codec_buffer->memory;
+
+               for (k = 0 ; k < codec_memory->num_fd ; k++) {
+                       if (codec_memory->fd[k] > CODEC_HAL_INITIAL_FD) {
+                               LOGI("[OUTPUT][%d][%d] close fd[%d]", i, k, codec_memory->fd[k]);
+                               close(codec_memory->fd[k]);
+                               codec_memory->fd[k] = CODEC_HAL_INITIAL_FD;
+                       }
+
+                       if (buffers->bo[i][k]) {
+                               LOGI("[OUTPUT][%d][%d] unref bo[%p]", i, k, buffers->bo[i][k]);
+                               tbm_bo_unref(buffers->bo[i][k]);
+                               buffers->bo[i][k] = NULL;
+                       }
+               }
+       }
+
+       ret = __codec_v4l2_reqbufs(handle->device_fd,
+               buffer_control->config.buf_type, V4L2_MEMORY_MMAP, 0, NULL);
+       if (ret != HAL_CODEC_ERROR_NONE) {
+               LOGE("[OUTPUT] REQBUFS 0 failed[0x%x]", ret);
+               return;
+       }
+
+       memset(&v4l2_fmt, 0x0, sizeof(struct v4l2_format));
+
+       v4l2_fmt.type = buffer_control->config.buf_type;
+
+       if (ioctl(handle->device_fd, VIDIOC_G_FMT, &v4l2_fmt) < 0) {
+               LOGE("G_FMT failed");
+               return;
+       }
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(buffer_control->config.buf_type)) {
+               new_width = v4l2_fmt.fmt.pix_mp.width;
+               new_height = v4l2_fmt.fmt.pix_mp.height;
+       } else {
+               new_width = v4l2_fmt.fmt.pix.width;
+               new_height = v4l2_fmt.fmt.pix.height;
+       }
+
+       LOGI("new resolution[%dx%d]", new_width, new_height);
+
+       buffer_control->config.resolution.width = new_width;
+       buffer_control->config.resolution.height = new_height;
+
+       ret = __codec_v4l2_start_stream(handle, buffer_control, BUFFER_NUM_OUTPUT);
+       if (ret != HAL_CODEC_ERROR_NONE) {
+               LOGE("[output] __codec_start_stream failed[0x%x]", ret);
+               return;
+       }
+
+       __codec_v4l2_message_send(handle, HAL_CODEC_MESSAGE_TYPE_RESOLUTION_CHANGED,
+               &buffer_control->config.resolution);
+
+       LOGI("done");
+}
+
+
 static gpointer __codec_v4l2_buffer_handler_func(gpointer data)
 {
        int ret = 0;
@@ -1871,6 +2015,21 @@ static gpointer __codec_v4l2_buffer_handler_func(gpointer data)
                if (ret < 0)
                        break;
 
+               if (revents & POLLPRI) {
+                       struct v4l2_event event = { 0, };
+
+                       if (ioctl(handle->device_fd, VIDIOC_DQEVENT, &event) == 0) {
+                               if (event.type == V4L2_EVENT_SOURCE_CHANGE) {
+                                       LOGW("SOURCE_CHANGE event detected, restart stream with new resolution");
+                                       __codec_v4l2_restart_output_stream(handle);
+                               } else {
+                                       LOGW("Unknown event[%u]", event.type);
+                               }
+                       } else {
+                               LOGI("VIDIOC_DQEVENT failed");
+                       }
+               }
+
                if (revents & POLLERR) {
                        LOGE("POLLERR[errno:%d,%s]", poll_errno, __codec_v4l2_get_error_string(poll_errno));
 
@@ -2170,7 +2329,6 @@ int codec_v4l2_decode(void *codec_handle, hal_codec_buffer_s *buffer)
        ret = __codec_v4l2_qbuf(handle->device_fd,
                buffer_control, idle_buffer);
 
-
        if (ret != HAL_CODEC_ERROR_NONE) {
                LOGE("QBUF[%p] failed[i:%d], 0x%x",
                        buffer, idle_buffer->index, ret);