[M108 Migration] ImageCapture support for video capture device. 73/290273/3
authorSun-woo Nam <sunny.nam@samsung.com>
Wed, 22 Mar 2023 07:20:37 +0000 (12:50 +0530)
committerBot Blink <blinkbot@samsung.com>
Thu, 23 Mar 2023 13:08:08 +0000 (13:08 +0000)
- ImageCapture support for video capture device.
- Don't expose CAPI camera handle.
- Re-select Camera Preview Format.

Migrated from:
https://review.tizen.org/gerrit/#/c/279886/

Change-Id: I31470b8471ddef942655df59d2202237bd1ab863
Signed-off-by: Suhaspoornachandra <s.poornachan@samsung.com>
media/capture/mojom/BUILD.gn
media/capture/mojom/video_capture_types.mojom
media/capture/mojom/video_capture_types_mojom_traits.cc
media/capture/mojom/video_capture_types_mojom_traits.h
media/capture/video_capture_types.h
tizen_src/chromium_impl/media/capture/video/tizen/camera_device_tizen.cc
tizen_src/chromium_impl/media/capture/video/tizen/camera_device_tizen.h
tizen_src/chromium_impl/media/capture/video/tizen/video_capture_device_factory_tizen.cc
tizen_src/chromium_impl/media/capture/video/tizen/video_capture_device_tizen.cc
tizen_src/chromium_impl/media/capture/video/tizen/video_capture_device_tizen.h

index 65cd420..9f0823f 100644 (file)
@@ -9,6 +9,10 @@ if (use_aura) {
 }
 
 enabled_features = []
+if (tizen_multimedia) {
+  enabled_features += [ "tizen_multimedia" ]
+}
+
 if (tizen_tbm_support) {
   enabled_features += [ "tizen_tbm_support" ]
 }
index e58304e..8b23d5e 100644 (file)
@@ -295,6 +295,8 @@ struct VideoCaptureParams {
   ResolutionChangePolicy resolution_change_policy;
   PowerLineFrequency power_line_frequency;
   bool enable_face_detection;
+  [EnableIf=tizen_multimedia]
+  bool lazy_start;
 };
 
 [Stable, RenamedFrom="media.mojom.VideoFrameFeedback"]
index aa05fa7..be79c50 100644 (file)
@@ -1821,6 +1821,9 @@ bool StructTraits<media::mojom::VideoCaptureParamsDataView,
   if (!data.ReadPowerLineFrequency(&out->power_line_frequency))
     return false;
   out->enable_face_detection = data.enable_face_detection();
+#if defined(TIZEN_MULTIMEDIA)
+  out->lazy_start = data.lazy_start();
+#endif
   return true;
 }
 
index b0e4c82..51f8c41 100644 (file)
@@ -172,6 +172,12 @@ struct COMPONENT_EXPORT(MEDIA_CAPTURE_MOJOM_TRAITS)
     return params.enable_face_detection;
   }
 
+#if defined(TIZEN_MULTIMEDIA)
+  static bool lazy_start(const media::VideoCaptureParams& params) {
+    return params.lazy_start;
+  }
+#endif
+
   static bool Read(media::mojom::VideoCaptureParamsDataView data,
                    media::VideoCaptureParams* out);
 };
index e6d8961..9f48d90 100644 (file)
@@ -333,6 +333,9 @@ struct CAPTURE_EXPORT VideoCaptureParams {
   // exposures around the face area. Currently only applicable on
   // Android platform with Camera2 driver support.
   bool enable_face_detection;
+#if defined(TIZEN_MULTIMEDIA)
+  bool lazy_start{false};
+#endif
 };
 
 }  // namespace media
index 703389c..65356f6 100644 (file)
@@ -6,12 +6,16 @@
 
 #include "media/capture/video/tizen/video_capture_device_tizen.h"
 
-namespace media {
+namespace {
 
 enum { kMaxWidth = 1280 };
 enum { kMaxHeight = 720 };
 enum { kMaxFramerate = CAMERA_ATTR_FPS_30 };
 
+constexpr int kMinIso = 50;
+constexpr int kMaxIso = 3200;
+constexpr int kStepIso = 50;
+
 media::VideoPixelFormat toChromiumType(camera_pixel_format_e e) {
   switch (e) {
     case CAMERA_PIXEL_FORMAT_NV12:
@@ -33,11 +37,55 @@ media::VideoPixelFormat toChromiumType(camera_pixel_format_e e) {
     case CAMERA_PIXEL_FORMAT_JPEG:
       return media::PIXEL_FORMAT_MJPEG;
     default:
-      NOTREACHED();
+      NOTREACHED() << "Unknown format #" << e;
   }
   return media::PIXEL_FORMAT_UNKNOWN;
 }
 
+int FromIsoToInteger(camera_attr_iso_e mode) {
+  switch (mode) {
+    case CAMERA_ATTR_ISO_50:
+      return 50;
+    case CAMERA_ATTR_ISO_100:
+      return 100;
+    case CAMERA_ATTR_ISO_200:
+      return 200;
+    case CAMERA_ATTR_ISO_400:
+      return 400;
+    case CAMERA_ATTR_ISO_800:
+      return 800;
+    case CAMERA_ATTR_ISO_1600:
+      return 1600;
+    case CAMERA_ATTR_ISO_3200:
+      return 3200;
+
+    // return minimum value in default case
+    default:
+      LOG(ERROR) << "Unknown mode (" << mode << ") Returning " << kMinIso;
+      return kMinIso;
+  }
+}
+
+camera_attr_iso_e FromIntegerToCapiIsoFormat(unsigned int value) {
+  if (value == kMinIso)
+    return CAMERA_ATTR_ISO_50;
+  if (value == 100)
+    return CAMERA_ATTR_ISO_100;
+  if (value == 200)
+    return CAMERA_ATTR_ISO_200;
+  if (value == 400)
+    return CAMERA_ATTR_ISO_400;
+  if (value == 800)
+    return CAMERA_ATTR_ISO_800;
+  if (value == 1600)
+    return CAMERA_ATTR_ISO_1600;
+  if (value == kMaxIso)
+    return CAMERA_ATTR_ISO_3200;
+
+  LOG(ERROR) << "Unknown value (" << value << ") Returning ISO Auto";
+  return CAMERA_ATTR_ISO_AUTO;
+}
+
 bool OnCameraSupportedPreviewResolution(int width,
                                         int height,
                                         void* user_data) {
@@ -98,35 +146,133 @@ void GenerateChromiumVideoCaptureFormat(
   }
 }
 
-CameraHandle::CameraHandle() : camera_handle_(NULL), client_(NULL) {
+}  // anonymous namespace
+
+namespace media {
+
+mojom::RangePtr SetRange(double min, double max, double current, double step) {
+  mojom::RangePtr range_ptr = mojom::Range::New();
+  range_ptr->min = min;
+  range_ptr->max = max;
+  range_ptr->current = current;
+  range_ptr->step = step;
+  return range_ptr;
+}
+
+mojom::RedEyeReduction PhotoCapabilities::GetRedEyeReduction() {
+  camera_attr_flash_mode_e mode = CAMERA_ATTR_FLASH_MODE_OFF;
+  camera_attr_get_flash_mode(camera_, &mode);
+  if (mode != CAMERA_ATTR_FLASH_MODE_REDEYE_REDUCTION)
+    return mojom::RedEyeReduction::CONTROLLABLE;
+  return mojom::RedEyeReduction::NEVER;
+}
+
+mojom::RangePtr PhotoCapabilities::GetExposureCompensation() {
+  if (exposureCompensation_.min == -1)
+    camera_attr_get_exposure_range(camera_, &exposureCompensation_.min,
+                                   &exposureCompensation_.max);
+
+  if (exposureCompensation_.min != -1) {
+    int current = -1;
+    if (CAMERA_ERROR_NONE == camera_attr_get_exposure(camera_, &current))
+      return SetRange(exposureCompensation_.min, exposureCompensation_.max,
+                      current, 1.0);
+  }
+  return mojom::Range::New();
+}
+
+mojom::RangePtr PhotoCapabilities::GetIso() {
+  camera_attr_iso_e current_iso = CAMERA_ATTR_ISO_AUTO;
+  if (CAMERA_ERROR_NONE == camera_attr_get_iso(camera_, &current_iso))
+    return SetRange(kMinIso, kMaxIso, FromIsoToInteger(current_iso), kStepIso);
+
+  return mojom::Range::New();
+}
+
+mojom::RangePtr PhotoCapabilities::GetBrightness() {
+  if (brightness_.min == -1)
+    camera_attr_get_brightness_range(camera_, &brightness_.min,
+                                     &brightness_.max);
+
+  if (brightness_.min != -1) {
+    int current = -1;
+    if (CAMERA_ERROR_NONE == camera_attr_get_brightness(camera_, &current))
+      return SetRange(brightness_.min, brightness_.max, current, 1.0);
+  }
+  return mojom::Range::New();
+}
+
+mojom::RangePtr PhotoCapabilities::GetContrast() {
+  if (contrast_.min == -1)
+    camera_attr_get_contrast_range(camera_, &contrast_.min, &contrast_.max);
+
+  if (contrast_.min != -1) {
+    int current = -1;
+    if (CAMERA_ERROR_NONE == camera_attr_get_contrast(camera_, &current))
+      return SetRange(contrast_.min, contrast_.max, current, 1.0);
+  }
+  return mojom::Range::New();
+}
+
+mojom::RangePtr PhotoCapabilities::GetImageHeight() {
+  // TODO: Remove hardcoding height and width values
+  return SetRange(480, 480, 480, 0.0);
+}
+
+mojom::RangePtr PhotoCapabilities::GetImageWidth() {
+  return SetRange(640, 640, 640, 0.0);
+}
+
+mojom::RangePtr PhotoCapabilities::GetZoom() {
+  if (zoom_.min == -1)
+    camera_attr_get_zoom_range(camera_, &zoom_.min, &zoom_.max);
+
+  if (zoom_.min != -1) {
+    int current = -1;
+    if (CAMERA_ERROR_NONE == camera_attr_get_zoom(camera_, &current))
+      return SetRange(zoom_.min, zoom_.max, current, 1.0);
+  }
+  return mojom::Range::New();
+}
+
+CameraHandle::CameraHandle() : camera_handle_(nullptr), client_(nullptr) {
   LOG(INFO) << "Creating the camera instance";
   device_name_ = CAMERA_DEVICE_CAMERA0;
   ResetHandle(device_name_);
 }
 
 CameraHandle::~CameraHandle() {
-  if (camera_handle_ != NULL)
+  if (camera_handle_ != nullptr) {
     camera_destroy(camera_handle_);
+    camera_handle_ = nullptr;
+  }
 }
 
 void CameraHandle::ResetHandle(camera_device_e device_name) {
-  if (camera_handle_ != NULL) {
-    if (client_ != NULL)
+  int err = 0;
+  if (camera_handle_ != nullptr) {
+    if (client_ != nullptr)
       client_->OnStreamStopped();
 
-    camera_destroy(camera_handle_);
-    camera_handle_ = NULL;
+    if (CAMERA_ERROR_NONE != (err = camera_destroy(camera_handle_))) {
+      LOG(ERROR) << "camera_destroy error : "
+                 << media::VideoCaptureDeviceTizen::GetErrorString(err);
+    }
+
+    camera_handle_ = nullptr;
   }
 
-  int err = 0;
   if (CAMERA_ERROR_NONE !=
       (err = camera_create(device_name, &camera_handle_))) {
-    camera_handle_ = NULL;
+    camera_handle_ = nullptr;
     LOG(ERROR) << "Cannot create camera, Error:"
                << media::VideoCaptureDeviceTizen::GetErrorString(err);
+    return;
   } else {
     device_name_ = device_name;
   }
+
+  capabilities_.reset(new PhotoCapabilities(camera_handle_));
 }
 
 void CameraHandle::SetClient(CameraHandleClient* client) {
@@ -135,7 +281,7 @@ void CameraHandle::SetClient(CameraHandleClient* client) {
 
 void CameraHandle::UnsetClient(CameraHandleClient* client) {
   if (client_ == client)
-    client_ = NULL;
+    client_ = nullptr;
 }
 
 CameraHandle* CameraHandle::GetInstance() {
@@ -143,7 +289,7 @@ CameraHandle* CameraHandle::GetInstance() {
 }
 
 bool CameraHandle::IsValid() {
-  return camera_handle_ != NULL;
+  return camera_handle_ != nullptr;
 }
 
 bool CameraHandle::GetSupportedPreviewResolutions(
@@ -251,4 +397,94 @@ int CameraHandle::GetDeviceCounts() {
   return device_count;
 }
 
+// Set Photo Capabilities
+bool CameraHandle::SetZoom(int value) {
+  return CAMERA_ERROR_NONE == camera_attr_set_zoom(camera_handle_, value);
+}
+
+bool CameraHandle::SetResolution(int width, int height) {
+  return CAMERA_ERROR_NONE ==
+         camera_set_capture_resolution(camera_handle_, width, height);
+}
+
+bool CameraHandle::SetBrightness(int value) {
+  return CAMERA_ERROR_NONE == camera_attr_set_brightness(camera_handle_, value);
+}
+
+bool CameraHandle::SetContrast(int value) {
+  return CAMERA_ERROR_NONE == camera_attr_set_contrast(camera_handle_, value);
+}
+
+bool CameraHandle::SetIso(int value) {
+  return CAMERA_ERROR_NONE ==
+         camera_attr_set_iso(camera_handle_, FromIntegerToCapiIsoFormat(value));
+}
+
+bool CameraHandle::SetExposure(int value) {
+  return CAMERA_ERROR_NONE == camera_attr_set_exposure(camera_handle_, value);
+}
+
+int CameraHandle::CameraStartPreview() {
+  return camera_start_preview(camera_handle_);
+}
+
+int CameraHandle::CameraStopPreview() {
+  return camera_stop_preview(camera_handle_);
+}
+
+camera_attr_fps_e CameraHandle::CameraGetPreviewFps() {
+  camera_attr_fps_e current_fps = static_cast<camera_attr_fps_e>(30);
+  camera_attr_get_preview_fps(camera_handle_, &current_fps);
+  return current_fps;
+}
+
+int CameraHandle::CameraSetDisplay(camera_display_type_e type,
+                                   camera_display_h display) {
+  return camera_set_display(camera_handle_, type, display);
+}
+
+int CameraHandle::CameraSetCaptureFormat(camera_pixel_format_e format) {
+  return camera_set_capture_format(camera_handle_, format);
+}
+
+int CameraHandle::CameraStartCapture(camera_capturing_cb capturingCB,
+                                     camera_capture_completed_cb completedCB,
+                                     void* data) {
+  return camera_start_capture(camera_handle_, capturingCB, completedCB, data);
+}
+
+int CameraHandle::CameraSetPreviewResolution(int width, int height) {
+  return camera_set_preview_resolution(camera_handle_, width, height);
+}
+
+int CameraHandle::CameraSetPreviewFormat(camera_pixel_format_e format) {
+  return camera_set_preview_format(camera_handle_, format);
+}
+
+int CameraHandle::CameraSetPreviewFps(camera_attr_fps_e fps) {
+  return camera_attr_set_preview_fps(camera_handle_, fps);
+}
+
+int CameraHandle::CameraSetPreviewCb(camera_preview_cb callback, void* data) {
+  return camera_set_preview_cb(camera_handle_, callback, data);
+}
+
+int CameraHandle::CameraUnsetPreviewCb() {
+  return camera_unset_preview_cb(camera_handle_);
+}
+
+int CameraHandle::CameraSetMediapacketPreviewCb(
+    camera_media_packet_preview_cb callback,
+    void* data) {
+  return camera_set_media_packet_preview_cb(camera_handle_, callback, data);
+}
+
+int CameraHandle::CameraUnsetMediapacketPreviewCb() {
+  return camera_unset_media_packet_preview_cb(camera_handle_);
+}
+
+int CameraHandle::CameraSetStreamFlip(camera_flip_e flip) {
+  return camera_attr_set_stream_flip(camera_handle_, flip);
+}
+
 }  // namespace media
index f649d04..91c6dda 100644 (file)
 
 #include "base/memory/singleton.h"
 #include "media/base/video_types.h"
+#include "media/capture/mojom/image_capture.mojom.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
+using media::mojom::MeteringMode;
+
 namespace media {
 
 struct VideoCaptureFormat;
@@ -29,6 +32,42 @@ struct CameraCapability {
   Fps fps;
 };
 
+struct Range {
+  int min = -1;
+  int max = -1;
+};
+
+class PhotoCapabilities {
+ public:
+  mojom::RedEyeReduction GetRedEyeReduction();
+  mojom::MeteringMode GetWhiteBalanceMode() {
+    return mojom::MeteringMode::NONE;
+  }
+  mojom::MeteringMode GetExposureMode() { return MeteringMode::NONE; }
+  mojom::MeteringMode GetFocusMode() { return MeteringMode::NONE; }
+  mojom::RangePtr GetColorTemperature() { return mojom::Range::New(); }
+  mojom::RangePtr GetExposureCompensation();
+  mojom::RangePtr GetIso();
+  mojom::RangePtr GetBrightness();
+  mojom::RangePtr GetContrast();
+  mojom::RangePtr GetSaturation() { return mojom::Range::New(); }
+  mojom::RangePtr GetSharpness() { return mojom::Range::New(); }
+  mojom::RangePtr GetImageHeight();
+  mojom::RangePtr GetImageWidth();
+  mojom::RangePtr GetZoom();
+
+ private:
+  PhotoCapabilities(camera_h camera) : camera_(camera) {}
+
+  camera_h camera_;
+  Range exposureCompensation_;
+  Range brightness_;
+  Range contrast_;
+  Range zoom_;
+
+  friend class CameraHandle;
+};
+
 class CameraHandleClient {
  public:
   virtual void OnStreamStopped() {}
@@ -41,7 +80,6 @@ class CameraHandle final {
  public:
   static CameraHandle* GetInstance();
 
-  camera_h GetCameraHandle() { return camera_handle_; }
   camera_device_e GetDeviceName() { return device_name_; }
 
   bool IsValid();
@@ -54,6 +92,43 @@ class CameraHandle final {
   void SetClient(CameraHandleClient* client);
   void UnsetClient(CameraHandleClient* client);
 
+  // Set Photo Capabilities
+  bool SetZoom(int value);
+  bool SetResolution(int width, int height);
+  bool SetBrightness(int value);
+  bool SetContrast(int value);
+  bool SetIso(int value);
+  bool SetExposure(int value);
+
+  std::unique_ptr<PhotoCapabilities> capabilities_;
+
+  int CameraStartCapture(camera_capturing_cb capturingCB,
+                         camera_capture_completed_cb completedCB,
+                         void* data);
+  int CameraSetCaptureFormat(camera_pixel_format_e format);
+  int CameraSetCaptureResolution(int width, int height);
+
+  // camera apis
+  int CameraStartPreview();
+  int CameraStopPreview();
+  int CameraSetStreamFlip(camera_flip_e flip);
+
+  // getter
+  camera_attr_fps_e CameraGetPreviewFps();
+
+  // setter
+  int CameraSetDisplay(camera_display_type_e type, camera_display_h display);
+  int CameraSetPreviewResolution(int width, int height);
+  int CameraSetPreviewFormat(camera_pixel_format_e format);
+  int CameraSetPreviewFps(camera_attr_fps_e fps);
+
+  // callbacks
+  int CameraSetPreviewCb(camera_preview_cb callback, void* data);
+  int CameraUnsetPreviewCb();
+  int CameraSetMediapacketPreviewCb(camera_media_packet_preview_cb callback,
+                                    void* data);
+  int CameraUnsetMediapacketPreviewCb();
+
  private:
   CameraHandle();
   ~CameraHandle();
index aa935bb..b3f3bf3 100644 (file)
@@ -33,13 +33,11 @@ void VideoCaptureDeviceFactoryTizen::GetDevicesInfo(
     GetDevicesInfoCallback callback) {
   std::vector<VideoCaptureDeviceInfo> devices_info;
   CameraHandle* camera_handle = CameraHandle::GetInstance();
-  LOG(INFO) << "Received the camera handle:"
-            << camera_handle->GetCameraHandle();
-
-  if (!camera_handle->IsValid()) {
+  if (camera_handle == nullptr || !camera_handle->IsValid()) {
     LOG(ERROR) << "Cannot use camera";
     return;
   }
+  LOG(INFO) << "Received the camera handle:" << camera_handle;
 
   int device_count = camera_handle->GetDeviceCounts();
   if (device_count == 0) {
index 85251ff..e57ee7a 100644 (file)
@@ -5,6 +5,8 @@
 #include "media/capture/video/tizen/video_capture_device_tizen.h"
 
 #include "base/bind.h"
+#include "media/base/video_color_space.h"
+#include "media/capture/mojom/image_capture_types.h"
 #include "third_party/libyuv/include/libyuv.h"
 #include "tizen/system_info.h"
 #include "ui/display/display.h"
@@ -19,7 +21,6 @@ namespace {
 
 enum { kMjpegWidth = 640 };
 enum { kMjpegHeight = 480 };
-enum { kTypicalFramerate = 30 };
 enum CameraOrientation {
   DEGREE_0 = 0,
   DEGREE_90 = 90,
@@ -28,7 +29,7 @@ enum CameraOrientation {
   DEGREE_360 = 360,
 };
 
-camera_pixel_format_e toCapiType(media::VideoPixelFormat e) {
+camera_pixel_format_e ToCapiType(media::VideoPixelFormat e) {
   switch (e) {
     case media::PIXEL_FORMAT_NV12:
       return CAMERA_PIXEL_FORMAT_NV12;
@@ -51,6 +52,7 @@ camera_pixel_format_e toCapiType(media::VideoPixelFormat e) {
     default:
       NOTREACHED();
   }
+  LOG(ERROR) << __func__ << " " << e;
   return CAMERA_PIXEL_FORMAT_INVALID;
 }
 
@@ -116,7 +118,7 @@ static CameraOrientation GetCameraOrientation(const char* device_id) {
 
 VideoCaptureDeviceTizen::VideoCaptureDeviceTizen(
     const VideoCaptureDeviceDescriptor& device_descriptor)
-    : camera_(nullptr),
+    : camera_instance_(nullptr),
       buffer_(),
       device_descriptor_(device_descriptor),
       worker_("VideoCapture"),
@@ -126,15 +128,15 @@ VideoCaptureDeviceTizen::VideoCaptureDeviceTizen(
 #if TIZEN_MM_DEBUG_VIDEO_DUMP_DECODED_FRAME == 1
   frameDumper = new FrameDumper();
 #endif
-
-  if (IsMobileProfile())
-    use_media_packet_ = true;
 }
 
 VideoCaptureDeviceTizen::~VideoCaptureDeviceTizen() {
-  CameraHandle::GetInstance()->UnsetClient(this);
+  if (camera_instance_ != nullptr)
+    camera_instance_->UnsetClient(this);
+
   state_ = kIdle;
   DCHECK(!worker_.IsRunning());
+  photo_callbacks_.clear();
 }
 
 void VideoCaptureDeviceTizen::OnStreamStopped() {
@@ -147,13 +149,13 @@ void VideoCaptureDeviceTizen::AllocateAndStart(
   DCHECK(!worker_.IsRunning());
   worker_.Start();
   worker_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VideoCaptureDeviceTizen::OnAllocateAndStart,
-                     base::Unretained(this),
-                     params.requested_format.frame_size.width(),
-                     params.requested_format.frame_size.height(),
-                     params.requested_format.frame_rate,
-                     params.requested_format.pixel_format, std::move(client)));
+      FROM_HERE, base::BindOnce(&VideoCaptureDeviceTizen::OnAllocateAndStart,
+                                base::Unretained(this),
+                                params.requested_format.frame_size.width(),
+                                params.requested_format.frame_size.height(),
+                                params.requested_format.frame_rate,
+                                params.requested_format.pixel_format,
+                                params.lazy_start, std::move(client)));
 }
 
 void VideoCaptureDeviceTizen::StopAndDeAllocate() {
@@ -164,6 +166,91 @@ void VideoCaptureDeviceTizen::StopAndDeAllocate() {
   worker_.Stop();
 }
 
+void VideoCaptureDeviceTizen::OnGetPhotoState(GetPhotoStateCallback callback) {
+  DCHECK(worker_.task_runner()->BelongsToCurrentThread());
+
+  mojom::PhotoStatePtr photo_capabilities = mojo::CreateEmptyPhotoState();
+
+  photo_capabilities->iso = camera_instance_->capabilities_->GetIso();
+  photo_capabilities->height =
+      camera_instance_->capabilities_->GetImageHeight();
+  photo_capabilities->width = camera_instance_->capabilities_->GetImageWidth();
+  photo_capabilities->zoom = camera_instance_->capabilities_->GetZoom();
+  photo_capabilities->current_focus_mode =
+      camera_instance_->capabilities_->GetFocusMode();
+  photo_capabilities->current_exposure_mode =
+      camera_instance_->capabilities_->GetExposureMode();
+  photo_capabilities->exposure_compensation =
+      camera_instance_->capabilities_->GetExposureCompensation();
+  photo_capabilities->current_white_balance_mode =
+      camera_instance_->capabilities_->GetWhiteBalanceMode();
+  photo_capabilities->red_eye_reduction =
+      camera_instance_->capabilities_->GetRedEyeReduction();
+  photo_capabilities->color_temperature =
+      camera_instance_->capabilities_->GetColorTemperature();
+  photo_capabilities->brightness =
+      camera_instance_->capabilities_->GetBrightness();
+  photo_capabilities->contrast = camera_instance_->capabilities_->GetContrast();
+  photo_capabilities->saturation =
+      camera_instance_->capabilities_->GetSaturation();
+  photo_capabilities->sharpness =
+      camera_instance_->capabilities_->GetSharpness();
+  std::move(callback).Run(std::move(photo_capabilities));
+}
+
+void VideoCaptureDeviceTizen::TakePhoto(TakePhotoCallback callback) {
+  int err = 0;
+  if (CAMERA_ERROR_NONE != (err = camera_instance_->CameraSetCaptureFormat(
+                                CAMERA_PIXEL_FORMAT_JPEG))) {
+    SetErrorState(GetErrorString(err), FROM_HERE);
+    return;
+  }
+
+  std::unique_ptr<TakePhotoCallback> heap_callback(
+      new TakePhotoCallback(std::move(callback)));
+
+  if (CAMERA_ERROR_NONE !=
+      (err = camera_instance_->CameraStartCapture(
+           VideoCaptureDeviceTizen::CapturedCb,
+           VideoCaptureDeviceTizen::CaptureCompletedCb, this))) {
+    SetErrorState(GetErrorString(err), FROM_HERE);
+    return;
+  }
+  photo_callbacks_.push_back(std::move(heap_callback));
+}
+
+void VideoCaptureDeviceTizen::SetPhotoOptions(
+    mojom::PhotoSettingsPtr settings,
+    SetPhotoOptionsCallback callback) {
+  DCHECK(worker_.IsRunning());
+  worker_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&VideoCaptureDeviceTizen::OnSetPhotoOptions,
+                                base::Unretained(this), std::move(settings),
+                                std::move(callback)));
+}
+
+void VideoCaptureDeviceTizen::OnSetPhotoOptions(
+    mojom::PhotoSettingsPtr settings,
+    SetPhotoOptionsCallback callback) {
+  DCHECK(worker_.task_runner()->BelongsToCurrentThread());
+
+  if (SetCameraZoom(settings) && SetCameraResolution(settings) &&
+      SetCameraExposure(settings) && SetCameraBrightness(settings) &&
+      SetCameraContrast(settings) && SetCameraIso(settings)) {
+    std::move(callback).Run(true);
+  } else {
+    LOG(ERROR) << "Fail to set camera properties.";
+    std::move(callback).Run(false);
+  }
+}
+
+void VideoCaptureDeviceTizen::GetPhotoState(GetPhotoStateCallback callback) {
+  DCHECK(worker_.IsRunning());
+  worker_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&VideoCaptureDeviceTizen::OnGetPhotoState,
+                                base::Unretained(this), std::move(callback)));
+}
+
 camera_device_e VideoCaptureDeviceTizen::DeviceNameToCameraId(
     const VideoCaptureDeviceDescriptor& device_descriptor) {
   if (device_descriptor.device_id.compare("0") == 0) {
@@ -215,19 +302,15 @@ void VideoCaptureDeviceTizen::PreviewCameraCaptured(ScopedMediaPacket pkt) {
   format.reset(fmt);
 
   media_format_mimetype_e mime;
-  int width, height, avg_bps, max_bps;
+  int width, height, avg_bps, max_bps, orientation;
 
   media_format_get_video_info(format.get(), &mime, &width, &height, &avg_bps,
                               &max_bps);
-  CHECK((mime == MEDIA_FORMAT_I420) || (mime == MEDIA_FORMAT_NV12));
-
-  camera_attr_fps_e current_fps =
-      static_cast<camera_attr_fps_e>(kTypicalFramerate);
-  camera_attr_get_preview_fps(camera_, &current_fps);
+  CHECK((mime == MEDIA_FORMAT_I420) || (mime == MEDIA_FORMAT_NV12) ||
+        (mime == MEDIA_FORMAT_H264_SP));
 
-  int orientation =
+  orientation =
       display::Screen::GetScreen()->GetPrimaryDisplay().RotationAsDegree();
-
   gfx::Size target_resolution(width, height);
 
   int target_rotation =
@@ -240,8 +323,10 @@ void VideoCaptureDeviceTizen::PreviewCameraCaptured(ScopedMediaPacket pkt) {
 
   media::VideoCaptureFormat videocaptureformat;
   videocaptureformat.frame_size = gfx::Size(width, height);
-  videocaptureformat.frame_rate = static_cast<int>(current_fps);
-  videocaptureformat.pixel_format = media::PIXEL_FORMAT_I420;
+  videocaptureformat.frame_rate = frame_rate_;
+  videocaptureformat.pixel_format =
+      (mime == MEDIA_FORMAT_H264_SP ? media::PIXEL_FORMAT_ENCODED
+                                    : media::PIXEL_FORMAT_I420);
 
   int dest_width = width;
   int dest_height = height;
@@ -329,16 +414,14 @@ void VideoCaptureDeviceTizen::OnCameraCaptured(camera_preview_data_s* frame,
   VideoCaptureDeviceTizen* self = static_cast<VideoCaptureDeviceTizen*>(data);
   if (!self->client_)
     return;
-  camera_attr_fps_e current_fps =
-      static_cast<camera_attr_fps_e>(kTypicalFramerate);
-  camera_attr_get_preview_fps(self->camera_, &current_fps);
 
-  DVLOG(3) << " width:" << frame->width << " height:" << frame->height;
-
-  const display::Display display =
-      display::Screen::GetScreen()->GetPrimaryDisplay();
+  int orientation = DEGREE_0;
+  display::Screen* screen = display::Screen::GetScreen();
+  if (screen) {
+    display::Display Display = screen->GetPrimaryDisplay();
+    orientation = Display.RotationAsDegree();
+  }
 
-  int orientation = display.RotationAsDegree();
   gfx::Size target_resolution(frame->width, frame->height);
   int target_rotation =
       (orientation +
@@ -350,7 +433,7 @@ void VideoCaptureDeviceTizen::OnCameraCaptured(camera_preview_data_s* frame,
 
   media::VideoCaptureFormat videocaptureformat;
   videocaptureformat.frame_size = gfx::Size(frame->width, frame->height);
-  videocaptureformat.frame_rate = static_cast<int>(current_fps);
+  videocaptureformat.frame_rate = self->frame_rate_;
   videocaptureformat.pixel_format = media::PIXEL_FORMAT_I420;
 
   int dest_width = frame->width;
@@ -415,64 +498,132 @@ void VideoCaptureDeviceTizen::OnCameraCaptured(camera_preview_data_s* frame,
                                           now - self->first_ref_time_);
 }
 
+bool VideoCaptureDeviceTizen::SetCameraZoom(
+    const mojom::PhotoSettingsPtr& settings) {
+  if (settings->has_zoom)
+    return camera_instance_->SetZoom(settings->zoom);
+  return true;
+}
+
+bool VideoCaptureDeviceTizen::SetCameraResolution(
+    const mojom::PhotoSettingsPtr& settings) {
+  const size_t DEFAULT_WIDTH = 640;
+  const size_t DEFAULT_HEIGHT = 480;
+  const int width = settings->has_width ? settings->width : DEFAULT_WIDTH;
+  const int height = settings->has_height ? settings->height : DEFAULT_HEIGHT;
+  return camera_instance_->SetResolution(width, height);
+}
+
+bool VideoCaptureDeviceTizen::SetCameraExposure(
+    const mojom::PhotoSettingsPtr& settings) {
+  if (settings->has_exposure_compensation)
+    return camera_instance_->SetExposure(settings->exposure_compensation);
+  return true;
+}
+
+bool VideoCaptureDeviceTizen::SetCameraBrightness(
+    const mojom::PhotoSettingsPtr& settings) {
+  if (settings->has_brightness)
+    return camera_instance_->SetBrightness(settings->brightness);
+  return true;
+}
+
+bool VideoCaptureDeviceTizen::SetCameraContrast(
+    const mojom::PhotoSettingsPtr& settings) {
+  if (settings->has_contrast)
+    return camera_instance_->SetContrast(settings->contrast);
+  return true;
+}
+
+bool VideoCaptureDeviceTizen::SetCameraIso(
+    const mojom::PhotoSettingsPtr& settings) {
+  if (settings->has_iso)
+    return camera_instance_->SetIso(settings->iso);
+  return true;
+}
+
+VideoPixelFormat VideoCaptureDeviceTizen::ReselectCameraPreviewFormat(
+    const std::vector<VideoPixelFormat>& support_formats) {
+  if (std::find(support_formats.begin(), support_formats.end(),
+                PIXEL_FORMAT_ENCODED) != support_formats.end())
+    return PIXEL_FORMAT_ENCODED;
+  else if (std::find(support_formats.begin(), support_formats.end(),
+                     PIXEL_FORMAT_MJPEG) != support_formats.end())
+    return PIXEL_FORMAT_MJPEG;
+
+  if (support_formats.size())
+    return support_formats[0];
+
+  return PIXEL_FORMAT_UNKNOWN;
+}
+
 void VideoCaptureDeviceTizen::OnAllocateAndStart(
     int width,
     int height,
     int frame_rate,
     VideoPixelFormat format,
+    bool lazy_start,
     std::unique_ptr<Client> client) {
-  DVLOG(3) << " width:" << width << " height:" << height
-           << " frame_rate:" << frame_rate
-           << " format:" << media::VideoPixelFormatToString(format).c_str();
+  LOG(INFO) << " width:" << width << " height:" << height
+            << " frame_rate:" << frame_rate
+            << " format:" << media::VideoPixelFormatToString(format).c_str()
+            << " lazy_start:" << lazy_start;
 
   DCHECK(worker_.task_runner()->BelongsToCurrentThread());
 
   client_ = std::move(client);
+  format_ = format;
 
-  CameraHandle* camera_instance = CameraHandle::GetInstance();
-  if (camera_instance == nullptr) {
+  camera_instance_ = CameraHandle::GetInstance();
+  if (camera_instance_ == nullptr) {
     SetErrorState("Failed to get camera instance", FROM_HERE);
     return;
   }
 
-  if (camera_instance->GetDeviceName() !=
+  if (camera_instance_->GetDeviceName() !=
       DeviceNameToCameraId(device_descriptor_)) {
-    camera_instance->ResetHandle(DeviceNameToCameraId(device_descriptor_));
+    camera_instance_->ResetHandle(DeviceNameToCameraId(device_descriptor_));
   }
 
-  camera_instance->SetClient(this);
-  camera_ = camera_instance->GetCameraHandle();
-  if (camera_ == nullptr) {
-    SetErrorState("Camera creation failed", FROM_HERE);
+  camera_instance_->SetClient(this);
+  if (!camera_instance_->IsValid()) {
+    SetErrorState("Camera handle is invalid", FROM_HERE);
     return;
   }
 
-  int err = camera_set_display(camera_, CAMERA_DISPLAY_TYPE_NONE, nullptr);
+  int err =
+      camera_instance_->CameraSetDisplay(CAMERA_DISPLAY_TYPE_NONE, nullptr);
   if (CAMERA_ERROR_NONE != err) {
     SetErrorState(GetErrorString(err), FROM_HERE);
     return;
   }
 
-  err = camera_set_preview_format(camera_, toCapiType(format));
+  err = camera_instance_->CameraSetPreviewFormat(ToCapiType(format));
   if (CAMERA_ERROR_NONE != err) {
-    LOG(ERROR) << "camera_set_preview_format failed. Error# "
+    LOG(ERROR) << "camera_set_preview_format (" << format << ") failed. Error# "
                << GetErrorString(err);
 
     std::vector<media::VideoPixelFormat> supported_formats;
-    if (!camera_instance->GetSupportedPreviewPixelFormats(supported_formats)) {
-      SetErrorState("Camera internal error", FROM_HERE);
+    if (!camera_instance_->GetSupportedPreviewPixelFormats(supported_formats)) {
+      SetErrorState(
+          "Camera internal error: GetSupportedPreviewPixelFormats Failed",
+          FROM_HERE);
       return;
     }
 
-    LOG(ERROR) << "Trying with format (" << supported_formats[0] << ")";
-    err = camera_set_preview_format(camera_, toCapiType(supported_formats[0]));
+    VideoPixelFormat reselected_format =
+        ReselectCameraPreviewFormat(supported_formats);
+
+    LOG(ERROR) << "Trying with format (" << reselected_format << ")";
+    err =
+        camera_instance_->CameraSetPreviewFormat(ToCapiType(reselected_format));
     if (CAMERA_ERROR_NONE != err) {
       SetErrorState(GetErrorString(err), FROM_HERE);
       return;
     }
   }
 
-  err = camera_set_preview_resolution(camera_, width, height);
+  err = camera_instance_->CameraSetPreviewResolution(width, height);
   if (CAMERA_ERROR_NONE != err) {
     LOG(ERROR) << "camera_set_preview_resolution failed. Error#" << err
                << ". Trying default resolution: " << kMjpegWidth << " x "
@@ -480,77 +631,78 @@ void VideoCaptureDeviceTizen::OnAllocateAndStart(
 
     width = kMjpegWidth;
     height = kMjpegHeight;
-    err = camera_set_preview_resolution(camera_, width, height);
+    err = camera_instance_->CameraSetPreviewResolution(width, height);
     if (CAMERA_ERROR_NONE != err) {
       SetErrorState(GetErrorString(err), FROM_HERE);
       return;
     }
   }
 
-  if (use_media_packet_) {
-    err = camera_set_media_packet_preview_cb(
-        camera_, OnCameraCapturedWithMediaPacket, this);
-    if (CAMERA_ERROR_NONE != err) {
-      SetErrorState(GetErrorString(err), FROM_HERE);
-      return;
-    }
-  } else {
-    err = camera_set_preview_cb(camera_, OnCameraCaptured, this);
+  err = camera_instance_->CameraSetMediapacketPreviewCb(
+      OnCameraCapturedWithMediaPacket, this);
+  if (CAMERA_ERROR_NONE == err)
+    use_media_packet_ = true;
+  else {
+    err = camera_instance_->CameraSetPreviewCb(OnCameraCaptured, this);
     if (CAMERA_ERROR_NONE != err) {
       SetErrorState(GetErrorString(err), FROM_HERE);
       return;
     }
+    use_media_packet_ = false;
   }
 
-  err = camera_attr_set_preview_fps(camera_,
-                                    static_cast<camera_attr_fps_e>(frame_rate));
+  frame_rate_ = frame_rate;
+  err = camera_instance_->CameraSetPreviewFps(
+      static_cast<camera_attr_fps_e>(frame_rate));
   if (CAMERA_ERROR_NONE != err) {
-    LOG(ERROR) << "camera_attr_set_preview_fps failed. Error# " << err;
+    LOG(ERROR) << "cameraSetPreviewFps failed. Error# " << err;
 
-    int max_supported_fps = camera_instance->GetMaxFrameRate(
+    int max_supported_fps = camera_instance_->GetMaxFrameRate(
         CameraCapability::Resolution(width, height));
     if (!max_supported_fps) {
-      SetErrorState("Camera internal error", FROM_HERE);
+      SetErrorState("Camera internal error : GetMaxFrameRate Failed",
+                    FROM_HERE);
       return;
     }
 
-    err = camera_attr_set_preview_fps(
-        camera_, static_cast<camera_attr_fps_e>(max_supported_fps));
+    frame_rate_ = max_supported_fps;
+
+    err = camera_instance_->CameraSetPreviewFps(
+        static_cast<camera_attr_fps_e>(max_supported_fps));
     if (CAMERA_ERROR_NONE != err) {
       SetErrorState(GetErrorString(err), FROM_HERE);
       return;
     }
   }
 
-  if (IsMobileProfile() &&
-      (camera_instance->GetDeviceName() == CAMERA_DEVICE_CAMERA1)) {
-    err = camera_attr_set_stream_flip(camera_, CAMERA_FLIP_VERTICAL);
+  if (IsMobileProfile() && !IsEmulatorArch() &&
+      (camera_instance_->GetDeviceName() == CAMERA_DEVICE_CAMERA1)) {
+    err = camera_instance_->CameraSetStreamFlip(CAMERA_FLIP_VERTICAL);
     if (CAMERA_ERROR_NONE != err) {
       SetErrorState(GetErrorString(err), FROM_HERE);
       return;
     }
   }
 
-  state_ = kCapturing;
-
-  err = camera_start_preview(camera_);
-  if (CAMERA_ERROR_NONE != err) {
-    SetErrorState(GetErrorString(err), FROM_HERE);
-    return;
+  state_ = kAllocated;
+  if (!lazy_start) {
+    if (!StartPreview()) {
+      LOG(ERROR) << "StartPreview failed!!!";
+      return;
+    }
   }
 
-  if (IsMobileProfile())
-    WakeUpDisplayAndAcquireDisplayLock();
+  client_->OnStarted();
 }
 
 void VideoCaptureDeviceTizen::OnStopAndDeAllocate() {
   DCHECK(worker_.task_runner()->BelongsToCurrentThread());
 
   if (use_media_packet_)
-    camera_unset_media_packet_preview_cb(camera_);
+    camera_instance_->CameraUnsetMediapacketPreviewCb();
   else
-    camera_unset_preview_cb(camera_);
-  camera_stop_preview(camera_);
+    camera_instance_->CameraUnsetPreviewCb();
+  camera_instance_->CameraStopPreview();
 
   if (IsMobileProfile())
     ReleaseDisplayLock();
@@ -596,4 +748,79 @@ void VideoCaptureDeviceTizen::SetErrorState(const char* error,
       error);
 }
 
+void VideoCaptureDeviceTizen::CapturedCb(camera_image_data_s* image,
+                                         camera_image_data_s* postview,
+                                         camera_image_data_s* thumbnail,
+                                         void* userData) {
+  LOG(INFO) << "CapturedCb!";
+  VideoCaptureDeviceTizen* handle =
+      static_cast<VideoCaptureDeviceTizen*>(userData);
+
+  if (handle->photo_callbacks_.empty())
+    return;
+
+  mojom::BlobPtr blob = mojom::Blob::New();
+  blob->data.resize(image->size);
+  memcpy(blob->data.data(), image->data, image->size);
+
+  if (blob) {
+    TakePhotoCallback cb = std::move(*(handle->photo_callbacks_.front().get()));
+    handle->photo_callbacks_.pop_front();
+    std::move(cb).Run(std::move(blob));
+  }
+}
+
+void VideoCaptureDeviceTizen::CaptureCompletedCb(void* userData) {
+  LOG(INFO) << "Capture Completed!";
+  VideoCaptureDeviceTizen* handle =
+      static_cast<VideoCaptureDeviceTizen*>(userData);
+
+  handle->worker_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&VideoCaptureDeviceTizen::OnCaptureCompletedCb,
+                                base::Unretained(handle)));
+}
+
+void VideoCaptureDeviceTizen::OnCaptureCompletedCb() {
+  if (!camera_instance_) {
+    SetErrorState("Failed to get camera instance!", FROM_HERE);
+    return;
+  }
+
+  int err = 0;
+  if (CAMERA_ERROR_NONE != (err = camera_instance_->CameraStartPreview())) {
+    SetErrorState(GetErrorString(err), FROM_HERE);
+    return;
+  }
+}
+
+bool VideoCaptureDeviceTizen::StartPreview() {
+  if (state_ != kAllocated)
+    return false;
+
+  LOG(INFO) << "VideoCaptureDeviceTizen::StartPreview()";
+  state_ = kCapturing;
+
+  int err = camera_instance_->CameraStartPreview();
+  if (CAMERA_ERROR_NONE != err) {
+    SetErrorState(GetErrorString(err), FROM_HERE);
+    return false;
+  }
+
+  if (IsMobileProfile() || IsWearableProfile())
+    WakeUpDisplayAndAcquireDisplayLock();
+
+  return true;
+}
+
+void VideoCaptureDeviceTizen::StopPreview() {
+  if (state_ != kCapturing)
+    return;
+
+  state_ = kAllocated;
+
+  int err = camera_instance_->CameraStopPreview();
+  if (CAMERA_ERROR_NONE != err)
+    SetErrorState(GetErrorString(err), FROM_HERE);
+}
+
 }  // namespace media
index 9e732e3..9b26e82 100644 (file)
@@ -34,12 +34,22 @@ class VideoCaptureDeviceTizen : public VideoCaptureDevice,
   VideoCaptureDeviceTizen(const VideoCaptureDeviceTizen&) = delete;
   VideoCaptureDeviceTizen& operator=(const VideoCaptureDeviceTizen&) = delete;
 
+  // Implements CameraHandleClient
+  void OnStreamStopped() override;
+
+  // Implements VideoCaptureDevice
   void AllocateAndStart(const VideoCaptureParams& params,
                         std::unique_ptr<Client> client) override;
 
   void StopAndDeAllocate() override;
 
-  void OnStreamStopped() override;
+  bool StartPreview();
+  void StopPreview();
+
+  void GetPhotoState(GetPhotoStateCallback callback) override;
+  void SetPhotoOptions(mojom::PhotoSettingsPtr settings,
+                       SetPhotoOptionsCallback callback) override;
+  void TakePhoto(TakePhotoCallback callback) override;
 
   static camera_device_e DeviceNameToCameraId(
       const VideoCaptureDeviceDescriptor& device_descriptor);
@@ -49,10 +59,16 @@ class VideoCaptureDeviceTizen : public VideoCaptureDevice,
   static void OnCameraCaptured(camera_preview_data_s* frame, void* data);
 
   static const char* GetErrorString(int err_code);
+  static void CapturedCb(camera_image_data_s* image,
+                         camera_image_data_s* postview,
+                         camera_image_data_s* thumbnail,
+                         void* userData);
+  static void CaptureCompletedCb(void* userData);
 
  private:
   enum InternalState {
     kIdle,       // The device driver is opened but camera is not in use.
+    kAllocated,  // This device is configured, but preview is not started
     kCapturing,  // Video is being captured.
     kError       // Error accessing HW functions.
                  // User needs to recover by destroying the object.
@@ -62,8 +78,8 @@ class VideoCaptureDeviceTizen : public VideoCaptureDevice,
                           int height,
                           int frame_rate,
                           VideoPixelFormat format,
+                          bool lazy_start,
                           std::unique_ptr<Client> client);
-  void OnStopAndDeAllocate();
 
   // For handling the camera preview callback returning mediapacket
   bool IsCapturing();
@@ -72,7 +88,23 @@ class VideoCaptureDeviceTizen : public VideoCaptureDevice,
 
   void SetErrorState(const char* reason, const base::Location& location);
 
-  camera_h camera_;
+  void OnStopAndDeAllocate();
+
+  void OnGetPhotoState(GetPhotoStateCallback callback);
+  void OnSetPhotoOptions(mojom::PhotoSettingsPtr settings,
+                         SetPhotoOptionsCallback callback);
+  void OnCaptureCompletedCb();
+
+  bool SetCameraZoom(const mojom::PhotoSettingsPtr& settings);
+  bool SetCameraResolution(const mojom::PhotoSettingsPtr& settings);
+  bool SetCameraExposure(const mojom::PhotoSettingsPtr& settings);
+  bool SetCameraBrightness(const mojom::PhotoSettingsPtr& settings);
+  bool SetCameraContrast(const mojom::PhotoSettingsPtr& settings);
+  bool SetCameraIso(const mojom::PhotoSettingsPtr& settings);
+  VideoPixelFormat ReselectCameraPreviewFormat(
+      const std::vector<VideoPixelFormat>& support_formats);
+
+  CameraHandle* camera_instance_;
   std::unique_ptr<VideoCaptureDevice::Client> client_;
   VideoCaptureDevice::Client::Buffer buffer_;
   base::Lock capturing_state_lock_;
@@ -85,6 +117,11 @@ class VideoCaptureDeviceTizen : public VideoCaptureDevice,
   InternalState state_;
 
   base::TimeTicks first_ref_time_;
+  int frame_rate_{0};
+  media::VideoPixelFormat format_{media::PIXEL_FORMAT_UNKNOWN};
+
+  // List of |photo_callbacks_| in flight.
+  std::list<std::unique_ptr<TakePhotoCallback>> photo_callbacks_;
 };
 
 }  // namespace media