android - 如何在Android中同步音频?

标签 android android-mediaplayer android-youtube-api

我需要使用 MediaPlayer 同步音频文件,并使用 YoutubePlayer 从 Youtube 流式传输音频。

我在启动 MediaPlayer 时遇到错误。它在一些延迟后开始,音轨不同步。使用 SoundPool 不好,因为音频文件很重。

是否可以管理 MediaPlayer 的启动,或预缓冲音频(将音频解码为 PCM 并播放?)?

提前致谢。

最佳答案

我不熟悉 YoutubePlayer,但就启动音频文件时减少延迟而言,您最好使用 AudioTrack(或者如果您熟悉 openSL - 请查看此 video)。

我用来同步音频并获得不错结果的一个简单修复方法是设置 AudioTrack 并将 PCM 数据从音频文件读取到字节数组中。然后创建一个AudioTrack播放的线程,在里面写命令。当您准备好开始播放音频文件时,只需在线程上调用 start 即可。显然,采样率、 channel 和格式取决于您流式传输的音频文件。

int minBufferSize = AudioTrack.getMinBufferSize(441000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
at = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
filepath = Environment.getExternalStorageDirectory().getPath() + File.separator + "audiofile.wav";
fp = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "audiofile.wav");

        count = 0;
        data = new byte[(int) fp.length()];
        try {
            fis = new FileInputStream(filepath);
            dis = new DataInputStream(fis); 
            while((count = dis.read(data, 0, (int) fp.length())) > -1){}
            data = null;
            dis.close();
            fis.close();
        } catch (FileNotFoundException e) { 
            e.printStackTrace();
        } catch (IOException e) { 
            e.printStackTrace();
        }

        atThread = new Thread(new Runnable() {
            public void run() { 
                    at.play();
                    at.write(data, 0, (int) fp.length());
                    data = null; at.stop(); at.flush(); at.release();

            }
            });

希望这对您有所帮助!

编辑:

非常感谢@WilliamMorrison 的帮助!修改@inazaruk 的回答后 here ,我能够在不将整个文件写入内存的情况下播放单独的 wave 文件。呸!

AudioTrack song_1, song_2;
boolean stop = false;
Button start_btn, stop_btn;
DataInputStream dis_1, dis_2;
File file_1 = new File(Environment.getExternalStorageDirectory().toString() + "/song_1.wav");
File file_2 = new File(Environment.getExternalStorageDirectory().toString() + "/song_2.wav");
FileInputStream fis_1, fis_2;
int minBufferSize;
Thread thread_1, thread_2;

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    start_btn = (Button)findViewById(R.id.start);
    start_btn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            start();  
        }
    });

    stop_btn = (Button)findViewById(R.id.stop);
    stop_btn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            stop();  
        }
    });      
}  

Runnable read_song1 = new Runnable() {       
    public void run() {

        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        byte [] audio_data = new byte[minBufferSize];
        try {
            while(!stop) {    
                dis_1.read(audio_data, 0, minBufferSize);
                song_1.write(audio_data, 0, audio_data.length);
            }
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    } 
};

Runnable read_song2 = new Runnable()
{       
    public void run() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        byte [] audio_data = new byte[minBufferSize];
        try {
            while(!stop) {    
                dis_2.read(audio_data, 0, minBufferSize);
                song_2.write(audio_data, 0, audio_data.length);
            }
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    } 
};

void start() {

    try {
        fis_1 = new FileInputStream(file_1); fis_2 = new FileInputStream(file_2);
        dis_1 = new DataInputStream(fis_1); dis_2 = new DataInputStream(fis_2); 

        stop = false;

        minBufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

        song_1 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);            
        song_2 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);

        song_1.play(); song_2.play();        
        thread_1 = new Thread(read_song1); thread_2 = new Thread(read_song2);
        thread_1.start(); thread_2.start();
    } 
    catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

void stop() {

    stop = true;          
    song_1.stop(); song_2.stop();
    song_1.flush(); song_2.flush();
    song_1.release(); song_2.release();

    try {
        dis_1.close(); dis_2.close();
        fis_1.close(); fis_2.close();
    } 
    catch (IOException e) {
        e.printStackTrace();
    }
}   

关于android - 如何在Android中同步音频?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17676310/

相关文章:

android - Kotlin:不能将null强制转换为非null类型com.google.android.youtube.player.YouTubePlayerFragment

android - 在 Android 中制作完美的 Chromeless Youtube 播放器

android - 上下文操作栏 : Done button

android - 负灰度值 - 位图

java - 在 Android 应用程序中更新背景颜色并在其间进行短暂 sleep

Android 媒体播放器错误 (-38,0)

java - 使用 AACDecoder 开始新流

android - MediaPlayer 错误代码的完整列表

android - 如何设置任务管理器中显示的服务描述

android - RecyclerView 项目中的 YouTubePlayerView