fps_list->count = ival.index;
}
+static int __vision_source_v4l2_qbuf(int device_fd, int type, int memory,
+ int index)
+{
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return VISION_SOURCE_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.index = index;
+ v4l2_buf.type = type;
+ v4l2_buf.memory = memory;
+ v4l2_buf.m.planes = v4l2_planes;
+
+ if (v4l2_ioctl(device_fd, VIDIOC_QBUF, &v4l2_buf) < 0) {
+ LOGE("qbuf failed. [i: %d, t: %d, m: %d] errno %d", index, type,
+ memory, errno);
+ return VISION_SOURCE_ERROR_INTERNAL;
+ }
+
+ /*LOGD("QBUF done [i: %d, t: %d, m: %d]", index, type, memory);*/
+
+ return VISION_SOURCE_ERROR_NONE;
+}
+
static int
__vision_source_get_device_info(int device_index, int device_fd,
vision_source_device_info_s *device_info,
_GET_DEVICE_INFO_LIST_DONE:
LOGD("ret 0x%x", ret);
+ globfree(&glob_buf);
if (ret != VISION_SOURCE_ERROR_NONE)
g_free(device_info_list);
return VISION_SOURCE_ERROR_NONE;
}
+static int __vision_source_v4l2_dqbuf(int device_fd, int type, int memory,
+ int *index, unsigned int *used_size)
+{
+ int ret = VISION_SOURCE_ERROR_NONE;
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return VISION_SOURCE_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!index) {
+ LOGE("NULL parameter");
+ return VISION_SOURCE_ERROR_INVALID_PARAMETER;
+ }
+
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.type = type;
+ v4l2_buf.memory = memory;
+ v4l2_buf.m.planes = v4l2_planes;
+
+ ret = v4l2_ioctl(device_fd, VIDIOC_DQBUF, &v4l2_buf);
+ if (ret < 0) {
+ if (errno != EIO) {
+ LOGE("dqbuf failed. [t: %d, m: %d] errno %d", type, memory, errno);
+ return VISION_SOURCE_ERROR_INTERNAL;
+ }
+ }
+
+ *index = v4l2_buf.index;
+ *used_size = v4l2_buf.bytesused;
+
+ return VISION_SOURCE_ERROR_NONE;
+}
+
+static int __vision_source_v4l2_wait_frame(int device_fd, int wait_time)
+{
+ int ret = VISION_SOURCE_ERROR_NONE;
+ fd_set fds;
+ struct timeval timeout;
+
+ if (device_fd < 0) {
+ LOGE("invalid fd %d", device_fd);
+ return VISION_SOURCE_ERROR_INVALID_PARAMETER;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(device_fd, &fds);
+
+ memset(&timeout, 0x0, sizeof(struct timeval));
+
+ timeout.tv_sec = wait_time;
+ timeout.tv_usec = 0;
+
+ /*LOGD("select : %d sec", wait_time);*/
+
+ ret = select(device_fd + 1, &fds, NULL, NULL, &timeout);
+ if (ret == -1) {
+ if (EINTR == errno) {
+ LOGD("select error : EINTR");
+ return VISION_SOURCE_ERROR_NONE;
+ }
+ LOGE("select failed. errno %d", errno);
+ return VISION_SOURCE_ERROR_INTERNAL;
+ }
+
+ if (ret == 0) {
+ LOGE("select timeout.");
+ return VISION_SOURCE_ERROR_INTERNAL;
+ }
+
+ /*LOGD("select done");*/
+
+ return VISION_SOURCE_ERROR_NONE;
+}
+
+static int
+__vision_source_start_stream(vision_source_v4l2_s *handle,
+ vision_source_pixel_format_e pixel_format,
+ vision_source_resolution_s *resolution,
+ uint32_t fps, uint32_t request_buffer_count)
+{
+ vision_source_buffer_s *buffer = NULL;
+ struct v4l2_buffer v4l2_buf;
+ struct v4l2_plane v4l2_planes[V4L2_PLANES_MAX];
+ guint32 plane_num = 0;
+
+ int ret = __vision_source_set_stream(handle, pixel_format, resolution, fps,
+ request_buffer_count);
+ if (ret != VISION_SOURCE_ERROR_NONE) {
+ return ret;
+ }
+
+ /* request buffer */
+ ret = __vision_source_v4l2_reqbufs(handle->device_fd, handle->buffer_type,
+ V4L2_MEMORY_MMAP, request_buffer_count,
+ &handle->buffer_count);
+ if (ret != VISION_SOURCE_ERROR_NONE) {
+ return ret;
+ }
+
+ LOGD("buffer count : request %d -> result %d", request_buffer_count,
+ handle->buffer_count);
+
+ /* query buffer, mmap and qbuf */
+ for (int i = 0; i < handle->buffer_count; i++) {
+ memset(&v4l2_buf, 0x0, sizeof(struct v4l2_buffer));
+ memset(v4l2_planes, 0x0, sizeof(v4l2_planes));
+
+ v4l2_buf.type = handle->buffer_type;
+ v4l2_buf.memory = V4L2_MEMORY_MMAP;
+ v4l2_buf.index = i;
+ v4l2_buf.m.planes = v4l2_planes;
+ v4l2_buf.length = plane_num;
+
+ if (v4l2_ioctl(handle->device_fd, VIDIOC_QUERYBUF, &v4l2_buf) < 0) {
+ LOGE("[%d] query buf failed. errno %d", i, errno);
+ goto _START_STREAM_FAILED;
+ }
+
+ buffer = &handle->vision_source_buffers[i];
+
+ buffer->index = i;
+ buffer->pixel_format = pixel_format;
+ buffer->resolution.width = resolution->width;
+ buffer->resolution.height = resolution->height;
+ buffer->total_size = v4l2_buf.length;
+ buffer->num_planes = plane_num;
+ buffer->planes[0].size = v4l2_buf.length;
+ buffer->planes[0].data =
+ v4l2_mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE,
+ MAP_SHARED, handle->device_fd, v4l2_buf.m.offset);
+
+ if (buffer->planes[0].data == MAP_FAILED) {
+ LOGE("[%d] mmap failed (errno %d)", i, errno);
+ goto _START_STREAM_FAILED;
+ }
+
+ if (__vision_source_v4l2_qbuf(handle->device_fd, handle->buffer_type,
+ V4L2_MEMORY_MMAP,
+ i) != VISION_SOURCE_ERROR_NONE) {
+ LOGE("[%d] qbuf failed (errno %d)", i, errno);
+ goto _START_STREAM_FAILED;
+ }
+ }
+
+ /* stream on */
+ ret = __vision_source_v4l2_stream(handle->device_fd, handle->buffer_type,
+ TRUE);
+ if (ret != VISION_SOURCE_ERROR_NONE) {
+ LOGE("stream on failed");
+ goto _START_STREAM_FAILED;
+ }
+
+ return VISION_SOURCE_ERROR_NONE;
+
+_START_STREAM_FAILED:
+ __vision_source_stop_stream(handle, handle->buffer_count);
+ return ret;
+}
+
int vision_source_v4l2_init(vision_source_h *handle)
{
LOGD("enter");
return VISION_SOURCE_ERROR_NONE;
}
+static void *__fetch_buffer_and_callback(gpointer data)
+{
+ vision_source_v4l2_s *v4l2_handle = (vision_source_v4l2_s *) data;
+ int index;
+ unsigned int byte_size;
+
+ /* run buffer thread */
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+
+ while (v4l2_handle->buffer_thread_run) {
+ g_mutex_unlock(&v4l2_handle->buffer_lock);
+
+ if (__vision_source_v4l2_wait_frame(v4l2_handle->device_fd, 5) !=
+ VISION_SOURCE_ERROR_NONE) {
+ LOGE("frame wait failed");
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+ break;
+ }
+
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+
+ if (v4l2_handle->buffer_thread_run == FALSE) {
+ LOGW("stop buffer handler thread");
+ break;
+ }
+ if (__vision_source_v4l2_dqbuf(v4l2_handle->device_fd,
+ v4l2_handle->buffer_type,
+ V4L2_MEMORY_MMAP, &index, &byte_size) !=
+ VISION_SOURCE_ERROR_NONE) {
+ LOGE("dqbuf failed");
+ break;
+ }
+ g_mutex_unlock(&v4l2_handle->buffer_lock);
+ v4l2_handle->vision_source_buffers[index].planes[0].used_size =
+ byte_size;
+
+ if (v4l2_handle->stream_callback) {
+ v4l2_handle->stream_callback(
+ &v4l2_handle->vision_source_buffers[index],
+ v4l2_handle->stream_callback_data);
+ }
+ if (__vision_source_v4l2_qbuf(
+ v4l2_handle->device_fd, v4l2_handle->buffer_type,
+ V4L2_MEMORY_MMAP, index) != VISION_SOURCE_ERROR_NONE) {
+ LOGE("qbuf failed");
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+ break;
+ }
+ sched_yield();
+
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+ }
+
+ g_mutex_unlock(&v4l2_handle->buffer_lock);
+
+ return NULL;
+}
+
+int source_v4l2_start_stream(vision_source_h handle, stream_cb callback,
+ void *user_data)
+{
+ VISION_SOURCE_NULL_ARG_CHECK(handle);
+
+ vision_source_v4l2_s *v4l2_handle = (vision_source_v4l2_s *) handle;
+
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&v4l2_handle->lock);
+
+ int ret = __vision_source_start_stream(
+ v4l2_handle, v4l2_handle->stream_format.pixel_format,
+ &v4l2_handle->stream_format.resolution,
+ v4l2_handle->stream_format.fps, BUFFER_MAX);
+ if (ret != VISION_SOURCE_ERROR_NONE) {
+ LOGE("__vision_source_start_stream failed[0x%x]", ret);
+ g_mutex_unlock(&v4l2_handle->lock);
+ return ret;
+ }
+
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+
+ v4l2_handle->buffer_thread_run = TRUE;
+ v4l2_handle->stream_callback = callback;
+ v4l2_handle->stream_callback_data = user_data;
+
+ v4l2_handle->buffer_thread = g_thread_try_new("vision_source_buffer_thread",
+ __fetch_buffer_and_callback,
+ (gpointer) v4l2_handle, NULL);
+ if (!v4l2_handle->buffer_thread) {
+ LOGE("failed to create buffer handler thread");
+ g_mutex_unlock(&v4l2_handle->buffer_lock);
+ __vision_source_stop_stream(v4l2_handle, v4l2_handle->buffer_count);
+
+ return ret;
+ }
+
+ g_mutex_unlock(&v4l2_handle->buffer_lock);
+
+ LOGD("start preview done");
+
+ return VISION_SOURCE_ERROR_NONE;
+}
+
+int source_v4l2_stop_stream(vision_source_h handle)
+{
+ VISION_SOURCE_NULL_ARG_CHECK(handle);
+
+ vision_source_v4l2_s *v4l2_handle = (vision_source_v4l2_s *) handle;
+
+ LOGD("start");
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&v4l2_handle->lock);
+
+ g_mutex_lock(&v4l2_handle->buffer_lock);
+
+ v4l2_handle->buffer_thread_run = FALSE;
+
+ g_mutex_unlock(&v4l2_handle->buffer_lock);
+
+ int ret =
+ __vision_source_stop_stream(v4l2_handle, v4l2_handle->buffer_count);
+
+ /* wait for preview thread exit */
+ if (v4l2_handle->buffer_thread)
+ g_thread_join(v4l2_handle->buffer_thread);
+ v4l2_handle->buffer_thread = NULL;
+
+ LOGD("stop preview done [0x%x]", ret);
+
+ return VISION_SOURCE_ERROR_NONE;
+}
+
void attach_backend(vision_source_func_s *funcp)
{
funcp->init = vision_source_v4l2_init;
funcp->open_device = vision_source_v4l2_open_device;
funcp->close_device = vision_source_v4l2_close_device;
funcp->set_stream_format = vision_source_v4l2_set_stream_format;
+ funcp->start_stream = source_v4l2_start_stream;
+ funcp->stop_stream = source_v4l2_stop_stream;
}
\ No newline at end of file
EXPECT_EQ(vision_source_set_stream_format(ms_handle, &format),
VISION_SOURCE_ERROR_NONE);
EXPECT_EQ(vision_source_close_device(ms_handle), VISION_SOURCE_ERROR_NONE);
+}
+
+class VisionV4L2FixedFormat : public ::testing::Test
+{
+protected:
+ void SetUp() override
+ {
+ ASSERT_EQ(vision_source_init(&ms_handle), VISION_SOURCE_ERROR_NONE);
+ vision_source_device_info_list_s dev_list;
+ ASSERT_EQ(vision_source_enumerate_devices(ms_handle, &dev_list),
+ VISION_SOURCE_ERROR_NONE);
+ ASSERT_GT(dev_list.count, 0);
+
+ ASSERT_GT(dev_list.device_info[0].pixel_list.count, 0);
+ vision_source_format_s format;
+ format.pixel_format =
+ dev_list.device_info[0].pixel_list.pixels[0].pixel_format;
+ ASSERT_GT(dev_list.device_info[0]
+ .pixel_list.pixels[0]
+ .resolution_list.count,
+ 0);
+
+ format.resolution.width = dev_list.device_info[0]
+ .pixel_list.pixels[0]
+ .resolution_list.resolutions[0]
+ .resolution.width;
+ format.resolution.height = dev_list.device_info[0]
+ .pixel_list.pixels[0]
+ .resolution_list.resolutions[0]
+ .resolution.height;
+ ASSERT_GT(dev_list.device_info[0]
+ .pixel_list.pixels[0]
+ .resolution_list.resolutions[0]
+ .fps_list.count,
+ 0);
+ format.fps = dev_list.device_info[0]
+ .pixel_list.pixels[0]
+ .resolution_list.resolutions[0]
+ .fps_list.fps[0];
+
+ ASSERT_EQ(vision_source_open_device(ms_handle, 0),
+ VISION_SOURCE_ERROR_NONE);
+ EXPECT_EQ(vision_source_set_stream_format(ms_handle, &format),
+ VISION_SOURCE_ERROR_NONE);
+ }
+
+ void TearDown() override
+ {
+ EXPECT_EQ(vision_source_close_device(ms_handle),
+ VISION_SOURCE_ERROR_NONE);
+ ASSERT_EQ(vision_source_exit(ms_handle), VISION_SOURCE_ERROR_NONE);
+ }
+ vision_source_h ms_handle;
+};
+
+TEST_F(VisionV4L2FixedFormat, OnlyStopStream)
+{
+ EXPECT_EQ(vision_source_stop_stream(ms_handle), VISION_SOURCE_ERROR_NONE);
+}
+
+TEST_F(VisionV4L2FixedFormat, StartStream)
+{
+ ASSERT_EQ(vision_source_start_stream(ms_handle, nullptr, nullptr),
+ VISION_SOURCE_ERROR_NONE);
+ sleep(1);
+ EXPECT_EQ(vision_source_stop_stream(ms_handle), VISION_SOURCE_ERROR_NONE);
+}
+
+static int test_cb(vision_source_buffer_s *buffer, void *user_data)
+{
+ FILE *file;
+ clock_t end = clock();
+ clock_t *start = (clock_t *) user_data;
+ char filename[128];
+
+ double delta_ms = (double) (end - *start) / CLOCKS_PER_SEC * 1000;
+
+ snprintf(filename, 127, "out_%04u.data", (unsigned) delta_ms);
+ file = fopen(filename, "w");
+
+ fwrite(buffer->planes[0].data, sizeof(unsigned char),
+ buffer->planes[0].used_size, file);
+ fclose(file);
+ return 0;
+}
+
+TEST_F(VisionV4L2FixedFormat, StartWithCallback)
+{
+ clock_t start = clock();
+ ASSERT_EQ(vision_source_start_stream(ms_handle, test_cb, &start),
+ VISION_SOURCE_ERROR_NONE);
+ sleep(1);
+ EXPECT_EQ(vision_source_stop_stream(ms_handle), VISION_SOURCE_ERROR_NONE);
}
\ No newline at end of file