Media.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. *
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. *
  20. */
  21. /* global MediaError */
  22. var argscheck = require('cordova/argscheck'),
  23. utils = require('cordova/utils');
  24. var mediaObjects = {};
  25. /**
  26. * This class provides access to the device media, interfaces to both sound and video
  27. *
  28. * @constructor
  29. * @param src The file name or url to play
  30. * @param successCallback The callback to be called when the file is done playing or recording.
  31. * successCallback()
  32. * @param errorCallback The callback to be called if there is an error.
  33. * errorCallback(int errorCode) - OPTIONAL
  34. * @param statusCallback The callback to be called when media status has changed.
  35. * statusCallback(int statusCode) - OPTIONAL
  36. */
  37. var Media = function(src, successCallback, errorCallback, statusCallback) {
  38. argscheck.checkArgs('SFFF', 'Media', arguments);
  39. this.id = utils.createUUID();
  40. mediaObjects[this.id] = this;
  41. this.src = src;
  42. this.successCallback = successCallback;
  43. this.errorCallback = errorCallback;
  44. this.statusCallback = statusCallback;
  45. this._duration = -1;
  46. this._position = -1;
  47. Media.onStatus(this.id, Media.MEDIA_STATE, Media.MEDIA_STARTING);
  48. try {
  49. this.node = createNode(this);
  50. } catch (err) {
  51. Media.onStatus(this.id, Media.MEDIA_ERROR, { code: MediaError.MEDIA_ERR_ABORTED });
  52. }
  53. };
  54. /**
  55. * Creates new Audio node and with necessary event listeners attached
  56. * @param {Media} media Media object
  57. * @return {Audio} Audio element
  58. */
  59. function createNode (media) {
  60. var node = new Audio();
  61. node.onloadstart = function () {
  62. Media.onStatus(media.id, Media.MEDIA_STATE, Media.MEDIA_STARTING);
  63. };
  64. node.onplaying = function () {
  65. Media.onStatus(media.id, Media.MEDIA_STATE, Media.MEDIA_RUNNING);
  66. };
  67. node.ondurationchange = function (e) {
  68. Media.onStatus(media.id, Media.MEDIA_DURATION, e.target.duration || -1);
  69. };
  70. node.onerror = function (e) {
  71. // Due to media.spec.15 It should return MediaError for bad filename
  72. var err = e.target.error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED ?
  73. { code: MediaError.MEDIA_ERR_ABORTED } :
  74. e.target.error;
  75. Media.onStatus(media.id, Media.MEDIA_ERROR, err);
  76. };
  77. node.onended = function () {
  78. Media.onStatus(media.id, Media.MEDIA_STATE, Media.MEDIA_STOPPED);
  79. };
  80. if (media.src) {
  81. node.src = media.src;
  82. }
  83. return node;
  84. }
  85. // Media messages
  86. Media.MEDIA_STATE = 1;
  87. Media.MEDIA_DURATION = 2;
  88. Media.MEDIA_POSITION = 3;
  89. Media.MEDIA_ERROR = 9;
  90. // Media states
  91. Media.MEDIA_NONE = 0;
  92. Media.MEDIA_STARTING = 1;
  93. Media.MEDIA_RUNNING = 2;
  94. Media.MEDIA_PAUSED = 3;
  95. Media.MEDIA_STOPPED = 4;
  96. Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
  97. /**
  98. * Start or resume playing audio file.
  99. */
  100. Media.prototype.play = function() {
  101. // if Media was released, then node will be null and we need to create it again
  102. if (!this.node) {
  103. try {
  104. this.node = createNode(this);
  105. } catch (err) {
  106. Media.onStatus(this.id, Media.MEDIA_ERROR, { code: MediaError.MEDIA_ERR_ABORTED });
  107. }
  108. }
  109. this.node.play();
  110. };
  111. /**
  112. * Stop playing audio file.
  113. */
  114. Media.prototype.stop = function() {
  115. try {
  116. this.pause();
  117. this.seekTo(0);
  118. Media.onStatus(this.id, Media.MEDIA_STATE, Media.MEDIA_STOPPED);
  119. } catch (err) {
  120. Media.onStatus(this.id, Media.MEDIA_ERROR, err);
  121. }
  122. };
  123. /**
  124. * Seek or jump to a new time in the track..
  125. */
  126. Media.prototype.seekTo = function(milliseconds) {
  127. try {
  128. this.node.currentTime = milliseconds / 1000;
  129. } catch (err) {
  130. Media.onStatus(this.id, Media.MEDIA_ERROR, err);
  131. }
  132. };
  133. /**
  134. * Pause playing audio file.
  135. */
  136. Media.prototype.pause = function() {
  137. try {
  138. this.node.pause();
  139. Media.onStatus(this.id, Media.MEDIA_STATE, Media.MEDIA_PAUSED);
  140. } catch (err) {
  141. Media.onStatus(this.id, Media.MEDIA_ERROR, err);
  142. }};
  143. /**
  144. * Get duration of an audio file.
  145. * The duration is only set for audio that is playing, paused or stopped.
  146. *
  147. * @return duration or -1 if not known.
  148. */
  149. Media.prototype.getDuration = function() {
  150. return this._duration;
  151. };
  152. /**
  153. * Get position of audio.
  154. */
  155. Media.prototype.getCurrentPosition = function(success, fail) {
  156. try {
  157. var p = this.node.currentTime;
  158. Media.onStatus(this.id, Media.MEDIA_POSITION, p);
  159. success(p);
  160. } catch (err) {
  161. fail(err);
  162. }
  163. };
  164. /**
  165. * Start recording audio file.
  166. */
  167. Media.prototype.startRecord = function() {
  168. Media.onStatus(this.id, Media.MEDIA_ERROR, "Not supported");
  169. };
  170. /**
  171. * Stop recording audio file.
  172. */
  173. Media.prototype.stopRecord = function() {
  174. Media.onStatus(this.id, Media.MEDIA_ERROR, "Not supported");
  175. };
  176. /**
  177. * Release the resources.
  178. */
  179. Media.prototype.release = function() {
  180. try {
  181. delete this.node;
  182. } catch (err) {
  183. Media.onStatus(this.id, Media.MEDIA_ERROR, err);
  184. }};
  185. /**
  186. * Adjust the volume.
  187. */
  188. Media.prototype.setVolume = function(volume) {
  189. this.node.volume = volume;
  190. };
  191. /**
  192. * Audio has status update.
  193. * PRIVATE
  194. *
  195. * @param id The media object id (string)
  196. * @param msgType The 'type' of update this is
  197. * @param value Use of value is determined by the msgType
  198. */
  199. Media.onStatus = function(id, msgType, value) {
  200. var media = mediaObjects[id];
  201. if (media) {
  202. switch(msgType) {
  203. case Media.MEDIA_STATE :
  204. if (media.statusCallback) {
  205. media.statusCallback(value);
  206. }
  207. if (value === Media.MEDIA_STOPPED) {
  208. if (media.successCallback) {
  209. media.successCallback();
  210. }
  211. }
  212. break;
  213. case Media.MEDIA_DURATION :
  214. media._duration = value;
  215. break;
  216. case Media.MEDIA_ERROR :
  217. if (media.errorCallback) {
  218. media.errorCallback(value);
  219. }
  220. break;
  221. case Media.MEDIA_POSITION :
  222. media._position = Number(value);
  223. break;
  224. default :
  225. if (console.error) {
  226. console.error("Unhandled Media.onStatus :: " + msgType);
  227. }
  228. break;
  229. }
  230. } else if (console.error) {
  231. console.error("Received Media.onStatus callback for unknown media :: " + id);
  232. }
  233. };
  234. module.exports = Media;