我的项目需要有一个mp3播放器,其中多个mp3文件完全同步播放。 我已经尝试过MediaPlayer,但问题是,在我的循环中,我启动了两个mp3文件,它们略微不同步。当然,它们是在调用Play()之前创建和准备的。 这些不仅仅是声音,而是需要无缝循环的3-4分钟音乐文件。
目前我正在努力使用AudioTrack,因为这些文件是AndroidAssets,当我创建一个流时,ByteReader会出现内存不足错误...
那么有更好的方法来进行这种mp3音乐同步播放吗?
由于 格雷格
答案 0 :(得分:2)
我几天来一直在为同样的问题苦苦挣扎。我的方法是使用MediaExtractor / MediaCodec / AudioTrack让多个线程流/解码/播放mp3文件。我让它在C#中工作,但在播放期间观察了很多GC活动。您可以在下面找到我单个曲目的代码。您可以在此处详细了解我遇到的问题以及我将如何解决这些问题:How to stream data from MediaCodec to AudioTrack with Xamarin for Android。
我还发现,在低端设备上,音轨不会同步播放(延迟为10s到100s ms)。我认为问题是当我做audioTrack.Play()时,AudioTrack在其缓冲区中没有足够的数据来立即开始播放,并且根据输入文件格式,它需要不同数量的mp3帧来填充它,因此曲目以不同的延迟开始。我正在尝试的解决方案是推迟audioTrack.Play(),直到我知道缓冲区有足够的字节(AudioTrack.GetMinBufferSize(...))来立即启动播放,而不是调用audioTrack.Play()。
var fd = this.Resources.OpenRawResourceFd(Resource.Raw.PianoInsideMics);
var extractor = new MediaExtractor();
extractor.SetDataSource(fd.FileDescriptor, fd.StartOffset, fd.Length);
extractor.SelectTrack(0);
var trackFormat = extractor.GetTrackFormat(0);
var decoder = MediaCodec.CreateDecoderByType(trackFormat.GetString(MediaFormat.KeyMime));
decoder.Configure(trackFormat, null, null, MediaCodecConfigFlags.None);
var thread = new Thread(() =>
{
decoder.Start();
var decoderInputBuffers = decoder.GetInputBuffers();
var decoderOutputBuffers = decoder.GetOutputBuffers();
var inputIndex = decoder.DequeueInputBuffer(-1);
var inputBuffer = decoderInputBuffers[inputIndex];
var bufferInfo = new MediaCodec.BufferInfo();
byte[] audioBuffer = null;
AudioTrack audioTrack = null;
var read = extractor.ReadSampleData(inputBuffer, 0);
while (read > 0)
{
decoder.QueueInputBuffer(inputIndex, 0, read, extractor.SampleTime,
extractor.SampleFlags == MediaExtractorSampleFlags.Sync ? MediaCodecBufferFlags.SyncFrame : MediaCodecBufferFlags.None);
extractor.Advance();
var outputIndex = decoder.DequeueOutputBuffer(bufferInfo, -1);
if (outputIndex == (int) MediaCodecInfoState.OutputFormatChanged)
{
trackFormat = decoder.OutputFormat;
}
else if (outputIndex >= 0)
{
if (bufferInfo.Size > 0)
{
var outputBuffer = decoderOutputBuffers[outputIndex];
if (audioBuffer == null || audioBuffer.Length < bufferInfo.Size)
{
audioBuffer = new byte[bufferInfo.Size];
Debug.WriteLine("Allocated new audiobuffer: {0}", audioBuffer.Length);
}
outputBuffer.Rewind();
outputBuffer.Get(audioBuffer, 0, bufferInfo.Size);
decoder.ReleaseOutputBuffer(outputIndex, false);
if (audioTrack == null)
{
var sampleRateInHz = trackFormat.GetInteger(MediaFormat.KeySampleRate);
var channelCount = trackFormat.GetInteger(MediaFormat.KeyChannelCount);
var channelConfig = channelCount == 1 ? ChannelOut.Mono : ChannelOut.Stereo;
audioTrack = new AudioTrack(
Stream.Music,
sampleRateInHz,
channelConfig,
Encoding.Pcm16bit,
AudioTrack.GetMinBufferSize(sampleRateInHz, channelConfig, Encoding.Pcm16bit)*2,
AudioTrackMode.Stream);
audioTrack.Play();
}
audioTrack.Write(audioBuffer, 0, bufferInfo.Size);
}
}
inputIndex = decoder.DequeueInputBuffer(-1);
inputBuffer = decoderInputBuffers[inputIndex];
read = extractor.ReadSampleData(inputBuffer, 0);
}
});