【发布时间】:2015-06-25 02:22:08
【问题描述】:
背景:
我正在开发一个 Android 移动应用程序,用户在应用程序中录制音频,然后将其上传到服务器,然后可以将其流式传输回社区。
到目前为止,我们一直使用 .WAV 格式,它可以输出相当大的文件大小,以便通过移动网络上传和流式传输。我正在努力转换为 .AAC 格式,但我了解到不同制造商会出现一些不同的问题。
问题:
- 目前测试的所有 Android 手机 (API-15+) 都可以在 AAC 中录制音频。
- 但是,三星设备不会使用 MediaPlayer 直接播放 AAC 音频文件。相反,它们需要以 AAC 编码,但输出格式为 .MP4
- .MP4 容器中的 AAC 音频文件可以在所有经过测试的设备上完美录制和播放。
- 文件可以正常上传到服务器。
那么这里是主要问题。将文件流式传输到音频播放器时,声音播放器无法解码仅在三星设备上录制的音频。摩托罗拉和 Nexus 手机上录制的 AAC 文件可以与以前的 WAV 文件完美互换。我可以直接在我的桌面上播放声音文件,如果我直接在网络浏览器中打开文件,它们都会播放。
当我使用 AudioContext 流式传输和解码音频时会发生此问题。我相信这个问题与三星音频编解码器缺少标头数据有关,但我在这个问题上投入了大量时间,但还没有找到可行的解决方案。这个问题看起来很简单,但我花了很多工作才弄清楚到底是什么问题。任何帮助深表感谢。
我看到的两种方法是: 1.修复Android应用源代码编码问题 2. 更正浏览器中的错误。我看过有关使用函数尝试重新同步流的帖子,但我不完全了解如何实现。
我认为最好的解决方案是#1。
我在浏览器中下载了两个示例文件并查看了原始数据以进行比较。 (原始数据快照http://i.imgur.com/tF6uHoO.png // 我没有足够的声誉来嵌入图像,但稍后会修改。)
在 Android 中录制代码 RecordActiviy.java
// FILE NAME
mFileName = "file.mp4";
// INITIATE MEDIA RECORDER
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioSamplingRate(48000);
mRecorder.setAudioEncodingBitRate(64000);
try {
mRecorder.prepare();
mRecorder.start();
} catch (IOException e) {
Log.e("AUDIO RECORDING", "prepare() failed");
e.printStackTrace();
}
}
...
getSound.php
...
$File = $path . $filename;
switch($filetype){
case 'wav':
//FILE TYPE WAV
$mime_type = "audio/vnd.wave";
header("Content-type: ".$mime_type);
$test = readfile($file);
break;
case 'mp4':
//FILE TYPE MP4
//$mime_type = "audio/mp4";
$mime_type = "audio/aac";
header("Content-type: ".$mime_type);
$test = readfile($file);
break;
default:
break;
}
soundPlayer.js
...
var player;
function loadPlayer() {
player = new SoundPlayer();
..UI and other stuff..
}
// Build Sound Player Object
function SoundPlayer() {
this.arrayBuffer = null; //array buffer of audio data
this.context = new AudioContext(); //browser sound context
this.duration = null; //Length of audio clip
this.source = null; //Converted audio source
this.trueEnd = true; //Check to see if sound ended due to completion
this.isPlaying = false; //Check to see if sound is currently playing
this.t = 0; //Update variable for time counter
this.startTime = null; //Start time of audio is logged when playAudio()
this.byteData = null;
var sp = this; // sp = soundPlayer (Reference to SoundPlayer obj)
this.playAudio = function() {
this.source = null;
/* ArrayBuffer must be decoded each time play is called
* due to the nature of a decoded AudioBuffer only
* being able to be played once */
sp.context.decodeAudioData(sp.arrayBuffer, decoded);
sp.t = setInterval(updateProgress, 10);
};
this.loadSound = function(pid) {
$.ajax({
async: true,
url: "getSound.php",
data: { soundID: sID },
beforeSend: function(xhr) {
//Allow for binary data
xhr.overrideMimeType("text/plain; charset=x-user-defined");
},
success: function(dataAsString) {
dataAsString = dataAsString.trim();
if( dataAsString.localeCompare("") == 0 ){
alert("Sound File Not Found");
return;
}
// convert string to byte array
var bytes = [];
var i, l = dataAsString.length;
for (i = 0; i < l; ++i) {
bytes.push(dataAsString.charCodeAt(i) & 0xFF);
}
sp.byteData = bytes;
//Convert byte array into audio buffer
var arrayBuffer = new ArrayBuffer(bytes.length);
var bufferView = new Uint8Array(arrayBuffer);
for (var i = 0; i < bytes.length; i++) {
bufferView[i] = bytes[i];
}
sp.arrayBuffer = arrayBuffer;
//get sound duration info
sp.context.decodeAudioData(arrayBuffer, function(buffer) {
sp.duration = buffer.duration;
notify_UI_load_complete();
}, function(error){
**** // WE GET TO THIS BLOCK IN THE BROWSER
**** // RETURNS UNDEFINED
console.log("error decoding "+ arrayBuffer.length);
console.log(arrayBuffer);
console.log("error type "+ error);
});
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("load error");
console.log(textStatus, errorThrown);
alert("Could not load specific sound file");
}
});
}
...
TL;DR Web Audio AudioContext 中的 Android 三星 AAC 音频流错误。网络音频很难。我们是否已经获得了一个通用标准。
【问题讨论】:
-
您是否尝试过在桌面浏览器中播放三星录制的音频?您确实说过“文件”播放,但我并不清楚您指的是哪些文件。另外题外话:“我们能不能得到一个通用标准”:AAC 已经是一个定义明确且质量很好的标准。
-
@TimothyGu 很抱歉不清楚,但是三星文件在桌面上播放正常。流式传输文件时出现问题
-
你的直播怎么样?我没有看到任何流式传输代码。
-
soundPlayer.js 中的 loadSound() 向 getSound.php 发送一个 ajax 请求,getSound.php 获取文件,将其写入输出缓冲区并将其发回。 loadSound 以字符串的形式获取数据,然后遍历块中的数据以生成 byteArray 缓冲区。这可能会帮助php.net/manual/en/function.readfile.php
标签: javascript android html5-audio audio-streaming android-mediarecorder