setNextMediaPlayer方法

在Android4.1及以上版本,setNextMediaPlayer方法实现了MediaPlayer间的无缝播放。在第一个MediaPlayer播放结束前调用此方法,指定第二个MediaPlayer实例,系统会在第一个播放完毕后立即开始播放第二个。该功能涉及jni层、MediaPlayerService及AndroidOutput的switchToNextOutput函数,确保音频流的连续播放,适用于需要平滑切换音频文件的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android r16版本上新增的方法,记录一下

setNextMediaPlayer


摘要:在Android4.1中,MediaPlayer实现两个完全独立的MediaPlayer类上执行无缝播放,这里介绍一下Android是如何实现无缝播放的。

  一、使用方法
  在第一个MediaPlayer类执行结束前的任何时间调用setNextMediaPlayer(MediaPlayer next)这个方法,该方法的参数是第二个文件创建的MediaPlayer实例。然后Android系统将会在您第一个停止的时候紧接着播放第二个文件。

  二、实现
  MediaPlayer类中的setNextMediaPlayer方法一路追踪JNI层的Android_Media_MediaPlayer.cpp,再到MediaPlayer.cpp,通过IMediaPlayer.cpp中的BnMediaPlayer到MediaPlayerService中的Client::setNextPlayer函数。

复制代码
status_t MediaPlayerService::Client::setNextPlayer(const sp<IMediaPlayer>& player) {
    ALOGV("setNextPlayer");
    Mutex::Autolock l(mLock);
    sp<Client> c = static_cast<Client*>(player.get());
    mNextClient = c;
    if (mAudioOutput != NULL && c != NULL) {
        mAudioOutput->setNextOutput(c->mAudioOutput);
    } else {
        ALOGE("no current audio output");
    }
    return OK;
}
复制代码

  从MediaPlayerServie中的client获得另外一个track的AndroidOutput

void MediaPlayerService::AudioOutput::setNextOutput(const sp<AudioOutput>& nextOutput) {
    mNextOutput = nextOutput;
}

  设置好了mNextOutput后,AndroidOutput定义了另外一个重要的函数switchToNextOutput

复制代码
void MediaPlayerService::AudioOutput::switchToNextOutput() {
    ALOGV("switchToNextOutput");
    if (mNextOutput != NULL) {
        if (mCallbackData != NULL) {
            mCallbackData->beginTrackSwitch();
        }
        delete mNextOutput->mCallbackData;
        mNextOutput->mCallbackData = mCallbackData;
        mCallbackData = NULL;
        mNextOutput->mRecycledTrack = mTrack;
        mTrack = NULL;
        mNextOutput->mSampleRateHz = mSampleRateHz;
        mNextOutput->mMsecsPerFrame = mMsecsPerFrame;
        mNextOutput->mBytesWritten = mBytesWritten;
        mNextOutput->mFlags = mFlags;
    }
}
复制代码

  这个函数的调用是在notify函数中,

复制代码
void MediaPlayerService::Client::notify(
        void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
    Client* client = static_cast<Client*>(cookie);

    {
        Mutex::Autolock l(client->mLock);
        if (msg == MEDIA_PLAYBACK_COMPLETE && client->mNextClient != NULL) {
            client->mAudioOutput->switchToNextOutput();
            client->mNextClient->start();
            client->mNextClient->mClient->notify(MEDIA_INFO, MEDIA_INFO_STARTED_AS_NEXT, 0, obj);
        }
    }

    if (MEDIA_INFO == msg &&
        MEDIA_INFO_METADATA_UPDATE == ext1) {
        const media::Metadata::Type metadata_type = ext2;

        if(client->shouldDropMetadata(metadata_type)) {
            return;
        }

        // Update the list of metadata that have changed. getMetadata
        // also access mMetadataUpdated and clears it.
        client->addNewMetadataUpdate(metadata_type);
    }
    ALOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
    client->mClient->notify(msg, ext1, ext2, obj);
}
复制代码

  当前播放的client发出MEDIA_PLAYBACK_COMPLETE消息时,调用switchToNextOutput函数,从而实现了两个独立的MediaPlayer的无缝播放。

  三、总结
  无缝播放是android4.1添加的新的特性,支持音频流在一起播放而不产生停顿。这对需要在不同的音频文件无缝转换的App很有用。


补充: 

Android MediaPlayer使用方法简单介绍

1)如何获得MediaPlayer实例:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//这时就不用调用setDataSource了
 
 
 
2) 如何设置要播放的文件:
MediaPlayer要播放的文件主要包括3个来源:
a. 用户在应用中事先自带的resource资源
例如:MediaPlayer.create(this, R.raw.test);
b. 存储在SD卡或其他文件路径下的媒体文件
例如:mp.setDataSource("/sdcard/test.mp3");
 
c. 网络上的媒体文件
例如:mp.setDataSource(" http://www.citynorth.cn/music/confucius.mp3 ");
 
MediaPlayer的setDataSource一共四个方法:
setDataSource (String path) 
setDataSource (FileDescriptor fd) 
setDataSource (Context context, Uri uri) 
setDataSource (FileDescriptor fd, long offset, long length)
 
其中使用 FileDescriptor时,需要将文件放到与res文件夹平级的assets文件夹里,然后使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
来设置datasource
 
3)对播放器的主要控制方法:
Android通过控制播放器的状态的方式来控制媒体文件的播放,其中:
prepare()和prepareAsync() 提供了同步和异步两种方式设置播放器进入prepare状态,需要注意的是,如果MediaPlayer实例是由create方法创建的,那么第一次启动播放前不需要再调用prepare()了,因为create方法里已经调用过了。
start()是真正启动文件播放的方法,
pause()和stop()比较简单,起到暂停和停止播放的作用,

seekTo()是定位方法,可以让播放器从指定的位置开始播放,需要注意的是该方法是个异步方法,也就是说该方法返回时并不意味着定位完成,尤其是播放的网络文件,真正定位完成时会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的。
release()可以释放播放器占用的资源,一旦确定不再使用播放器时应当尽早调用它释放资源。
reset()可以使播放器从Error状态中恢复过来,重新会到Idle状态。

4)设置播放器的监听器:
MediaPlayer提供了一些设置不同监听器的方法来更好地对播放器的工作状态进行监听,以期及时处理各种情况,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,设置播放器时需要考虑到播放器可能出现的情况设置好监听和处理逻辑,以保持播放器的健壮性。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值