Task: Self Camera
This task, based on the SelfCamera sample delivered with the Tizen SDK, demonstrates how you can use the getUserMedia API to access and display the camera video stream, and capture a single photo. For more information on the sample functionality and creating the sample with the full source code, see the SelfCamera.
This task consists of the following parts:
- Defining the Application Layout defines how to create the application screens.
- Initializing the Application defines how to initialize the application.
- Accessing the Camera Stream defines how to access the front camera video stream.
- Capturing Pictures from Video defines how to capture a single frame from the video tag.
This sample is a fully functional camera application for taking photos with the device front camera. The user can set the timer delay and access photos by clicking on the photo previews.
Defining the Application Layout
The SelfCamera sample application layout contains only 1 screen: the main screen that displays the camera view and application control elements (buttons).
The following figure shows the main screen of the application.
Figure: SelfCamera screen
Defining the Main Screen
- index.html Source File
The <div> elements are used to define the camera elements for the screen.
<body> <div id="camera"></div> <div id="countdown"></div> <div class="timers"> <div id="timer2"></div> <div id="timer5"></div> <div id="timer10"></div> </div> <div id="thumbnail"> <div id="upImage"></div> </div> <div id="shutter-container"> <div id="shutter"></div> </div> </body>
- css/style.css Source File
The style.css file defines the positions and styles of the camera elements.
#camera { width: 100%; height: 100%; overflow: hidden; } div#countdown { position: absolute; width: 100%; height: 100%; top: 20%; font-size: 200pt; text-align: center; vertical-align: middle; color: rgba(135, 226, 0, 0.8); margin: auto 0; z-index: 10; } .timers { position: absolute; width: 100%; background-color: rgba(10, 10, 10, 0.4); top: 0; left: 0; } div#thumbnail { position: absolute; width: 56px; height: 64px; left: 13px; bottom: 19px; background-size: 56px 64px; background-color: transparent; background-image: url('../images/none.png'); background-repeat: no-repeat; z-index: 100; } div#shutter-container { width: 100%; height: 86px; position: absolute; bottom: 0; z-index: 11 }
Initializing the Application
- config.xml Source File
The required privileges are declared in the config.xml file.
<!--Configuration file content--> <widget ...> <!--Other configuration details--> <tizen:privilege name="http://tizen.org/privilege/application.launch"/> <tizen:privilege name="http://tizen.org/privilege/filesystem.read"/> <tizen:privilege name="http://tizen.org/privilege/filesystem.write"/> <tizen:privilege name="http://tizen.org/privilege/content.read"/> <tizen:privilege name="http://tizen.org/privilege/content.write"/> </widget>
Accessing the Camera Stream
This section builds upon the elements described in Accessing a Video Stream.
Managing the Application Object
The application object functionality is implemented in the main.js file.
- Creating and Initializing the Application Object
- The SelfCamera object represents the entire application entity, and by using the prototype, the member methods are registered in the object.
var selfCamera; function SelfCamera() { "use strict"; } function() { "use strict"; var DELAY_2_SECOND = 2, DELAY_5_SECOND = 5, DELAY_10_SECOND = 10; var previewLock = false; SelfCamera.prototype = { countdown: -1, /* Current value after clicking the camera button */ countdownTimeoutID: -1, countSound: new Audio('sounds/sounds_count.wav'), img: document.createElement('canvas'), filename: '', loadDirectory: '', saveDirectory: 'images/', IMG_PREFIX: 'SelfCamera_', shutterSound: new Audio('sounds/sounds_Shutter_01.wav'), timer: null, /* Value set by the buttons */ systemIO: null, video: null, src: null, isMediaWorking: false previewLock: false }; }
- The SelfCamera object is created and initialized.
selfCamera = new SelfCamera(); $(document).ready(function() { "use strict"; selfCamera.init(); });
- The SelfCamera object represents the entire application entity, and by using the prototype, the member methods are registered in the object.
- Binding Events to the Application Object
-
To bind video events to the application object, call the bindVideoEvents() method, which registers the event handlers to the corresponding events using the bind() method.
SelfCamera.prototype.bindVideoEvents = function() { var self = this; $(this.video).on("stalled", function(e) {this.load();}); $(this.video).on("playing", this.resizeVideo.bind(this)); $(this.video).on('click', function() {this.play();}); };
-
To bind button and hardware key events to the application object, call the bindEvents() method, which register the event handlers to the corresponding events using the bind() method.
SelfCamera.prototype.bindEvents = function bindEvents() { var self = this; document.addEventListener('webkitvisibilitychange', function(event) { self.clearCountdown(); previewLock = false; if (document.webkitVisibilityState === 'visible') { if (self.video !== null) { self.reloadSaveDirectory(function() {self.video.play();}); } self.loadThumbnail(true, false); } }); $('shutter').mousedown(function(ev) {$('shutter').addClass('active');}) .mouseup(function(ev) {$('shutter').removeClass('active');}) .on('touchstart', function(ev) {$('shutter').addClass('active');}) .on('touchend', function(ev) {$('shutter').removeClass('active');}); $(window).on('tizenhwkey', function(e) { if (e.originalEvent.keyName === "back") { if (self.countdownTimeoutID !== -1) { self.clearCountdown(); self.loadThumbnail(true, false); } else { tizen.application.getCurrentApplication().exit(); } }); this.bindTimerClicks(); $('#thumbnail').on('click', this.launchPreview.bind(this)); $('#shutter').on('touchstart', this.shutterTouched.bind(this)); };
-
Accessing the Camera Stream
- main.js Source File
- The startPreview() method requests the video stream using the getUserMedia() method of the navigator.
navigator.getUserMedia(options, this.onCaptureVideoSuccess.bind(this), this.onCaptureVideoError.bind(this));
- The onCaptureVideoSuccess() event handler obtains the stream URL and creates the video element with the createVideoElement() method.
SelfCamera.prototype.onCaptureVideoSuccess = function onCaptureVideoSuccess(stream) { var urlStream; urlStream = window.webkitURL.createObjectURL(stream); /* Create stream */ this.isMediaWorking = true; this.createVideoElement(urlStream); /* Create video element with stream handler */ this.setTimer(DELAY_2_SECOND); /* Initialize timer button options */ };
-
The createVideoElement() method defines the video element, except for the src attribute, which is already defined by the onCaptureVideoSuccess() event handler.
SelfCamera.prototype.createVideoElement = function(src) { this.video = $('<video/>', { autoplay: 'autoplay', id: 'video', style: 'height:' + $(window).height() + 'px', src: src }).appendTo("#camera").get(0); this.bindVideoEvents(); };
- The startPreview() method requests the video stream using the getUserMedia() method of the navigator.
Capturing Pictures from Video
- main.js Source File
- Capture a video frame with canvas.
SelfCamera.prototype.captureImage = function captureImage(video) { var sourceWidth = window.innerWidth, sourceHeight = window.innerHeight, sourceX = (sourceWidth - $(video).width()) / 2, sourceY = (sourceHeight - $(video).height()) / 2; this.img.width = sourceWidth; this.img.height = sourceHeight; /* Crop image to viewport dimensions */ this.img.getContext('2d').drawImage(video, sourceX, sourceY, $(video).width(), $(video).height()); /* For the best available dimension, use the following: */ /* this.img.width = video.videoWidth; */ /* this.img.height = video.videoHeight; */ /* this.img.getContext('2d').drawImage(video, 0, 0); */ };
- Extract the image data from the canvas.
SelfCamera.prototype.saveCanvas = function saveCanvas(canvas, fileName) { var data, self = this, onSuccess = function(fileHandle) { this.setLoadDirectory(this.getFileDirectoryURI(fileHandle)); tizen.content.scanFile(fileName, function() {self.loadThumbnail(false, false);}, function() {/* Error handling */}); }.bind(this); data = canvas.toDataURL().replace('data:image/png;base64,', '').replace('data:,', ''); this.systemIO.saveFileContent(fileName, data, onSuccess, 'base64'); };
- Save the image.
saveFileContent: function SystemIO_saveFileContent(filePath, fileContent, onSaveSuccess, fileEncoding) { var pathData = this.getPathData(filePath), self = this, fileHandle; function onOpenDirSuccess(dir) { /* Create a new file */ fileHandle = self.createFile(dir, pathData.fileName); if (fileHandle !== false) { /* Save data into the file */ self.writeFile(fileHandle, fileContent, onSaveSuccess, false, fileEncoding); } } /* Open the directory */ this.openDir(pathData.dirName, onOpenDirSuccess); };
- Capture a video frame with canvas.