Wearable Web

Task: Camera

This task, based on the Camera sample delivered with the Tizen SDK, demonstrates how you can use the Camera API (Tizen Extension) API to control the camera on the Tizen wearable device. For more information on the sample functionality and creating the sample with the full source code, see Camera.

This task consists of the following parts:

This sample is a fully functional application for launching and controlling the camera on the Tizen wearable device.

Defining the Application Layout

The Camera sample application layout contains 3 screens: the main screen that shows the camera view port and the mode control, the preview screen that shows the camera preview, and the settings screen that allows you to define the camera settings.

The application uses a simple MV (Model View) architecture. The js/core directory contains files that implement a simple AMD (Asynchronous Module Definition) and specify module defining.

Defining the Main Screen

  1. index.html Source File

    The main screen displays the camera view port and buttons for camera modes and settings. In addition, it contains a progress area for displaying the progress of a recording.

    The screen is styled with the css/style.css file that contains both common styling for all screens and list views, as well as styles for separate elements of each screen. The JavaScript implementation for the screen is located in the js/views/main.js file.

    <div class="ui-page" id="main">
       <div class="ui-content">
          <video id="camera-preview"></video>
          <button type="button" class="ui-btn hidden navigation" id="mode-btn"></button>
          <button type="button" class="ui-btn hidden navigation" id="settings-btn"></button>
          <div id="record-progress" class="hidden">
             <div id="record-progress-label">
                <span id="record-progress-label-val"></span>/<span id="record-progress-label-max"></span> secs
             </div>
             <div id="record-progress-val"></div>
          </div>
          <div id="focus-container" class="hidden">
             <div id="focus-frame"></div>
          </div>
       </div>
    </div>
    

Defining the Preview Screen

  1. index.html Source File

    The preview screen displays the image and video preview.

    The screen is styled with the css/style.css file that contains both common styling for all screens and list views, as well as styles for separate elements of each screen. The JavaScript implementation for the screen is located in the js/views/preview.js file.

    <div class="ui-page" id="preview">
       <div class="ui-content paused">
          <img id="preview-picture" class="hidden"/>
          <video id="preview-video" class="hidden"></video>
          <div id="preview-foreground" class="paused hidden"></div>
       </div>
    </div>
    

Defining the Settings Screen

  1. index.html Source File

    The Settings screen displays the picture and video size and format settings.

    The header section of the screen is defined within a <div> element whose class attribute is set to ui-header. The header section determines the title of the screen. The content section contains a list view component with links to different settings.

    Each individual setting has its own sub-screen with a screen title and a list view component displaying the available settings values.

    The screen is styled with the css/style.css file that contains both common styling for all screens and list views, as well as styles for separate elements of each screen. The JavaScript implementation for the screen is located in the js/views/settings.js file.

    <div class="ui-page" id="settings">
       <div class="ui-header" data-position="fixed">
          <h2 class="ui-title">Settings</h2>
       </div>
       <div class="ui-content">
          <ul class="ui-listview">
             <li><a id="picture-size-btn">Picture size</a></li>
             <li><a id="picture-format-btn">Picture format</a></li>
             <li><a id="recording-format-btn">Recording format</a></li>
          </ul>
       </div>
    </div>
    
    <div class="ui-page" id="picture-size">
       <div class="ui-header" data-position="fixed">
          <h2 class="ui-title">Picture size</h2>
       </div>
       <div class="ui-content">
          <div class="listbox">
             <ul class="ui-listview" id="picture-size-list"></ul>
          </div>
       </div>
    </div>
    <!--Picture and recording format have similar screens-->
    

Defining the Preview

This section builds upon the elements described in Accessing the Camera Device.

Initializing the Preview

  1. js/views/main.js Source File
    1. After launching the application, set the variables for the camera preview, call the initCameraPreview() method to initialize the preview, and register the event handler for when the camera is ready to display the preview.

      var e = req.core.event,
          page = null,
          camera = req.models.camera,
          cameraPreview = null;
      /* Initialize module */
      function init() 
      {
         page = document.getElementById('main');
         cameraPreview = document.getElementById('camera-preview');
         bindEvents();
         initCameraPreview();
      }
      e.listeners({'camera.ready': onCameraReady});
      
    2. In the initCameraPreview() method, call the requestForCameraStream() method to retrieve the media stream from the camera.

      function requestForCameraStream() 
      {
         previewInitAttemtps += 1;
         navigator.webkitGetUserMedia({video: true, audio: true}, 
                                      onPreviewStream, onPreviewStreamError);
      }
      

Displaying the Preview

  1. js/views/main.js Source File

    If you successfully retrieve the media stream from the camera, use the onPreviewStream() event handler to first call the registerStream() method and then play the preview with the play() method.

    function onPreviewStream(stream) 
    {
       previewInitAttemtps = 0;
       cameraStream = stream;
       cameraPreview.src = streamUrl;
       camera.registerStream(cameraStream);
       if (pageHelper.isPageActive(page)) 
       {
          cameraPreview.play();
       }
    }
    
  2. js/models/camera.js Source File
    1. In the registerStream() method, create the camera control for the media stream.

      function registerStream(mediaStream) 
      {
         navigator.tizCamera.createCameraControl(mediaStream, 
                                                 onCameraControlCreated, 
                                                 onCameraControlError);
      }
      
    2. When the camera control (the cameraControl object) is created, initialize the camera settings and fire the camera.ready event.

      function onCameraControlCreated(control) 
      {
         cameraControl = control;
         initCameraSettings();
         e.fire('camera.ready');
      }
      
    3. When initializing the camera settings, read and saves them.
      function initCameraSettings() 
      {
         var pictureFormats = null,
             pictureSizes = null;
         cameraSettings = storage.get(STORAGE_SETTINGS_KEY);
         if (!cameraSettings) 
         {
            cameraSettings = {};
            pictureFormats = getAvailablePictureFormats();
            cameraSettings.pictureFormat = pictureFormats[0];
            pictureSizes = getAvailablePictureSizes();
            cameraSettings.pictureSize = pictureSizes[0];
            saveCameraSettings();
         }
      }
      
  3. js/views/main.js Source File

    When the camera is ready to display the preview and the camera.ready event is fired, the onCameraReady() event handler is called.

    function onCameraReady() 
    {
       previewInitInProgress = false;
       if (pageHelper.isPageActive(page)) 
       {
          cameraPreview.play();
       }
    }
    

Capturing Images

This section builds upon the elements described in Managing the Camera.

Initializing the Image Capture

  1. js/views/main.js Source File

    Set the needed variables and events for the image capture.

    var e = req.core.event,
        camera = req.models.camera,
        page = null,
        cameraPreview = null,
        focusContainer = null,
        focusFrame = null,
        photoMode = true,
    
    /* Initialize the module */
    function init() 
    {
       cameraPreview = document.getElementById('camera-preview');
       focusContainer = document.getElementById('focus-container');
       focusFrame = document.getElementById('focus-frame');
       bindEvents();
    }
    
    e.listeners(
    {
       'camera.shutter': onCameraShutter,
       'camera.picture.done': onPictureDone,
       'camera.autofocus.start': onAutoFocusStart,
       'camera.autofocus.success': onAutoFocusSuccess,
       'camera.autofocus.failure': onAutoFocusFailure
    });
    
  2. js/views/main.js Source File

    When the user taps the preview, the onCameraPreviewClick() event handler is called. If the photo mode is on, the image is captured with the takePicture() method.

    function onCameraPreviewClick() 
    {
       if (photoMode) 
       {
          takePhoto();
       } 
       else 
       {
          /* Record video */
       }
    }
    
    function takePhoto() 
    {
       camera.takePicture();
    }
    

Auto-focusing an Image

  1. js/models/camera.js Source File

    To capture an image, first set the needed variables and activate the auto-focus.

    var e = req.core.event,
        storage = req.core.storage.localstorage,
        dateHelper = req.helpers.date,
        PICTURE_DESTINATION_DIRECTORY = '/opt/usr/media/Images',
        cameraControl = null,
        picturePath = '';
    
    function takePicture() 
    {
       if (busy) 
       {
          return false;
       }
       busy = true;
       e.fire('camera.autofocus.start');
       if (cameraControl.autoFocus()) 
       {
          setTimeout(onAutoFocusSuccess, AUTOFOCUS_DELAY);
       } 
       else 
       {
          /* Error handling */
       }
    
       return true;
    }
    
  2. js/views/main.js Source File

    When the camera.autofocus.start event is fired, the onAutoFocusStart() event handler calls the showAutoFocus() method to add styles to the animated focus frame on the preview.

    function onAutoFocusStart() 
    {
       showAutoFocus();
    }
    
    function showAutoFocus() 
    {
       focusContainer.classList.remove('hidden');
       focusFrame.classList.add('autofocus-animation');
    }
    
  3. js/models/camera.js Source File

    If the auto-focus succeeds, the onAutoFocusSuccess event handler calls the setTimeout() method to start the image capture. The event handler also fires the camera.autofocus.success event.

    function onAutoFocusSuccess() 
    {
       e.fire('camera.autofocus.success');
       setTimeout(startTakingPicture, TAKE_PICTURE_DELAY);
    }
    
  4. js/views/main.js Source File

    Based on the camera.autofocus.success event, the focus frame style is changed on the screen.

    function onAutoFocusSuccess() 
    {
       focusFrame.classList.add('autofocus-success');
    }
    

Capturing an Image

  1. js/models/camera.js Source File

    In the startTakingPicture() method, set name and path of the captured image file, and the format and size of the file.

    function startTakingPicture() 
    {
       var settings = {},
           fileName = '';
           fileName = createPictureFileName();
       picturePath = PICTURE_DESTINATION_DIRECTORY + '/' + fileName;
       settings.fileName = fileName;
       settings.pictureFormat = getPictureFormat();
       settings.pictureSize = getPictureSize();
    
       cameraControl.image.applySettings(settings, onImageSettingsApplied, 
                                         onImageSettingsError);
    }
    
  2. js/models/camera.js Source File

    When the image settings have been successfully applied and the picture is ready, the onImageSettingsApplied() and onPictureDone() event handlers are called.

    function onImageSettingsApplied() 
    {
       cameraControl.image.takePicture(onPictureDone, onPictureError);
    }
    
    function onPictureDone() 
    {
       e.fire('camera.picture.done', {path: picturePath});
    }
    
  3. js/views/main.js Source File

    When the picture is done, the auto-focus frame is hidden and the views.preview.show event is fired to show the image preview.

    /* Called when the camera.picture.done event is fired */
    function onPictureDone(ev) 
    {
       var path = ev.detail.path;
       hideAutoFocus();
       e.fire('views.preview.show', {picture: path});
    }
    
    function hideAutoFocus() 
    {
       var classList = focusFrame.classList;
       focusContainer.classList.add('hidden');
       classList.remove('autofocus-animation');
       classList.remove('autofocus-success');
       classList.remove('autofocus-failure');
    }
    
  4. js/views/preview.js Source File

    The views.preview.show event calls the show() method, which displays the image preview on the screen.

    function show(ev) 
    {
       var detail = ev.detail;
       if (detail.picture) 
       {
          picture.src = detail.picture;
          picture.classList.remove('hidden');
          video.classList.add('hidden');
       } 
       else if (detail.video) 
       {
          /* Show recorded video preview */ 
       }
    }
    
    e.listeners({'views.preview.show': show});
    

Recording Videos

This section builds upon the elements described in Managing the Camera.

Initializing Video Recording

  1. js/views/main.js Source File
    1. Set the needed variables for the image capture.

      The maximum recording time is set for 10 seconds (maxRecordingTimeSeconds). Set the RECORDING_INTERVAL_STEP variable in milliseconds for checking the length of the recording.

      var e = req.core.event,
          camera = req.models.camera,
          page = null,
          cameraPreview = null,
          recordProgress = null,
          recordProgressVal = null,
          recordProgressLabelVal = null,
          recordProgressLabelMax = null,
          recording = false,
          RECORDING_INTERVAL_STEP = 100,
          recordingInterval = null,
          maxRecordingTimeSeconds = Math.floor(camera.MAX_RECORDING_TIME / 1000);
      
      function init() 
      {
         page = document.getElementById('main');
         cameraPreview = document.getElementById('camera-preview');
         recordProgress = document.getElementById('record-progress');
         recordProgressVal = document.getElementById('record-progress-val');
         recordProgressLabelVal = document.getElementById('record-progress-label-val');
         recordProgressLabelMax = document.getElementById('record-progress-label-max');
         bindEvents();
         initCameraPreview();
      }
      
    2. When the user taps the preview, the onCameraPreviewClick() event handler is called. If the photo mode is not on, the video recording is set with the toggleRecording() and setRecording() methods.

      function onCameraPreviewClick() 
      {
         if (photoMode) 
         {
            /* Capture an image */
         } 
         else 
         {
            toggleRecording();
            setRecording();
         }
      }
      
    3. The toggleRecording() method defines the recording state (on or off).

      function toggleRecording(forceValue) 
      {
         if (forceValue !== undefined) 
         {
            recording = !!forceValue;
         } 
         else 
         {
            recording = !recording;
         }
      }
      
    4. The setRecording() method starts or stops recording, based on the recording state.
      function setRecording() 
      {
         if (recording) 
         {
            startRecording();
         } 
         else 
         {
            stopRecording();
         }
      }
      

Starting Video Recording

  1. js/views/main.js Source File
    1. The startRecording() method starts the recording process, and the stopRecording() method stops it.

      function startRecording() 
      {
         camera.startRecording();
         resetRecordingProgress();
         showRecordingView();
      }
      
      function stopRecording() 
      {
         camera.stopRecording();
      }
      
    2. To start the recording process, first reset the time and progress bar values.

      function resetRecordingProgress() 
      {
         recordingTime = 0;
         renderRecordingProgressBar();
      }
      
      function renderRecordingProgressBarValue(value) 
      {
         recordProgressVal.style.width = value + 'px';
      }
      
      function renderRecordingProgressBarLabel() 
      {
         recordProgressLabelVal.innerHTML = dateHelper.formatTime(time);
         recordProgressLabelMax.innerHTML = dateHelper.formatTime(maxRecordingTimeSeconds);
      }
      
      function renderRecordingProgressBar() 
      {
         var parentWidth = recordProgress.clientWidth,
             width = recordingTime / camera.MAX_RECORDING_TIME * parentWidth;
         renderRecordingProgressBarValue(width);
         renderRecordingProgressBarLabel();
      }
      
    3. While recording, you want to show the recording view and progress bar, but hide the navigation buttons.

      function showRecordingView() 
      {
         hideNavigationBtns();
         showRecordProgress();
      }
      
      function hideNavigationBtns() 
      {
         modeBtn.classList.add('hidden');
         settingsBtn.classList.add('hidden');
      }
      
      function showRecordProgress() 
      {
         recordProgress.classList.remove('hidden');
      }
      
  2. js/models/camera.js Source File
    1. Start video recording by applying video file parameters.

      function startRecording() 
      {
         var settings = {},
             fileName = '';
         if (busy) 
         {
            return false;
         }
         busy = true;
         fileName = createVideoFileName();
         videoPath = VIDEO_DESTINATION_DIRECTORY + '/' + fileName;
         settings.fileName = fileName;
         settings.recordingFormat = getRecordingFormat();
         cameraControl.recorder.applySettings(settings, onVideoSettingsApplied,
                                              onVideoSettingsError);
      				
         return true;
      }
      
      function createVideoFileName() 
      {
         var currentDate = new Date(),
             extension = getRecordingFormat(),
             fileName = '';
      
         fileName = dateHelper.format(currentDate, 'yyyymmdd_HHMMSS') + '.' + extension;
      
         return fileName;
      }
      
    2. When the video file parameters have been applied successfully, the onVideoSettingsApplied() event handler starts the actual recording.
      function onVideoSettingsApplied() 
      {
         cameraControl.recorder.start(onRecordingStartSuccess, onRecordingStartError);
      }
      
    3. Trace the length of the recording. If the recording lasts longer than 10 seconds, stop recording.

      function onRecordingStartSuccess() 
      {
         startTracingVideoLength();
         e.fire('camera.recording.start');
      }
      
      function startTracingVideoLength() 
      {
         videoRecordingStartTime = new Date();
         videoLengthCheckInterval = window.setInterval(checkVideoLength, 
                                                       VIDEO_LENGTH_CHECK_INTERVAL);
      }
      
      function checkVideoLength() 
      {
         var currentTime = new Date();
      
         videoRecordingTime = currentTime - videoRecordingStartTime;
         if (videoRecordingTime > MAX_RECORDING_TIME) 
         {
            stopRecording();
         }
      }
      
  3. js/views/main.js Source File

    Based on the camera.recording.start event, call the setRecordingInterval() method to set the recording interval and update the recording process.

    function onRecordingStart() 
    {
       setRecordingInterval();
    }
    
    function setRecordingInterval() 
    {
       recordingInterval = setInterval(updateRecordingProgress, 
                                       RECORDING_INTERVAL_STEP);
    }
    
    function updateRecordingProgress() 
    {
       recordingTime = camera.getRecordingTime();
    
       renderRecordingProgressBar();
    }
    
    function renderRecordingProgressBar() 
    {
       var parentWidth = recordProgress.clientWidth,
           width = recordingTime / camera.MAX_RECORDING_TIME * parentWidth;
       renderRecordingProgressBarValue(width);
       renderRecordingProgressBarLabel();
    }
    
    function renderRecordingProgressBarValue(value) 
    {
       recordProgressVal.style.width = value + 'px';
    }
    
    function renderRecordingProgressBarLabel() 
    {
       var time = Math.ceil(recordingTime / 1000);
    
       if (time > maxRecordingTimeSeconds) 
       {
          time = maxRecordingTimeSeconds;
       }
       recordProgressLabelVal.innerHTML = dateHelper.formatTime(time);
       recordProgressLabelMax.innerHTML = dateHelper.formatTime(maxRecordingTimeSeconds);
    }
    

Stopping Video Recording

  1. js/views/main.js Source File

    When the user taps the screen to stop the recording, the stopRecording() method calls the stopRecording() method.

    function stopRecording() 
    {
       camera.stopRecording();
    }
    
  2. js/models/camera.js Source File

    After the recording stops successfully, the onVideoRecordingStopSuccess() event handler fires the camera.recording.done event.

    function onVideoRecordingStopSuccess() 
    {
       busy = false;
       e.fire('camera.recording.done', {path: videoPath});
    }
    
  3. js/views/main.js Source File

    The camera.recording.done event calls the onRecordingDone event handler.

    function onRecordingDone(ev) 
    {
       var path = ev.detail.path;
    
       removeRecordingInterval();
       toggleRecording(false);
       updateRecordingProgress();
       if (!exitInProgress) 
       {
          e.fire('views.preview.show', {video: path});
       }
    }
    
  4. js/views/preview.js Source File

    The onRecordingDone event handler fires the views.preview.show event, which calls the show() method to displays the video preview on the screen.

    function show(ev) 
    {
       var detail = ev.detail;
    
       if (detail.picture) 
       {
          /* Show captured image preview */
       } 
       else if (detail.video) 
       {
          foreground.classList.remove('hidden');
          video.classList.remove('hidden');
          picture.classList.add('hidden');
          video.src = detail.video;
          video.addEventListener('loadeddata', showPreviewPage);
       }
    }
    

Changing Settings

This section builds upon the elements described in Managing the Camera.

Initializing the Settings

  1. js/views/main.js Source File

    When the user clicks the icon in the right top corner of the main screen, the click event opens the Settings screen.

    function onSettingsBtnClick() 
    {
       e.fire('views.settings.show', {photoMode: photoMode});
    }
    
  2. js/models/camera.js Source File

    When the settings are selected, the saveCameraSettings() method is used to save them.

    function saveCameraSettings()
    {
       storage.add(STORAGE_SETTINGS_KEY, cameraSettings);
    }

Setting the Image Format

  1. js/views/pictureFormat.js Source File

    The renderView() method creates the content for the subscreen for setting the image file format.

    function renderView() 
    {
       var formats = camera.getAvailablePictureFormats(),
           currentFormat = camera.getPictureFormat(),
           i = 0,
           len = formats.length,
           content = [],
           format = null,
           checked = false;
    
       for (i = 0; i < len; i += 1) 
       {
          format = formats[i];
          checked = currentFormat === format;
          content.push(t.get('pictureFormatRow', 
          {
             format: format,
             checked: checked
          }));
       }
    
       formatList.innerHTML = content.join('');
    }
    
  2. js/models/camera.js Source File

    The available formats are retrieved from the model.

    function getAvailablePictureFormats() 
    {
       return cameraControl.capabilities.pictureFormats;
    }
    
  3. js/views/pictureFormat.js Source File

    After the user selects the format they want, the selected format is passed to the model.

    function bindEvents() 
    {
       page.addEventListener('pagebeforeshow', onPageBeforeShow);
       formatList.addEventListener('click', onFormatListClick);
    }
    
    function onFormatListClick() 
    {
       var input = formatList.querySelectorAll('input:checked')[0];
    
       camera.setPictureFormat(input.getAttribute('data-format'));
    }
    
  4. js/models/camera.js Source File

    The selected format is used and saved in the model.

    function setPictureFormat(format) 
    {
       cameraSettings.pictureFormat = format;
       saveCameraSettings();
    }
    

Setting the Image File Size

  1. js/views/pictureSize.js Source File

    The renderView() method creates the content for the subscreen for setting the image file size.

    function renderView() 
    {
       var sizes = camera.getAvailablePictureSizes(),
           currentSize = camera.getPictureSize(),
           i = 0,
           len = sizes.length,
           content = [],
           size = null,
           checked = false;
    
       for (i = 0; i < len; i += 1) 
       {
          size = sizes[i];
          checked = currentSize.height === size.height &&
                    currentSize.width === size.width;
          content.push(t.get('pictureSizeRow', 
          {
             width: size.width,
             height: size.height,
             checked: checked
          }));
       }
    
       pictureSizeList.innerHTML = content.join('');
    }
    
  2. js/models/camera.js Source File

    The available sizes are retrieved from the model.

    function getAvailablePictureSizes() 
    {
       return cameraControl.capabilities.pictureSizes;
    }
    
  3. js/views/pictureSize.js Source File

    After the user selects the size they want, the selected size is passed to the model.

    function onSizeListClick() 
    {
       var input = pictureSizeList.querySelectorAll('input:checked')[0],
           width = parseInt(input.getAttribute('data-width'), 10),
           height = parseInt(input.getAttribute('data-height'), 10);
    
       camera.setPictureSize(
       {
          width: width,
          height: height
       });
    }
    
  4. js/models/camera.js Source File

    The selected size is used and saved in the model.

    function setPictureSize(size) 
    {
       cameraSettings.pictureSize = 
       {
          width: size.width,
          height: size.height
       };
       saveCameraSettings();
    }
    

Setting the Video Format

  1. js/views/recordingFormat.js Source File

    The renderView() method creates the content for the subscreen for setting the video file format.

    function renderView() 
    {
       var formats = camera.getAvailableRecordingFormats(),
           currentFormat = camera.getRecordingFormat(),
           i = 0,
           len = formats.length,
           content = [],
           format = null,
           checked = false;
    
       for (i = 0; i < len; i += 1) 
       {
          format = formats[i];
          checked = currentFormat === format;
          content.push(t.get('recordingFormatRow', 
          {
             format: format,
             checked: checked
          }));
       }
    
       formatList.innerHTML = content.join('');
    }
    
  2. js/models/camera.js Source File

    The available formats are retrieved from the model.

    function getAvailableRecordingFormats() 
    {
       return cameraControl.capabilities.recordingFormats;
    }
    
  3. js/views/recordingFormat.js Source File

    After the user selects the format they want, the selected format is passed to the model.

    function onFormatListClick() 
    {
       var input = formatList.querySelectorAll('input:checked')[0];
    
       camera.setRecordingFormat(input.getAttribute('data-format'));
    }
    
  4. js/models/camera.js Source File

    The selected format is used and saved in the model.

    function setRecordingFormat(format) 
    {
       cameraSettings.recordingFormat = format;
       saveCameraSettings();
    }
    
Go to top