/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ /* global MediaError */ var argscheck = require('cordova/argscheck'), utils = require('cordova/utils'); var mediaObjects = {}; /** * This class provides access to the device media, interfaces to both sound and video * * @constructor * @param src The file name or url to play * @param successCallback The callback to be called when the file is done playing or recording. * successCallback() * @param errorCallback The callback to be called if there is an error. * errorCallback(int errorCode) - OPTIONAL * @param statusCallback The callback to be called when media status has changed. * statusCallback(int statusCode) - OPTIONAL */ var Media = function(src, successCallback, errorCallback, statusCallback) { argscheck.checkArgs('SFFF', 'Media', arguments); this.id = utils.createUUID(); mediaObjects[this.id] = this; this.src = src; this.successCallback = successCallback; this.errorCallback = errorCallback; this.statusCallback = statusCallback; this._duration = -1; this._position = -1; Media.onStatus(this.id, Media.MEDIA_STATE, Media.MEDIA_STARTING); try { this.node = createNode(this); } catch (err) { Media.onStatus(this.id, Media.MEDIA_ERROR, { code: MediaError.MEDIA_ERR_ABORTED }); } }; /** * Creates new Audio node and with necessary event listeners attached * @param {Media} media Media object * @return {Audio} Audio element */ function createNode (media) { var node = new Audio(); node.onloadstart = function () { Media.onStatus(media.id, Media.MEDIA_STATE, Media.MEDIA_STARTING); }; node.onplaying = function () { Media.onStatus(media.id, Media.MEDIA_STATE, Media.MEDIA_RUNNING); }; node.ondurationchange = function (e) { Media.onStatus(media.id, Media.MEDIA_DURATION, e.target.duration || -1); }; node.onerror = function (e) { // Due to media.spec.15 It should return MediaError for bad filename var err = e.target.error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED ? { code: MediaError.MEDIA_ERR_ABORTED } : e.target.error; Media.onStatus(media.id, Media.MEDIA_ERROR, err); }; node.onended = function () { Media.onStatus(media.id, Media.MEDIA_STATE, Media.MEDIA_STOPPED); }; if (media.src) { node.src = media.src; } return node; } // Media messages Media.MEDIA_STATE = 1; Media.MEDIA_DURATION = 2; Media.MEDIA_POSITION = 3; Media.MEDIA_ERROR = 9; // Media states Media.MEDIA_NONE = 0; Media.MEDIA_STARTING = 1; Media.MEDIA_RUNNING = 2; Media.MEDIA_PAUSED = 3; Media.MEDIA_STOPPED = 4; Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"]; /** * Start or resume playing audio file. */ Media.prototype.play = function() { // if Media was released, then node will be null and we need to create it again if (!this.node) { try { this.node = createNode(this); } catch (err) { Media.onStatus(this.id, Media.MEDIA_ERROR, { code: MediaError.MEDIA_ERR_ABORTED }); } } this.node.play(); }; /** * Stop playing audio file. */ Media.prototype.stop = function() { try { this.pause(); this.seekTo(0); Media.onStatus(this.id, Media.MEDIA_STATE, Media.MEDIA_STOPPED); } catch (err) { Media.onStatus(this.id, Media.MEDIA_ERROR, err); } }; /** * Seek or jump to a new time in the track.. */ Media.prototype.seekTo = function(milliseconds) { try { this.node.currentTime = milliseconds / 1000; } catch (err) { Media.onStatus(this.id, Media.MEDIA_ERROR, err); } }; /** * Pause playing audio file. */ Media.prototype.pause = function() { try { this.node.pause(); Media.onStatus(this.id, Media.MEDIA_STATE, Media.MEDIA_PAUSED); } catch (err) { Media.onStatus(this.id, Media.MEDIA_ERROR, err); }}; /** * Get duration of an audio file. * The duration is only set for audio that is playing, paused or stopped. * * @return duration or -1 if not known. */ Media.prototype.getDuration = function() { return this._duration; }; /** * Get position of audio. */ Media.prototype.getCurrentPosition = function(success, fail) { try { var p = this.node.currentTime; Media.onStatus(this.id, Media.MEDIA_POSITION, p); success(p); } catch (err) { fail(err); } }; /** * Start recording audio file. */ Media.prototype.startRecord = function() { Media.onStatus(this.id, Media.MEDIA_ERROR, "Not supported"); }; /** * Stop recording audio file. */ Media.prototype.stopRecord = function() { Media.onStatus(this.id, Media.MEDIA_ERROR, "Not supported"); }; /** * Release the resources. */ Media.prototype.release = function() { try { delete this.node; } catch (err) { Media.onStatus(this.id, Media.MEDIA_ERROR, err); }}; /** * Adjust the volume. */ Media.prototype.setVolume = function(volume) { this.node.volume = volume; }; /** * Audio has status update. * PRIVATE * * @param id The media object id (string) * @param msgType The 'type' of update this is * @param value Use of value is determined by the msgType */ Media.onStatus = function(id, msgType, value) { var media = mediaObjects[id]; if (media) { switch(msgType) { case Media.MEDIA_STATE : if (media.statusCallback) { media.statusCallback(value); } if (value === Media.MEDIA_STOPPED) { if (media.successCallback) { media.successCallback(); } } break; case Media.MEDIA_DURATION : media._duration = value; break; case Media.MEDIA_ERROR : if (media.errorCallback) { media.errorCallback(value); } break; case Media.MEDIA_POSITION : media._position = Number(value); break; default : if (console.error) { console.error("Unhandled Media.onStatus :: " + msgType); } break; } } else if (console.error) { console.error("Received Media.onStatus callback for unknown media :: " + id); } }; module.exports = Media;