UE4 Android端使用MediaPlayer注意事项

Posted JoeyShaw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4 Android端使用MediaPlayer注意事项相关的知识,希望对你有一定的参考价值。

文章目录


前言

UE4中播放音频通常使用AudioComponent、MediaSoundComponent(需要挂载MediaPlayer);
前者可播放本地音频资源(eg. cue、wave),后者可播放本地指定路径或在线音频资源等(eg. OpenFile、OpenUrl)。
本文主要介绍在安卓端使用MediaSound播放在线音频需要注意的问题。


一、音量设置无效?

MediaSound(MediaPlayer)组件设置音量接口有’SetVolumeMultiplier’和SetNativeVolume;
如果使用MediaSoundComp:SetVolumeMultiplier设置音量,则在android下无效;
反之使用MediaPlayerComp:SetNativeVolume设置,则在Window下无效;
所以针对不同平台需要使用对应的接口设置。

二、应用休眠恢复后播放中断

播放过程应用切换到后台后重新切回,之前播放的音频中断不会继续播放。

1.原因

当应用挂起的时候,会将MediaPlayer暂停。
引擎代码如下,HandleApplicationHasEnteredForeground可看作Android的OnResume, HandleApplicationWillEnterBackground看作OnPause;
但此处我有点不理解,它是在OnResume的时候将其暂停,OnPause时Start?总感觉这两个设定相反了,或者安卓的机制就是这样设定也可有可能。有了解的朋友欢迎指正!

// 代码文件路径 \\UE_4.26\\Engine\\Plugins\\Media\\AndroidMedia\\Source\\AndroidMedia\\Private\\Player\\AndroidMediaPlayer.cpp

/* FAndroidMediaPlasoundyer callbacks
 *****************************************************************************/

void FAndroidMediaPlayer::HandleApplicationHasEnteredForeground()

	// check state in case changed before ticked
	if ((CurrentState == EMediaState::Playing) && JavaMediaPlayer.IsValid())
	
		JavaMediaPlayer->Pause();
	



void FAndroidMediaPlayer::HandleApplicationWillEnterBackground()

	// check state in case changed before ticked
	if ((CurrentState == EMediaState::Playing) && JavaMediaPlayer.IsValid())
	
		JavaMediaPlayer->Start();
	

2.解决方案

监测应用状态,当应用挂起又重新激活后,及进入OnResume时,再主动调用继续播放接口进行播放。UE4已经提供安卓端应用状态监听组件‘Application Lifecycle’,响应Actor添加改组件接口监听OnPause、OnResume等事件。
示例如下:

~ 小尾巴 ~
文章内容有错误、理解不到位以及有更优方案,欢迎指正 补充 讨论!!! 

Android MediaPlayer 播放音频

本文链接: Android MediaPlayer 播放音频

主要介绍使用MediaPlayer播放音频的方式。关于MediaPlayer的基础知识,比如状态,可以参考Android MediaPlayer 基础简介

为了方便表达,定义变量名为mediaPlayer。

MediaPlayer的使用方式

创建MediaPlayer

可以直接 new MediaPlayer,也可以用MediaPlayer提供的create方法创建。

mediaPlayer = new MediaPlayer();

使用create方法创建成功后,mediaPlayer处于Prepared状态。可以直接start播放。

    mediaPlayer = MediaPlayer.create(getApplicationContext(), Uri.fromFile(file));
    mediaPlayer.start();

设置音源 - setDataSource

通过调用setDataSource来设置音源。setDataSource有多个重载方法,我们来看常用的几种。

例如设置使用assets里的资源。实际情况可能需要try catch。

    AssetFileDescriptor fd = null;
    MediaPlayer mediaPlayer = new MediaPlayer();
    fd = context.getApplicationContext().getAssets().openFd(name);
    mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());

本地文件,需要文件的绝对路径。

mediaPlayer.setDataSource(file.getAbsolutePath());

或者获取文件的Uri来创建mediaPlayer。

mediaPlayer = MediaPlayer.create(getApplicationContext(), Uri.fromFile(file));

设置网络音频,也是用setDataSource方法,设置url。

mediaPlayer.setDataSource("https://demo.com/sample.mp3"));

播放网络音频时,如果使用的是http,有可能会报错

java.io.IOException: Cleartext HTTP traffic to demo.com not permitted

可以简单地设置一下manifest,设置usesCleartextTraffic="true"

<application
        android:usesCleartextTraffic="true">

准备 - prepare

同步和异步准备音频资源。prepareAsync()是异步的方式,prepare是同步的。注意线程调度问题,同时不要阻塞UI线程。

使用异步方式准备音频,经常与MediaPlayer.OnPreparedListener监听器配合使用。异步准备时,也可以进行其他的设置。

    mediaPlayer.prepareAsync();
    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() 
        @Override
        public void onPrepared(MediaPlayer mediaPlayer) 
            mediaPlayer.start(); // 准备好了就播放
        
    );

循环播放 - Looping

设置循环播放setLooping。

mediaPlayer.setLooping(true);

播放完毕后,不会回调OnCompletionListener,而是从头播放当前音频。

播放 - start

播放音频,调用start方法。

mediaPlayer.start();

处于Prepared,Pause和PlaybackComplete状态时,可以调用start方法,进入Started状态。

暂停 - pause

暂停播放,使用pause方法。在暂停前先判断一下mediaPlayer的是否在播放。

    if (mediaPlayer.isPlaying()) 
        mediaPlayer.pause();
    

暂停成功则处于Paused状态。

停止 - stop

回顾一下MediaPlayer状态切换的图示,我们可以得知在播放中,暂停,播放完成这3个状态下,可以调用stop方法,进入Stopped状态。

mediaPlayer.stop();

调进度 - seekTo

调整播放进度。我们平时使用音乐播放软件一般都会有这个功能。
seekTo方法接受一个毫秒参数。

    int targetMS = (int) (percent * mediaPlayer.getDuration());
    mediaPlayer.seekTo(targetMS);

seekTo并不会改变MediaPlayer的状态。

重置 - reset

reset后的mediaPlayer进入Idle状态。需要重新设置音源与准备。

释放 - release

不再使用这个mediaPlayer时,应当尽快释放掉,以释放相关的资源。
调用release后,mediaPlayer进入End状态。此时这个mediaPlayer就不能再使用了。

常用监听器

缓冲监听器 OnBufferingUpdateListener

比如我们加载网络音频的时候,常用这个监听器来监听缓冲进度。显示缓冲进度,也可以提高用户体验。

    mMediaPlayer.prepareAsync();
    mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() 
        @Override
        public void onBufferingUpdate(MediaPlayer mp, int percent) 
            // percent代表缓冲百分比
        
    );

错误监听器 OnErrorListener

mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() 
    @Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int i1) 
        return true; // 返回true表示在此处理错误,不会回调onCompletion
    
);

注意onError的返回值。可以选择自己处理error。

         * @return True if the method handled the error, false if it didn't.
         * Returning false, or not having an OnErrorListener at all, will
         * cause the OnCompletionListener to be called.
         */
        boolean onError(MediaPlayer mp, int what, int extra);

播放完毕监听器 OnCompletionListener

mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() 
    @Override
    public void onCompletion(MediaPlayer mediaPlayer) 
        // 播放完毕
    
);

使用示例

播放assets里的音频

播放assets里的音频文件,使用到AssetFileDescriptor类。使用后记得关闭AssetFileDescriptor。

    private void playAssetsAudio(final String name, Context context) 
        Log.d(TAG, "playAssetWordSound: try to play assets sound file. -> " + name);
        AssetFileDescriptor fd = null;
        try 
            MediaPlayer mediaPlayer;
            Log.v(TAG, "Looking in assets.");
            fd = context.getApplicationContext().getAssets().openFd(name);
            mediaPlayer = new MediaPlayer();
            mediaPlayer.reset();
            mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
            mediaPlayer.prepareAsync();
            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() 
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) 
                    Log.d(TAG, "onPrepared: " + name);
                    mediaPlayer.start();
                
            );
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() 
                @Override
                public void onCompletion(MediaPlayer mp) 
                    mp.release();
                    Log.d(TAG, "onCompletion: " + name);
                
            );
            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() 
                @Override
                public boolean onError(MediaPlayer mp, int i, int i1) 
                    mp.release();
                    return true;
                
            );
         catch (Exception e) 
            try 
                if (fd != null) 
                    fd.close();
                
             catch (Exception e1) 
                Log.e(TAG, "Exception close fd: ", e1);
            
         finally 
            if (fd != null) 
                try 
                    fd.close();
                 catch (IOException e) 
                    Log.e(TAG, "Finally, close fd ", e);
                
            
        
    

播放本地音频文件

尝试播放音频文件。仅播放一次。

    private void playAudioFile(final File file) 
        Log.d(TAG, "playAudioFile: " + file.getAbsolutePath());
        MediaPlayer mediaPlayer;
        try 
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setLooping(false);
            mediaPlayer.setDataSource(file.getAbsolutePath());
            mediaPlayer.prepare();
            mediaPlayer.start();
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() 
                @Override
                public void onCompletion(MediaPlayer mp) 
                    mp.release();
                
            );
            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() 
                @Override
                public boolean onError(MediaPlayer mediaPlayer, int i, int i1) 
                    Log.d(TAG, "Play local sound onError: " + i + ", " + i1);
                    return true;
                
            );
         catch (Exception e) 
            Log.e(TAG, "playAudioFile: ", e);
        
    

播放在线音频

设置url,播放在线音频

    private void playOnlineSound(String soundUrlDict) 
        try 
            MediaPlayer mediaPlayer = new MediaPlayer();
            mediaPlayer.setDataSource(soundUrlDict);
            mediaPlayer.prepareAsync();
            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() 
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) 
                    mediaPlayer.start();
                
            );
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() 
                @Override
                public void onCompletion(MediaPlayer mp) 
                    if (mp != null) 
                        mp.release();
                    
                    Log.d(TAG, "onCompletion: play sound.");
                
            );
            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() 
                @Override
                public boolean onError(MediaPlayer mediaPlayer, int i, int i1) 
                    Log.d(TAG, "Play online sound onError: " + i + ", " + i1);
                    return false;
                
            );
         catch (IOException e1) 
            Log.e(TAG, "url: ", e1);
        
    

代码可以参考示例工程: https://github.com/RustFisher/android-MediaPlayer

以上是关于UE4 Android端使用MediaPlayer注意事项的主要内容,如果未能解决你的问题,请参考以下文章

UE4 移动端最小包

UE4中添加Android BroadcastReceiver

UE4_UE5播放音频(播放停止功能)(附工程)

Android MediaPlayer 播放音频

Android 音频开发之 MediaPlayer

android的MediaPlayer.create方法怎么用