Skip to content

Cordova - 媒体

cordova-plugin-media 插件提供了在设备上录制和播放音频文件的能力。本教程重点介绍音频播放及其控制。

使用 Cordova CLI 安装插件:

cordova plugin add cordova-plugin-media

如果您要从设备的特定位置播放文件或录制文件,此插件可能还需要 cordova-plugin-file。对于从 www 文件夹或远程 URL 进行的基本播放,单独的 cordova-plugin-media 最初可能就足够了,但 cordova-plugin-file 通常是 Media 插件的好搭档。

# Optional, but often useful with media:
# 可选,但通常在处理 media 时很有用:
cordova plugin add cordova-plugin-file

添加插件后,重新构建您的项目:cordova build <platform>。

将音频文件(例如,sound.mp3)放在您的 www/audio/ 目录中。在本示例中,请创建 www/audio/sound.mp3。如果您没有 MP3 文件,可以在线查找简短的、免版税的声音文件。

在 www/index.html 中,添加用于控制音频播放的按钮:

<body>
<h1>Media Player Demo</h1>
<button id="playAudioBtn">PLAY</button>
<button id="pauseAudioBtn">PAUSE</button>
<button id="stopAudioBtn">STOP</button>
<br><br>
<label for="volumeRange">Volume:</label>
<input type="range" id="volumeRange" min="0" max="1" step="0.1" value="0.5">
<br><br>
<div id="mediaStatus">Status: Idle</div>
<script src="cordova.js"></script>
<script src="js/index.js"></script>
</body>

在 www/js/index.js 中,实现音频控制函数。所有 Media 对象的交互都必须在 deviceready 事件触发后进行。

document.addEventListener('deviceready', onDeviceReady, false);
let myMedia = null;
let mediaTimer = null; // For updating playback position, not used in this basic example
// 用于更新播放位置,在本基本示例中未使用
const audioFilePath = 'audio/sound.mp3'; // Relative path within www folder
// www 文件夹内的相对路径
// For Android, files in www are typically accessed via 'file:///android_asset/www/audio/sound.mp3'
// 在 Android 上,www 文件夹中的文件通常通过 'file:///android_asset/www/audio/sound.mp3' 访问
// The plugin often handles resolving this, but be aware of platform differences for local files.
// 插件通常会处理路径解析,但请注意本地文件在不同平台上的差异。
function onDeviceReady() {
console.log('Device is ready. Media API available.');
console.log('设备已就绪。Media API 可用。');
document.getElementById('playAudioBtn').addEventListener('click', playAudio);
document.getElementById('pauseAudioBtn').addEventListener('click', pauseAudio);
document.getElementById('stopAudioBtn').addEventListener('click', stopAudio);
document.getElementById('volumeRange').addEventListener('input', setVolume);
// Initialize buttons (optional)
// 初始化按钮(可选)
document.getElementById('pauseAudioBtn').disabled = true;
document.getElementById('stopAudioBtn').disabled = true;
}
function updateMediaStatus(statusText) {
document.getElementById('mediaStatus').textContent = `Status: ${statusText}`;
document.getElementById('mediaStatus').textContent = `状态: ${statusText}`;
console.log(`Media Status: ${statusText}`);
console.log(`Media 状态: ${statusText}`);
}
// Media object success callback
// Media 对象成功回调函数
function mediaSuccess() {
updateMediaStatus('Action successful.');
updateMediaStatus('操作成功。');
}
// Media object error callback
// Media 对象错误回调函数
function mediaError(error) {
updateMediaStatus(`Error: Code ${error.code}, Message: ${error.message}`);
updateMediaStatus(`错误: 错误码 ${error.code}, 消息: ${error.message}`);
console.error('Media Error:', error);
console.error('Media 错误:', error);
// Reset UI if playback failed critically
// 如果播放发生严重错误,重置 UI
if (myMedia) myMedia.release(); // Release resources
// 释放资源
myMedia = null;
document.getElementById('playAudioBtn').disabled = false;
document.getElementById('pauseAudioBtn').disabled = true;
document.getElementById('stopAudioBtn').disabled = true;
}
// Media object status callback (optional)
// Media 对象状态回调函数(可选)
function mediaStatusCallback(status) {
if (status === Media.MEDIA_STARTING) updateMediaStatus('Starting...');
if (status === Media.MEDIA_STARTING) updateMediaStatus('正在启动...');
if (status === Media.MEDIA_RUNNING) updateMediaStatus('Playing...');
if (status === Media.MEDIA_RUNNING) updateMediaStatus('正在播放...');
if (status === Media.MEDIA_PAUSED) updateMediaStatus('Paused.');
if (status === Media.MEDIA_PAUSED) updateMediaStatus('已暂停。');
if (status === Media.MEDIA_STOPPED) {
updateMediaStatus('Stopped.');
updateMediaStatus('已停止。');
// When playback finishes naturally or is stopped, release resources and reset UI
// 当播放自然结束或被停止时,释放资源并重置 UI
if (myMedia) myMedia.release();
myMedia = null;
document.getElementById('playAudioBtn').disabled = false;
document.getElementById('pauseAudioBtn').disabled = true;
document.getElementById('stopAudioBtn').disabled = true;
}
}
function getMediaSrc() {
// On Android, files in the 'www' folder are at 'file:///android_asset/www/'.
// 在 Android 上,'www' 文件夹中的文件路径是 'file:///android_asset/www/'。
// On iOS, they are in the app bundle.
// 在 iOS 上,它们位于应用程序 bundle 中。
// The plugin usually resolves relative paths correctly for www assets.
// 插件通常会正确解析 www 资源的相对路径。
// If playing from other locations (e.g., cordova.file.dataDirectory), provide the full native path.
// 如果从其他位置播放(例如,cordova.file.dataDirectory),请提供完整的原生路径。
if (device.platform === "Android") {
return '/android_asset/www/' + audioFilePath;
}
return audioFilePath; // For iOS and browser, relative path often works
// 对于 iOS 和浏览器,相对路径通常有效
}
function playAudio() {
if (myMedia === null) {
const src = getMediaSrc();
myMedia = new Media(src, mediaSuccess, mediaError, mediaStatusCallback);
console.log(`Playing: ${src}`);
console.log(`正在播放: ${src}`);
} else {
// If paused, resume from current position
// 如果已暂停,从当前位置恢复播放
console.log('Resuming playback.');
console.log('正在恢复播放。');
}
myMedia.play();
document.getElementById('playAudioBtn').disabled = true;
document.getElementById('pauseAudioBtn').disabled = false;
document.getElementById('stopAudioBtn').disabled = false;
}
function pauseAudio() {
if (myMedia) {
myMedia.pause();
updateMediaStatus('Paused');
updateMediaStatus('已暂停');
document.getElementById('playAudioBtn').disabled = false;
document.getElementById('pauseAudioBtn').disabled = true;
}
}
function stopAudio() {
if (myMedia) {
myMedia.stop(); // This will trigger MEDIA_STOPPED status, which handles release.
// 这将触发 MEDIA_STOPPED 状态,该状态会处理资源释放。
// No need to call myMedia.release() here if mediaStatusCallback handles it.
// 如果 mediaStatusCallback 处理了资源释放,这里无需调用 myMedia.release()。
// UI update is also handled by mediaStatusCallback.
// UI 更新也由 mediaStatusCallback 处理。
updateMediaStatus('Stopping...'); // Immediate feedback before async stop completes
updateMediaStatus('正在停止...'); // 在异步停止完成前提供即时反馈
}
}
function setVolume() {
if (myMedia) {
const volume = document.getElementById('volumeRange').value;
myMedia.setVolume(volume);
updateMediaStatus(`Volume set to ${volume}`);
updateMediaStatus(`音量设置为 ${volume}`);
}
}
  • new Media(src, mediaSuccess, mediaError, mediaStatus): 创建一个新的 Media 对象。
    • src: 音频文件的 URL。可以是相对路径(从 www 文件夹开始)、绝对本地文件路径(例如,来自 cordova.file.dataDirectory)或远程 URL。
    • mediaSuccess: 可选的回调函数,在操作(播放、暂停、停止、录制)成功完成时执行。
    • mediaError: 可选的回调函数,在发生错误时执行。接收一个包含 code 和 message 的 MediaError 对象。
    • mediaStatus: 可选的回调函数,在媒体播放器状态改变时(例如,正在启动、正在运行、已暂停、已停止)执行。接收一个状态码。
  • media.play(): 开始或恢复播放音频文件。
  • media.pause(): 暂停播放音频文件。
  • media.stop(): 停止播放音频文件。播放位置通常会被重置。
  • media.release(): 释放底层的操作系统音频资源。当不再需要 Media 对象时,务必调用此方法以释放资源,特别是在播放完成或用户导航离开后。示例中的 mediaStatusCallback 在处理 MEDIA_STOPPED 状态时会调用此方法。
  • media.seekTo(milliseconds): 移动播放位置(以毫秒为单位)。
  • media.setVolume(value): 设置音量(0.0 到 1.0)。
  • media.getCurrentPosition(successCallback, errorCallback): 获取当前播放位置(以秒为单位)。
  • media.getDuration(): 获取音频文件的时长(以秒为单位)。如果时长未知,则返回 -1。
  • media.startRecord(): 开始录制音频文件。
  • media.stopRecord(): 停止录制音频文件。

mediaError 回调函数接收一个包含 code 属性的对象:

  • MediaError.MEDIA_ERR_ABORTED (1)
  • MediaError.MEDIA_ERR_NETWORK (2)
  • MediaError.MEDIA_ERR_DECODE (3)
  • MediaError.MEDIA_ERR_NONE_SUPPORTED (4)
  • 资源管理: 当您使用完一个 Media 对象时,务必调用 myMedia.release() 以释放原生音频资源。这对于长时间运行的应用程序或播放多个声音的应用程序尤为重要。
  • 文件路径: 对文件路径要小心。从 www 文件夹开始的相对路径通常很简单。对于 www 之外的文件(例如,录制的音频、下载的文件),请使用从 cordova-plugin-file 获取的完整原生 URI(例如,fileEntry.toURL())。
  • 错误处理: 鲁棒地实现 mediaError 回调函数。
  • 状态回调: 使用 mediaStatus 回调函数响应播放状态的变化(例如,在播放完成时更新 UI)。
  • 多个声音: 如果您需要同时播放多个声音或管理复杂的音频,请考虑此插件是否足够,或者是否需要更高级的音频引擎/库。此插件通常用于更简单、单流的播放/录制。
  • 后台播放: 默认情况下,当应用程序进入后台时,音频可能会停止播放。如果需要后台音频,Android 和 iOS 有特定的平台指南和 Cordova 插件选项(如 cordova-plugin-background-mode 或特定的媒体播放器配置)。
  • 权限: 录制音频需要麦克风权限。插件通常会处理提示用户授权的过程。

有关所有方法、属性和平台特性差异的详细信息,请参阅Cordova Media 插件文档。