static void gst_droidcamsrc_dev_preview_metadata_callback (void *user, const DroidMediaCameraFace * faces, size_t num_faces) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GstStructure *s; gint width, height; GValue regions = G_VALUE_INIT; gint i; GST_DEBUG_OBJECT (src, "dev preview metadata callback"); GST_INFO_OBJECT (src, "camera detected %d faces", num_faces); GST_OBJECT_LOCK (src); width = src->width; height = src->height; GST_OBJECT_UNLOCK (src); s = gst_structure_new ("regions-of-interest", "frame-width", G_TYPE_UINT, width, "frame-height", G_TYPE_UINT, height, "type", G_TYPE_UINT, GST_DROIDCAMSRC_ROI_FACE_AREA, NULL); g_value_init (®ions, GST_TYPE_LIST); for (i = 0; i < num_faces; i++) { GValue region = G_VALUE_INIT; int x, y, w, h, r, b; GstStructure *rs; g_value_init (®ion, GST_TYPE_STRUCTURE); GST_DEBUG_OBJECT (src, "face %d: score=%d, left=%d, top=%d, right=%d, bottom=%d", i, faces[i].score, faces[i].left, faces[i].top, faces[i].right, faces[i].bottom); x = gst_util_uint64_scale (faces[i].left + 1000, width, 2000); y = gst_util_uint64_scale (faces[i].top + 1000, height, 2000); r = gst_util_uint64_scale (faces[i].right + 1000, width, 2000); b = gst_util_uint64_scale (faces[i].bottom + 1000, height, 2000); w = r - x; h = b - y; rs = gst_structure_new ("region-of-interest", "region-x", G_TYPE_UINT, x, "region-y", G_TYPE_UINT, y, "region-w", G_TYPE_UINT, w, "region-h", G_TYPE_UINT, h, "region-id", G_TYPE_INT, faces[i].id, "region-score", G_TYPE_INT, faces[i].score, NULL); gst_value_set_structure (®ion, rs); gst_structure_free (rs); gst_value_list_append_value (®ions, ®ion); g_value_unset (®ion); } gst_structure_take_value (s, "regions", ®ions); gst_droidcamsrc_post_message (src, s); }
static void gst_droidcamsrc_dev_prepare_buffer (GstDroidCamSrcDev * dev, GstBuffer * buffer, DroidMediaRect rect, int width, int height, GstVideoFormat format) { GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GstVideoCropMeta *crop; GST_LOG_OBJECT (src, "prepare buffer %" GST_PTR_FORMAT, buffer); gst_droidcamsrc_timestamp (src, buffer); crop = gst_buffer_add_video_crop_meta (buffer); crop->x = rect.left; crop->y = rect.top; crop->width = rect.right - rect.left; crop->height = rect.bottom - rect.top; gst_buffer_add_gst_buffer_orientation_meta (buffer, dev->info->orientation, dev->info->direction); gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, format, width, height); GST_LOG_OBJECT (src, "preview info: w=%d, h=%d, crop: x=%d, y=%d, w=%d, h=%d", width, height, crop->x, crop->y, crop->width, crop->height); }
gboolean gst_droidcamsrc_dev_start (GstDroidCamSrcDev * dev, gboolean apply_settings) { gboolean ret = FALSE; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); g_rec_mutex_lock (dev->lock); if (dev->running) { GST_WARNING_OBJECT (src, "preview is already running"); ret = TRUE; goto out; } GST_DEBUG_OBJECT (src, "dev start"); if (!gst_buffer_pool_set_active (dev->pool, TRUE)) { GST_ERROR_OBJECT (src, "Failed to activate buffer pool"); goto out; } if (apply_settings) { gst_droidcamsrc_apply_mode_settings (src, SET_ONLY); } /* now set params */ if (!gst_droidcamsrc_dev_set_params (dev)) { goto out; } if (dev->use_raw_data) { GST_INFO_OBJECT (src, "Using raw data mode"); droid_media_camera_set_preview_callback_flags (dev->cam, dev->c.CAMERA_FRAME_CALLBACK_FLAG_CAMERA); } else { GST_INFO_OBJECT (src, "Using native buffers mode"); droid_media_camera_set_preview_callback_flags (dev->cam, dev->c.CAMERA_FRAME_CALLBACK_FLAG_NOOP); } if (!droid_media_camera_start_preview (dev->cam)) { GST_ERROR_OBJECT (src, "error starting preview"); goto out; } dev->running = TRUE; ret = TRUE; out: if (ret != TRUE) { gst_buffer_pool_set_active (dev->pool, FALSE); } g_rec_mutex_unlock (dev->lock); return ret; }
static void gst_droidcamsrc_dev_raw_image_notify_callback (void *user) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GST_DEBUG_OBJECT (src, "dev raw image notify callback"); GST_FIXME_OBJECT (src, "implement me"); }
static void gst_droidcamsrc_dev_postview_frame_callback (void *user, G_GNUC_UNUSED DroidMediaData * mem) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GST_DEBUG_OBJECT (src, "dev postview frame callback"); GST_FIXME_OBJECT (src, "implement me"); }
static void gst_droidcamsrc_dev_zoom_callback (void *user, G_GNUC_UNUSED int value, G_GNUC_UNUSED int arg) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GST_DEBUG_OBJECT (src, "dev zoom callback"); GST_FIXME_OBJECT (src, "implement me"); }
static void gst_droidcamsrc_dev_error_callback (void *user, int arg) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GST_DEBUG_OBJECT (src, "dev error callback"); GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL), ("error 0x%x from camera HAL", arg)); }
static void gst_droidcamsrc_dev_focus_move_callback (void *user, int arg) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GstStructure *s; GST_DEBUG_OBJECT (src, "dev focus move callback"); /* TODO: an idea could be to query focus state when moving stops or starts * and use that to emulate realtime reporting of CAF status */ GST_LOG_OBJECT (src, "focus move %d", arg); s = gst_structure_new ("focus-move", "status", G_TYPE_INT, arg, NULL); gst_droidcamsrc_post_message (src, s); }
static void gst_droidcamsrc_dev_shutter_callback (void *user) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GST_DEBUG_OBJECT (src, "dev shutter callback"); g_rec_mutex_lock (dev->lock); if (!dev->img->image_start_sent) { gst_droidcamsrc_post_message (src, gst_structure_new_empty (GST_DROIDCAMSRC_CAPTURE_START)); dev->img->image_start_sent = TRUE; } g_rec_mutex_unlock (dev->lock); }
static void gst_droidcamsrc_dev_focus_callback (void *user, int arg) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GstStructure *s; gint status; GST_DEBUG_OBJECT (src, "dev focus callback"); if (arg) { status = GST_PHOTOGRAPHY_FOCUS_STATUS_SUCCESS; } else { status = GST_PHOTOGRAPHY_FOCUS_STATUS_FAIL; } s = gst_structure_new (GST_PHOTOGRAPHY_AUTOFOCUS_DONE, "status", G_TYPE_INT, status, NULL); gst_droidcamsrc_post_message (src, s); }
gboolean gst_droidcamsrc_dev_start_video_recording (GstDroidCamSrcDev * dev) { GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); gboolean ret = FALSE; GST_DEBUG ("dev start video recording"); gst_buffer_pool_set_flushing (dev->pool, TRUE); g_mutex_lock (&dev->vidsrc->lock); dev->vidsrc->pushed_buffers = 0; g_mutex_unlock (&dev->vidsrc->lock); g_rec_mutex_lock (dev->lock); if (dev->use_raw_data) { GST_ELEMENT_ERROR (src, STREAM, FORMAT, ("Cannot record video in raw mode"), (NULL)); goto out; } dev->vid->running = TRUE; dev->vid->eos_sent = FALSE; dev->vid->video_frames = 0; dev->vid->queued_frames = 0; if (!droid_media_camera_start_recording (dev->cam)) { GST_ELEMENT_ERROR (src, LIBRARY, FAILED, ("error starting video recording"), (NULL)); goto out; } ret = TRUE; out: g_rec_mutex_unlock (dev->lock); gst_buffer_pool_set_flushing (dev->pool, FALSE); return ret; }
gboolean gst_droidcamsrc_dev_open (GstDroidCamSrcDev * dev, GstDroidCamSrcCamInfo * info) { GstDroidCamSrc *src; g_rec_mutex_lock (dev->lock); src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GST_DEBUG_OBJECT (src, "dev open"); dev->info = info; dev->cam = droid_media_camera_connect (dev->info->num); if (!dev->cam) { g_rec_mutex_unlock (dev->lock); GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL), ("error opening camera")); return FALSE; } dev->queue = droid_media_camera_get_buffer_queue (dev->cam); if (!droid_media_camera_lock (dev->cam)) { droid_media_camera_disconnect (dev->cam); dev->cam = NULL; dev->queue = NULL; GST_ELEMENT_ERROR (src, LIBRARY, INIT, (NULL), ("error locking camera")); return FALSE; } /* disable shutter sound */ gst_droidcamsrc_dev_send_command (dev, dev->c.CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0); g_rec_mutex_unlock (dev->lock); return TRUE; }
static void gst_droidcamsrc_dev_preview_frame_callback (void *user, G_GNUC_UNUSED DroidMediaData * mem) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GstDroidCamSrcPad *pad = dev->vfsrc; GstBuffer *buffer; gsize width, height; DroidMediaRect rect; GST_DEBUG_OBJECT (src, "dev preview frame callback"); /* We are accessing this without a lock because: * 1) We should not be called while preview is stopped and this is when we manipulate this flag * 2) We can get called when we start the preview and we will deadlock because the lock is already held */ if (!dev->use_raw_data) { GST_WARNING_OBJECT (src, "preview frame callback called while not when we do not expect it"); return; } buffer = gst_buffer_new_allocate (NULL, mem->size, NULL); gst_buffer_fill (buffer, 0, mem->data, mem->size); GST_OBJECT_LOCK (src); width = src->width; height = src->height; rect = src->crop_rect; GST_OBJECT_UNLOCK (src); gst_droidcamsrc_dev_prepare_buffer (dev, buffer, rect, width, height, GST_VIDEO_FORMAT_NV21); g_mutex_lock (&pad->lock); g_queue_push_tail (pad->queue, buffer); g_cond_signal (&pad->cond); g_mutex_unlock (&pad->lock); }
static void gst_droidcamsrc_dev_frame_available (void *user) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); GstDroidCamSrcPad *pad = dev->vfsrc; DroidMediaBuffer *buffer; GstMemory *mem; DroidMediaRect rect; guint width, height; GstBuffer *buff; DroidMediaBufferCallbacks cb; GstFlowReturn flow_ret; DroidMediaBufferInfo info; GST_DEBUG_OBJECT (src, "frame available"); if (!pad->running) { GST_DEBUG_OBJECT (src, "vfsrc pad task is not running"); goto acquire_and_release; } /* We are accessing this without a lock because: * 1) We should not be called while preview is stopped and this is when we manipulate this flag * 2) We can get called when we start the preview and we will deadlock because the lock is already held */ if (dev->use_raw_data) { goto acquire_and_release; } flow_ret = gst_buffer_pool_acquire_buffer (dev->pool, &buff, NULL); if (flow_ret != GST_FLOW_OK) { GST_WARNING_OBJECT (src, "failed to acquire buffer from pool: %s", gst_flow_get_name (flow_ret)); goto acquire_and_release; } cb.ref = (DroidMediaCallback) gst_buffer_ref; cb.unref = (DroidMediaCallback) gst_buffer_unref; cb.data = buff; mem = gst_droid_media_buffer_allocator_alloc (dev->media_allocator, dev->queue, &cb); if (!mem) { GST_ERROR_OBJECT (src, "failed to acquire buffer from droidmedia"); gst_buffer_unref (buff); return; } buffer = gst_droid_media_buffer_memory_get_buffer (mem); gst_buffer_insert_memory (buff, 0, mem); gst_droidcamsrc_timestamp (src, buff); rect = droid_media_buffer_get_crop_rect (buffer); width = droid_media_buffer_get_width (buffer); height = droid_media_buffer_get_height (buffer); gst_droidcamsrc_dev_prepare_buffer (dev, buff, rect, width, height, GST_VIDEO_FORMAT_YV12); g_mutex_lock (&pad->lock); g_queue_push_tail (pad->queue, buff); g_cond_signal (&pad->cond); g_mutex_unlock (&pad->lock); GST_OBJECT_LOCK (src); src->crop_rect = rect; GST_OBJECT_UNLOCK (src); return; acquire_and_release: if (droid_media_buffer_queue_acquire_and_release (dev->queue, &info)) { GST_OBJECT_LOCK (src); src->crop_rect = info.crop_rect; GST_OBJECT_UNLOCK (src); } }
static void gst_droidcamsrc_dev_compressed_image_callback (void *user, DroidMediaData * mem) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); size_t size = mem->size; void *data = mem->data; GstBuffer *buffer; GstTagList *tags; GstEvent *event = NULL; void *d; GST_DEBUG_OBJECT (src, "dev compressed image callback"); if (!data) { GST_ERROR_OBJECT (src, "invalid memory from camera hal"); return; } /* TODO: research a way to get rid of the memcpy */ d = g_malloc (size); memcpy (d, data, size); buffer = gst_buffer_new_wrapped (d, size); if (!dev->img->image_preview_sent) { gst_droidcamsrc_post_message (src, gst_structure_new_empty (GST_DROIDCAMSRC_CAPTURE_END)); /* TODO: generate and send preview if we don't get it from HAL */ dev->img->image_preview_sent = TRUE; } gst_droidcamsrc_timestamp (src, buffer); tags = gst_droidcamsrc_exif_tags_from_jpeg_data (d, size); if (tags) { GST_INFO_OBJECT (src, "pushing tags %" GST_PTR_FORMAT, tags); event = gst_event_new_tag (tags); } g_mutex_lock (&dev->imgsrc->lock); // TODO: get the correct lock if (event) { src->imgsrc->pending_events = g_list_append (src->imgsrc->pending_events, event); } g_queue_push_tail (dev->imgsrc->queue, buffer); g_cond_signal (&dev->imgsrc->cond); g_mutex_unlock (&dev->imgsrc->lock); /* we need to start restart the preview * android demands this but GStreamer does not know about it. */ g_rec_mutex_lock (dev->lock); dev->running = FALSE; g_rec_mutex_unlock (dev->lock); gst_droidcamsrc_dev_start (dev, TRUE); g_mutex_lock (&src->capture_lock); --src->captures; g_mutex_unlock (&src->capture_lock); g_object_notify (G_OBJECT (src), "ready-for-capture"); }
static int gst_droidcamsrc_stream_window_enqueue_buffer (struct preview_stream_ops *w, buffer_handle_t * buffer) { GstDroidCamSrcStreamWindow *win; GstDroidCamSrc *src; GstBuffer *buff; int ret; GstVideoCropMeta *meta; GST_DEBUG ("enqueue buffer %p", buffer); win = container_of (w, GstDroidCamSrcStreamWindow, window); g_mutex_lock (&win->lock); src = GST_DROIDCAMSRC (GST_PAD_PARENT (win->pad->pad)); buff = gst_droidcamsrc_stream_window_get_buffer (buffer); if (!buff) { GST_ERROR ("no buffer corresponding to handle %p", buffer); ret = -1; goto unlock_and_out; } /* if the buffer pool is not our current pool then just release it */ if (buff->pool != GST_BUFFER_POOL (win->pool)) { GST_DEBUG ("releasing old buffer %p", buffer); gst_buffer_unref (buff); ret = 0; goto unlock_and_out; } /* now update crop meta */ meta = gst_buffer_get_video_crop_meta (buff); meta->x = win->left; meta->y = win->top; meta->width = win->right - win->left; meta->height = win->bottom - win->top; GST_LOG ("window width = %d, height = %d, crop info: left = %d, top = %d, right = %d, bottom = %d", win->width, win->height, win->left, win->top, win->right, win->bottom); g_mutex_unlock (&win->lock); /* it should be safe to access that variable without locking. * pad gets activated during READY_TO_PAUSED and deactivated during * PAUSED_TO_READY while we start the preview during PAUSED_TO_PLAYING * and stop it during PLAYING_TO_PAUSED. */ if (!win->pad->running) { gst_buffer_unref (buff); GST_DEBUG ("unreffing buffer because pad task is not running"); ret = 0; goto unlock_pad_and_out; } // TODO: duration, offset, offset_end ... gst_droidcamsrc_timestamp (src, buff); g_mutex_lock (&win->pad->queue_lock); g_queue_push_tail (win->pad->queue, buff); g_cond_signal (&win->pad->cond); ret = 0; goto unlock_pad_and_out; unlock_and_out: g_mutex_unlock (&win->lock); return ret; unlock_pad_and_out: g_mutex_unlock (&win->pad->queue_lock); return ret; }
static void gst_droidcamsrc_dev_video_frame_callback (void *user, DroidMediaCameraRecordingData * video_data) { GstDroidCamSrcDev *dev = (GstDroidCamSrcDev *) user; GstDroidCamSrc *src = GST_DROIDCAMSRC (GST_PAD_PARENT (dev->imgsrc->pad)); void *data = droid_media_camera_recording_frame_get_data (video_data); GstBuffer *buffer; GstMemory *mem; GstDroidCamSrcDevVideoData *mem_data; gboolean drop_buffer; GST_DEBUG_OBJECT (src, "dev video frame callback"); g_mutex_lock (&dev->vid->lock); /* TODO: not sure what to do with timestamp */ /* unlikely but just in case */ if (G_UNLIKELY (!data)) { GST_ERROR ("invalid memory from camera HAL"); droid_media_camera_release_recording_frame (dev->cam, video_data); goto unlock_and_out; } /* TODO: this is bad */ mem_data = g_slice_new0 (GstDroidCamSrcDevVideoData); mem_data->dev = dev; mem_data->data = video_data; buffer = gst_buffer_new (); mem = gst_wrapped_memory_allocator_wrap (dev->wrap_allocator, data, droid_media_camera_recording_frame_get_size (video_data), (GFunc) gst_droidcamsrc_dev_release_recording_frame, mem_data); gst_buffer_insert_memory (buffer, 0, mem); GST_BUFFER_OFFSET (buffer) = dev->vid->video_frames; GST_BUFFER_OFFSET_END (buffer) = ++dev->vid->video_frames; gst_droidcamsrc_timestamp (src, buffer); g_rec_mutex_lock (dev->lock); ++dev->vid->queued_frames; g_rec_mutex_unlock (dev->lock); drop_buffer = !dev->vid->running; if (drop_buffer) { GST_INFO_OBJECT (src, "dropping buffer because video recording is not running"); gst_buffer_unref (buffer); } else { g_mutex_lock (&dev->vidsrc->lock); g_queue_push_tail (dev->vidsrc->queue, buffer); g_cond_signal (&dev->vidsrc->cond); g_mutex_unlock (&dev->vidsrc->lock); } unlock_and_out: /* in case stop_video_recording() is waiting for us */ g_cond_signal (&dev->vid->cond); g_mutex_unlock (&dev->vid->lock); }