Video recording
authorPaul Kocialkowski <contact@paulk.fr>
Sat, 2 Feb 2013 09:55:27 +0000 (10:55 +0100)
committerPaul Kocialkowski <contact@paulk.fr>
Sat, 2 Feb 2013 09:55:27 +0000 (10:55 +0100)
Signed-off-by: Paul Kocialkowski <contact@paulk.fr>
exynos_camera.c
exynos_camera.h

index cbf401c..1c32dc2 100644 (file)
@@ -18,6 +18,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <time.h>
 #include <errno.h>
 #include <malloc.h>
 #include <sys/stat.h>
@@ -33,6 +34,7 @@
 
 #define LOG_TAG "exynos_camera"
 #include <utils/Log.h>
+#include <utils/Timers.h>
 
 #include "exynos_camera.h"
 
@@ -72,6 +74,7 @@ struct exynos_camera_preset exynos_camera_presets_galaxys2[] = {
 
                        .recording_preview_size = NULL,
                        .recording_size = "720x480",
+                       .recording_size_values = "1280x720,720x480",
                        .recording_format = "yuv420sp",
 
                        .zoom_supported = 1,
@@ -112,6 +115,7 @@ struct exynos_camera_preset exynos_camera_presets_galaxys2[] = {
 
                        .recording_preview_size = NULL,
                        .recording_size = "640x480",
+                       .recording_size_values = "640x480",
                        .recording_format = "yuv420sp",
 
                        .zoom_supported = 0,
@@ -154,7 +158,7 @@ int exynos_camera_init(struct exynos_camera *exynos_camera, int id)
        struct exynos_v4l2_ext_control control;
        int rc;
 
-       if (exynos_camera == NULL)
+       if (exynos_camera == NULL || id >= exynos_camera->config->presets_count)
                return -EINVAL;
 
        // Init FIMC1
@@ -305,6 +309,8 @@ int exynos_camera_params_init(struct exynos_camera *exynos_camera, int id)
        // Recording
        exynos_param_string_set(exynos_camera, "video-size",
                exynos_camera->config->presets[id].params.recording_size);
+       exynos_param_string_set(exynos_camera, "video-size-values",
+               exynos_camera->config->presets[id].params.recording_size_values);
        exynos_param_string_set(exynos_camera, "video-frame-format",
                exynos_camera->config->presets[id].params.recording_format);
 
@@ -365,6 +371,10 @@ int exynos_camera_params_apply(struct exynos_camera *exynos_camera)
        char *video_size_string;
        int recording_width = 0;
        int recording_height = 0;
+       char *video_frame_format_string;
+       int recording_format;
+       int camera_sensor_mode;
+       int camera_sensor_output_size;
 
        char *metering_string;
        int metering;
@@ -498,6 +508,45 @@ int exynos_camera_params_apply(struct exynos_camera *exynos_camera)
                        exynos_camera->recording_height = recording_height;
        }
 
+       video_frame_format_string = exynos_param_string_get(exynos_camera, "video-frame-format");
+       if (video_frame_format_string != NULL) {
+               if (strcmp(video_frame_format_string, "yuv420sp") == 0) {
+                       recording_format = V4L2_PIX_FMT_NV12;
+               } else if (strcmp(video_frame_format_string, "yuv420p") == 0) {
+                       recording_format = V4L2_PIX_FMT_YUV420;
+               } else if (strcmp(video_frame_format_string, "rgb565") == 0) {
+                       recording_format = V4L2_PIX_FMT_RGB565;
+               } else if (strcmp(video_frame_format_string, "rgb8888") == 0) {
+                       recording_format = V4L2_PIX_FMT_RGB32;
+               } else {
+                       LOGE("%s: Unsupported recording format: %s", __func__, video_frame_format_string);
+                       recording_format = V4L2_PIX_FMT_NV12;
+               }
+
+               if (recording_format != exynos_camera->recording_format)
+                       exynos_camera->recording_format = recording_format;
+       }
+
+       recording_hint_string = exynos_param_string_get(exynos_camera, "recording-hint");
+       if (recording_hint_string != NULL && strcmp(recording_hint_string, "true") == 0)
+               camera_sensor_mode = SENSOR_MOVIE;
+       else
+               camera_sensor_mode = SENSOR_CAMERA;
+
+       // Entering recording mode
+       if (camera_sensor_mode != exynos_camera->camera_sensor_mode) {
+               exynos_camera->camera_sensor_mode = camera_sensor_mode;
+               rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SENSOR_MODE, camera_sensor_mode);
+               if (rc < 0)
+                       LOGE("%s: s ctrl failed!", __func__);
+
+               camera_sensor_output_size = ((recording_width & 0xffff) << 16) | (recording_height & 0xffff);
+               rc = exynos_v4l2_s_ctrl(exynos_camera, 0, V4L2_CID_CAMERA_SENSOR_OUTPUT_SIZE,
+                       camera_sensor_output_size);
+               if (rc < 0)
+                       LOGE("%s: s ctrl failed!", __func__);
+       }
+
        // Zoom
        zoom_supported_string = exynos_param_string_get(exynos_camera, "zoom-supported");
        if (zoom_supported_string != NULL && strcmp(zoom_supported_string, "true") == 0) {
@@ -1334,6 +1383,12 @@ int exynos_camera_preview(struct exynos_camera *exynos_camera)
        void *preview_data;
        void *window_data;
 
+       unsigned int recording_y_addr;
+       unsigned int recording_cbcr_addr;
+       nsecs_t timestamp;
+       struct exynos_camera_addrs *addrs;
+       struct timespec ts;
+
        int index;
        int rc;
        int i;
@@ -1342,6 +1397,8 @@ int exynos_camera_preview(struct exynos_camera *exynos_camera)
                exynos_camera->preview_window == NULL)
                return -EINVAL;
 
+       timestamp = systemTime(1);
+
        // V4L2
 
        rc = exynos_v4l2_poll(exynos_camera, 0);
@@ -1396,7 +1453,68 @@ int exynos_camera_preview(struct exynos_camera *exynos_camera)
                        exynos_camera->preview_memory, index, NULL, exynos_camera->callbacks.user);
        }
 
+       // Recording
+
+       if (exynos_camera->recording_enabled && exynos_camera->recording_memory != NULL) {
+               pthread_mutex_lock(&exynos_camera->recording_mutex);
+
+               // V4L2
+
+               rc = exynos_v4l2_poll(exynos_camera, 2);
+               if (rc < 0) {
+                       LOGE("%s: poll failed!", __func__);
+                       goto error_recording;
+               } else if (rc == 0) {
+                       LOGE("%s: poll timeout!", __func__);
+                       goto error_recording;
+               }
+
+               index = exynos_v4l2_dqbuf_cap(exynos_camera, 2);
+               if (index < 0) {
+                       LOGE("%s: dqbuf failed!", __func__);
+                       goto error_recording;
+               }
+
+               recording_y_addr = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_PADDR_Y, index);
+               if (recording_y_addr == 0xffffffff) {
+                       LOGE("%s: s ctrl failed!", __func__);
+                       goto error_recording;
+               }
+
+               recording_cbcr_addr = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_PADDR_CBCR, index);
+               if (recording_cbcr_addr == 0xffffffff) {
+                       LOGE("%s: s ctrl failed!", __func__);
+                       goto error_recording;
+               }
+
+               addrs = (struct exynos_camera_addrs *) exynos_camera->recording_memory->data;
+
+               addrs[index].type = 0; // kMetadataBufferTypeCameraSource
+               addrs[index].y = recording_y_addr;
+               addrs[index].cbcr = recording_cbcr_addr;
+               addrs[index].index = index;
+               addrs[index].reserved = 0;
+
+               pthread_mutex_unlock(&exynos_camera->recording_mutex);
+
+               if (EXYNOS_CAMERA_MSG_ENABLED(CAMERA_MSG_VIDEO_FRAME) && EXYNOS_CAMERA_CALLBACK_DEFINED(data_timestamp)) {
+                       exynos_camera->callbacks.data_timestamp(timestamp, CAMERA_MSG_VIDEO_FRAME,
+                               exynos_camera->recording_memory, index, exynos_camera->callbacks.user);
+               } else {
+                       rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, index);
+                       if (rc < 0) {
+                               LOGE("%s: qbuf failed!", __func__);
+                               return -1;
+                       }
+               }
+       }
+
        return 0;
+
+error_recording:
+       pthread_mutex_lock(&exynos_camera->recording_mutex);
+
+       return -1;
 }
 
 void *exynos_camera_preview_thread(void *data)
@@ -1445,11 +1563,10 @@ int exynos_camera_preview_start(struct exynos_camera *exynos_camera)
 
        pthread_attr_t thread_attr;
 
-       int id;
        int rc;
        int i;
 
-       if (exynos_camera->config == NULL)
+       if (exynos_camera == NULL)
                return -EINVAL;
 
        if (exynos_camera->preview_enabled) {
@@ -1495,7 +1612,7 @@ int exynos_camera_preview_start(struct exynos_camera *exynos_camera)
        }
 
        exynos_camera->preview_buffers_count = rc;
-       LOGD("Found %d buffers available!", exynos_camera->preview_buffers_count);
+       LOGD("Found %d preview buffers available!", exynos_camera->preview_buffers_count);
 
        fps = exynos_camera->preview_fps;
        memset(&streamparm, 0, sizeof(streamparm));
@@ -1641,6 +1758,189 @@ void exynos_camera_preview_stop(struct exynos_camera *exynos_camera)
        exynos_camera->preview_window = NULL;
 
        pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+       pthread_mutex_destroy(&exynos_camera->preview_lock_mutex);
+       pthread_mutex_destroy(&exynos_camera->preview_mutex);
+}
+
+// Recording
+
+void exynos_camera_recording_frame_release(struct exynos_camera *exynos_camera, void *data)
+{
+       struct exynos_camera_addrs *addrs;
+       int rc;
+
+       if (exynos_camera == NULL || data == NULL)
+               return;
+
+       addrs = (struct exynos_camera_addrs *) data;
+       if (addrs->index >= (unsigned int) exynos_camera->recording_buffers_count)
+               return;
+
+       pthread_mutex_lock(&exynos_camera->recording_mutex);
+
+       rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, addrs->index);
+       if (rc < 0) {
+               LOGE("%s: qbuf failed!", __func__);
+               goto error;
+       }
+
+error:
+       pthread_mutex_unlock(&exynos_camera->recording_mutex);
+}
+
+int exynos_camera_recording_start(struct exynos_camera *exynos_camera)
+{
+       int width, height, format;
+       int fd;
+
+       int rc;
+       int i;
+
+       if (exynos_camera == NULL)
+               return -EINVAL;
+
+       if (exynos_camera->recording_enabled) {
+               LOGE("Recording was already started!");
+               return 0;
+       }
+
+       pthread_mutex_lock(&exynos_camera->preview_mutex);
+
+       // V4L2
+
+       format = exynos_camera->recording_format;
+
+       rc = exynos_v4l2_enum_fmt_cap(exynos_camera, 2, format);
+       if (rc < 0) {
+               LOGE("%s: enum fmt failed!", __func__);
+               goto error;
+       }
+
+       width = exynos_camera->recording_width;
+       height = exynos_camera->recording_height;
+
+       rc = exynos_v4l2_s_fmt_pix_cap(exynos_camera, 2, width, height, format, V4L2_PIX_FMT_MODE_CAPTURE);
+       if (rc < 0) {
+               LOGE("%s: s fmt failed!", __func__);
+               goto error;
+       }
+
+       for (i=EXYNOS_CAMERA_MAX_BUFFERS_COUNT ; i >= EXYNOS_CAMERA_MIN_BUFFERS_COUNT ; i--) {
+               rc = exynos_v4l2_reqbufs_cap(exynos_camera, 2, i);
+               if (rc >= 0)
+                       break;
+       }
+
+       if (rc < 0) {
+               LOGE("%s: reqbufs failed!", __func__);
+               goto error;
+       }
+
+       exynos_camera->recording_buffers_count = rc;
+       LOGD("Found %d recording buffers available!", exynos_camera->recording_buffers_count);
+
+       for (i=0 ; i < exynos_camera->recording_buffers_count ; i++) {
+               rc = exynos_v4l2_querybuf_cap(exynos_camera, 2, i);
+               if (rc < 0) {
+                       LOGE("%s: querybuf failed!", __func__);
+                       goto error;
+               }
+       }
+
+       if (exynos_camera->callbacks.request_memory != NULL) {
+               if (exynos_camera->recording_memory != NULL && exynos_camera->recording_memory->release != NULL)
+                       exynos_camera->recording_memory->release(exynos_camera->recording_memory);
+
+               exynos_camera->recording_memory =
+                       exynos_camera->callbacks.request_memory(-1, sizeof(struct exynos_camera_addrs),
+                               exynos_camera->recording_buffers_count, 0);
+               if (exynos_camera->recording_memory == NULL) {
+                       LOGE("%s: memory request failed!", __func__);
+                       goto error;
+               }
+       } else {
+               LOGE("%s: No memory request function!", __func__);
+               goto error;
+       }
+
+       for (i=0 ; i < exynos_camera->recording_buffers_count ; i++) {
+               rc = exynos_v4l2_qbuf_cap(exynos_camera, 2, i);
+               if (rc < 0) {
+                       LOGE("%s: qbuf failed!", __func__);
+                       goto error;
+               }
+       }
+
+       rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_ROTATION,
+               exynos_camera->camera_rotation);
+       if (rc < 0) {
+               LOGE("%s: s ctrl failed!", __func__);
+               goto error;
+       }
+
+       rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_HFLIP,
+               exynos_camera->camera_hflip);
+       if (rc < 0) {
+               LOGE("%s: s ctrl failed!", __func__);
+               goto error;
+       }
+
+       rc = exynos_v4l2_s_ctrl(exynos_camera, 2, V4L2_CID_VFLIP,
+               exynos_camera->camera_vflip);
+       if (rc < 0) {
+               LOGE("%s: s ctrl failed!", __func__);
+               goto error;
+       }
+
+       rc = exynos_v4l2_streamon_cap(exynos_camera, 2);
+       if (rc < 0) {
+               LOGE("%s: streamon failed!", __func__);
+               goto error;
+       }
+
+       pthread_mutex_init(&exynos_camera->recording_mutex, NULL);
+
+       exynos_camera->recording_enabled = 1;
+
+       pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+       return 0;
+error:
+       pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+       return -1;
+}
+
+void exynos_camera_recording_stop(struct exynos_camera *exynos_camera)
+{
+       int rc;
+
+       if (exynos_camera == NULL)
+               return;
+
+       if (!exynos_camera->recording_enabled) {
+               LOGE("Recording was already stopped!");
+               return;
+       }
+
+       exynos_camera->recording_enabled = 0;
+
+       pthread_mutex_lock(&exynos_camera->preview_mutex);
+
+       rc = exynos_v4l2_streamoff_cap(exynos_camera, 2);
+       if (rc < 0) {
+               LOGE("%s: streamoff failed!", __func__);
+       }
+
+       if (exynos_camera->recording_memory != NULL && exynos_camera->recording_memory->release != NULL) {
+               exynos_camera->recording_memory->release(exynos_camera->recording_memory);
+               exynos_camera->recording_memory = NULL;
+       }
+
+       pthread_mutex_unlock(&exynos_camera->preview_mutex);
+
+       pthread_mutex_destroy(&exynos_camera->recording_mutex);
 }
 
 /*
@@ -1670,8 +1970,6 @@ int exynos_camera_set_preview_window(struct camera_device *dev,
        if (w == NULL)
                return 0;
 
-       pthread_mutex_lock(&exynos_camera->preview_mutex);
-
        exynos_camera->preview_window = w;
 
        if (w->set_buffer_count == NULL || w->set_usage == NULL || w->set_buffers_geometry == NULL)
@@ -1686,13 +1984,13 @@ int exynos_camera_set_preview_window(struct camera_device *dev,
        if (rc) {
                LOGE("%s: Unable to set buffer count (%d)", __func__,
                        exynos_camera->preview_buffers_count);
-               goto error;
+               return -1;
        }
 
        rc = w->set_usage(w, GRALLOC_USAGE_SW_WRITE_OFTEN);
        if (rc) {
                LOGE("%s: Unable to set usage", __func__);
-               goto error;
+               return -1;
        }
 
        width = exynos_camera->preview_width;
@@ -1720,20 +2018,13 @@ int exynos_camera_set_preview_window(struct camera_device *dev,
        rc = w->set_buffers_geometry(w, width, height, hal_format);
        if (rc) {
                LOGE("%s: Unable to set buffers geometry", __func__);
-               goto error;
+               return -1;
        }
 
-       pthread_mutex_unlock(&exynos_camera->preview_mutex);
-
        // Unlock preview lock
        pthread_mutex_unlock(&exynos_camera->preview_lock_mutex);
 
        return 0;
-
-error:
-       pthread_mutex_unlock(&exynos_camera->preview_mutex);
-
-       return -1;
 }
 
 void exynos_camera_set_callbacks(struct camera_device *dev,
@@ -1848,32 +2139,63 @@ int exynos_camera_store_meta_data_in_buffers(struct camera_device *dev,
 {
        LOGD("%s(%p, %d)", __func__, dev, enable);
 
+       if (!enable) {
+               LOGE("%s: Cannot disable meta-data in buffers!", __func__);
+               return -1;
+       }
+
        return 0;
 }
 
 int exynos_camera_start_recording(struct camera_device *dev)
 {
+       struct exynos_camera *exynos_camera;
+
        LOGD("%s(%p)", __func__, dev);
 
-       return 0;
+       exynos_camera = (struct exynos_camera *) dev->priv;
+
+       return exynos_camera_recording_start(exynos_camera);
 }
 
 void exynos_camera_stop_recording(struct camera_device *dev)
 {
+       struct exynos_camera *exynos_camera;
+
        LOGD("%s(%p)", __func__, dev);
+
+       exynos_camera = (struct exynos_camera *) dev->priv;
+
+       exynos_camera_recording_stop(exynos_camera);
 }
 
 int exynos_camera_recording_enabled(struct camera_device *dev)
 {
+       struct exynos_camera *exynos_camera;
+
        LOGD("%s(%p)", __func__, dev);
 
-       return 0;
+       if (dev == NULL || dev->priv == NULL)
+               return -EINVAL;
+
+       exynos_camera = (struct exynos_camera *) dev->priv;
+
+       return exynos_camera->recording_enabled;
 }
 
 void exynos_camera_release_recording_frame(struct camera_device *dev,
        const void *opaque)
 {
-       LOGD("%s(%p, %p)", __func__, dev, opaque);
+       struct exynos_camera *exynos_camera;
+
+       LOGV("%s(%p, %p)", __func__, dev, opaque);
+
+       if (dev == NULL || dev->priv == NULL)
+               return;
+
+       exynos_camera = (struct exynos_camera *) dev->priv;
+
+       exynos_camera_recording_frame_release(exynos_camera, (void *) opaque);
 }
 
 int exynos_camera_auto_focus(struct camera_device *dev)
@@ -1981,7 +2303,7 @@ char *exynos_camera_get_parameters(struct camera_device *dev)
                LOGE("%s: Couldn't find any param", __func__);
                return strdup("");
        }
-
+LOGD("[%s]", params);
        return params;
 }
 
index f45bdbe..d4c33f3 100644 (file)
@@ -92,6 +92,7 @@ struct exynos_camera_params {
 
        char *recording_preview_size;
        char *recording_size;
+       char *recording_size_values;
        char *recording_format;
 
        int zoom_supported;
@@ -175,6 +176,12 @@ struct exynos_camera {
        camera_memory_t *preview_memory;
        int preview_buffers_count;
 
+       // Recording
+       pthread_mutex_t recording_mutex;
+
+       int recording_enabled;
+       camera_memory_t *recording_memory;
+       int recording_buffers_count;
 
        // Camera params
        int camera_rotation;
@@ -183,6 +190,8 @@ struct exynos_camera {
        int camera_picture_format;
        int camera_focal_length;
 
+       int camera_sensor_mode;
+
        // Params
        int preview_width;
        int preview_height;
@@ -198,12 +207,21 @@ struct exynos_camera {
        int jpeg_quality;
        int recording_width;
        int recording_height;
+       int recording_format;
        int zoom;
        int metering;
        int focus_object_x;
        int focus_object_y;
 };
 
+struct exynos_camera_addrs {
+       unsigned int type;
+       unsigned int y;
+       unsigned int cbcr;
+       unsigned int index;
+       unsigned int reserved;
+};
+
 // This is because the linux header uses anonymous union
 struct exynos_v4l2_ext_control {
        __u32 id;