ex
Fork of mbed-os-example-mbed5-blinky by
dcs-sdk-java-master/app/src/main/java/com/baidu/duer/dcs/devicemodule/audioplayer/AudioPlayerDeviceModule.java
- Committer:
- TMBOY
- Date:
- 2017-07-18
- Revision:
- 45:2aa9f933c8d2
File content as of revision 45:2aa9f933c8d2:
/*
* Copyright (c) 2017 Baidu, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baidu.duer.dcs.devicemodule.audioplayer;
import com.baidu.duer.dcs.devicemodule.audioplayer.message.ClearQueuePayload;
import com.baidu.duer.dcs.devicemodule.audioplayer.message.PlayPayload;
import com.baidu.duer.dcs.devicemodule.audioplayer.message.PlaybackStatePayload;
import com.baidu.duer.dcs.devicemodule.audioplayer.message.StopPayload;
import com.baidu.duer.dcs.devicemodule.audioplayer.report.AudioPlayStateReport;
import com.baidu.duer.dcs.devicemodule.audioplayer.report.AudioPlayerProgressReporter;
import com.baidu.duer.dcs.devicemodule.audioplayer.report.AudioPlayerTimer;
import com.baidu.duer.dcs.devicemodule.system.HandleDirectiveException;
import com.baidu.duer.dcs.framework.BaseDeviceModule;
import com.baidu.duer.dcs.framework.IMessageSender;
import com.baidu.duer.dcs.framework.message.ClientContext;
import com.baidu.duer.dcs.framework.message.Directive;
import com.baidu.duer.dcs.framework.message.Header;
import com.baidu.duer.dcs.systeminterface.IMediaPlayer;
import com.baidu.duer.dcs.util.LogUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* 音乐播放的端能力实现,处理指令:Play,Stop,ClearQueue
* <p>
* Created by guxiuzhong@baidu.com on 2017/5/31.
*/
public class AudioPlayerDeviceModule extends BaseDeviceModule {
private static final String TAG = AudioPlayerDeviceModule.class.getSimpleName();
// 播放列表,先进先出
private LinkedList<PlayPayload.Stream> playQueue = new LinkedList<>();
// 当前stream的token
private String latestStreamToken = "";
// 当前manager的播放器
private IMediaPlayer mediaPlayer;
// 播放上报
private AudioPlayStateReport audioPlayStateReport;
// 开始时的缓冲时间
private long bufferingStartMilliseconds;
// 结束时的缓冲时间
private long bufferingEndMilliseconds;
// 用来统计状态的时间间隔offsetInMilliseconds
private AudioPlayerTimer timer;
// 播放状态的上报eg:暂停,完成等
private AudioPlayerProgressReporter progressReporter;
// 回调接口
private List<IMediaPlayer.IMediaPlayerListener> audioPlayerListeners;
public AudioPlayerDeviceModule(IMediaPlayer mediaPlayer,
IMessageSender messageSender) {
super(ApiConstants.NAMESPACE, messageSender);
this.mediaPlayer = mediaPlayer;
this.mediaPlayer.addMediaPlayerListener(mediaPlayerListener);
this.audioPlayStateReport = new AudioPlayStateReport(getNameSpace(),
messageSender,
audioPlayStateReportListener);
this.timer = new AudioPlayerTimer();
this.progressReporter = new AudioPlayerProgressReporter(
new ProgressReportDelayEventRunnable(audioPlayStateReport),
new ProgressReportIntervalEventRunnable(audioPlayStateReport), timer);
this.audioPlayerListeners = Collections.synchronizedList(
new ArrayList<IMediaPlayer.IMediaPlayerListener>());
}
@Override
public ClientContext clientContext() {
String namespace = ApiConstants.NAMESPACE;
String name = ApiConstants.Events.PlaybackState.NAME;
Header header = new Header(namespace, name);
PlaybackStatePayload payload = new PlaybackStatePayload(latestStreamToken,
mediaPlayer.getCurrentPosition(),
audioPlayStateReport.getState().name());
return new ClientContext(header, payload);
}
@Override
public void handleDirective(Directive directive) throws HandleDirectiveException {
LogUtil.d(TAG, "dcs-play-directive:" + directive.rawMessage);
String directiveName = directive.getName();
LogUtil.d(TAG, "dcs-play-directiveName:" + directiveName);
if (ApiConstants.Directives.Play.NAME.equals(directiveName)) {
handlePlay((PlayPayload) directive.getPayload());
} else if (ApiConstants.Directives.Stop.NAME.equals(directiveName)) {
handleStop((StopPayload) directive.getPayload());
} else if (ApiConstants.Directives.ClearQueue.NAME.equals(directiveName)) {
handleClearQueue((ClearQueuePayload) directive.getPayload());
} else {
String message = "audioPlayer cannot handle the directive";
throw (new HandleDirectiveException(
HandleDirectiveException.ExceptionType.UNSUPPORTED_OPERATION, message));
}
}
/**
* 处理播放指令(Play)
*
* @param payload payload
*/
private void handlePlay(PlayPayload payload) {
PlayPayload.AudioItem item = payload.audioItem;
if (payload.playBehavior == PlayPayload.PlayBehavior.REPLACE_ALL) {
clearAll();
} else if (payload.playBehavior == PlayPayload.PlayBehavior.REPLACE_ENQUEUED) {
clearEnqueued();
}
final PlayPayload.Stream stream = item.stream;
String streamUrl = stream.url;
String streamId = stream.token;
long offset = stream.offsetInMilliseconds;
LogUtil.i(TAG, "URL:" + streamUrl);
LogUtil.i(TAG, "StreamId:" + streamId);
LogUtil.i(TAG, "Offset:" + offset);
add(stream);
}
/**
* 处理停止指令(Stop)
*
* @param payload payload
*/
private void handleStop(StopPayload payload) {
stop();
}
/**
* 处理清空队列指令(Stop)
*
* @param clearQueuePayload clearQueuePayload
*/
private void handleClearQueue(ClearQueuePayload clearQueuePayload) {
// 清除播放列表,并停止当前播放的音频(如果有)
if (clearQueuePayload.clearBehavior == ClearQueuePayload.ClearBehavior.CLEAR_ALL) {
audioPlayStateReport.clearQueueAll();
clearAll();
} else if (clearQueuePayload.clearBehavior == ClearQueuePayload.ClearBehavior.CLEAR_ENQUEUED) {
// 清除播放列表,但不影响当前播放
audioPlayStateReport.clearQueueEnqueued();
clearEnqueued();
}
}
private void add(PlayPayload.Stream stream) {
String expectedPreviousToken = stream.expectedPreviousToken;
boolean startPlaying = playQueue.isEmpty();
if (expectedPreviousToken == null || latestStreamToken.isEmpty()
|| latestStreamToken.equals(expectedPreviousToken)) {
playQueue.add(stream);
}
LogUtil.d(TAG, " coming playQueue size :" + playQueue.size());
if (startPlaying) {
startPlay();
}
}
/**
* 开始播放音乐
*/
private void startPlay() {
if (playQueue.isEmpty()) {
LogUtil.d(TAG, "startPlay-playQueue isEmpty !!");
return;
}
PlayPayload.Stream currentStream = playQueue.peek();
if (currentStream == null) {
return;
}
latestStreamToken = currentStream.token;
String url = currentStream.url;
// 从哪个位置开始播放
long offset = currentStream.offsetInMilliseconds;
// 判断是否是流类型还是URL类型
if (currentStream.hasAttachedContent()) {
mediaPlayer.play(new IMediaPlayer.MediaResource(currentStream.getAttachedContent()));
} else {
mediaPlayer.play(new IMediaPlayer.MediaResource(url));
}
mediaPlayer.seekTo((int) offset);
}
private IMediaPlayer.IMediaPlayerListener mediaPlayerListener = new IMediaPlayer.SimpleMediaPlayerListener() {
// 是否处于暂停
private boolean isPause;
// 是否第一次到达了100
private boolean stutterFinished;
// 是否处于上报ProgressReport事件中
private boolean progressReporting;
// 是否处于缓冲中
private boolean bufferUnderRunInProgress;
@Override
public void onInit() {
super.onInit();
LogUtil.d(TAG, "onInit");
isPause = false;
stutterFinished = false;
progressReporting = false;
bufferUnderRunInProgress = false;
}
@Override
public void onPrepared() {
super.onPrepared();
LogUtil.d(TAG, "onPrepared");
fireOnPrepared();
}
@Override
public void onPlaying() {
super.onPlaying();
LogUtil.d(TAG, "onPlaying");
// 暂停后继续播放
if (isPause) {
isPause = false;
audioPlayStateReport.playbackResumed();
} else {
// 第一次播放
PlayPayload.Stream stream = playQueue.peek();
if (stream == null) {
return;
}
long offset = stream.offsetInMilliseconds;
LogUtil.d(TAG, "onPlaying---Duration----:" + mediaPlayer.getDuration());
timer.reset(offset, mediaPlayer.getDuration());
// 上报PlaybackStarted事件
audioPlayStateReport.playbackStarted();
}
startTimerAndProgressReporter();
fireOnPlaying();
}
@Override
public void onPaused() {
LogUtil.d(TAG, "onPaused");
stopTimerAndProgressReporter();
isPause = true;
audioPlayStateReport.playbackPaused();
fireOnPaused();
}
@Override
public void onStopped() {
super.onStopped();
stopTimerAndProgressReporter();
audioPlayStateReport.playbackStopped();
fireOnStopped();
}
@Override
public void onCompletion() {
LogUtil.d(TAG, "onCompletion");
stopTimerAndProgressReporter();
playQueue.poll();
audioPlayStateReport.playbackFinished();
audioPlayStateReport.playbackNearlyFinished();
if (!playQueue.isEmpty()) {
startPlay();
}
fireOnCompletion();
}
@Override
public void onRelease() {
LogUtil.d(TAG, "onError");
stopTimerAndProgressReporter();
fireOnRelease();
}
@Override
public void onError(String error, IMediaPlayer.ErrorType errorType) {
LogUtil.d(TAG, "onError");
playQueue.clear();
audioPlayStateReport.playbackFailed(errorType);
stopTimerAndProgressReporter();
fireOnError(error, errorType);
}
@Override
public void onBufferingUpdate(int percent) {
LogUtil.d(TAG, "onBufferingUpdate:" + percent);
fireOnBufferingUpdate(percent);
PlayPayload.Stream stream = playQueue.peek();
if (stream == null) {
return;
}
// Play指令有progressReportDelayInMilliseconds
if (!progressReporting && stream.getProgressReportRequired()) {
LogUtil.d(TAG, "onBufferingUpdate:" + percent);
progressReporting = true;
progressReporter.disable();
progressReporter.setup(stream.progressReport);
long offset = stream.offsetInMilliseconds;
timer.reset(offset, mediaPlayer.getDuration());
startTimerAndProgressReporter();
}
// 已经缓冲完成了
if (stutterFinished) {
return;
}
// 开始缓冲
if (!bufferUnderRunInProgress) {
LogUtil.d(TAG, "==playbackStutterStarted");
bufferUnderRunInProgress = true;
bufferingStartMilliseconds = System.currentTimeMillis();
audioPlayStateReport.playbackStutterStarted();
}
// 缓冲完毕后上报playbackStutterFinished
if (percent >= 100) {
stutterFinished = true;
bufferingEndMilliseconds = System.currentTimeMillis();
audioPlayStateReport.playbackStutterFinished();
}
}
};
private void startTimerAndProgressReporter() {
timer.start();
if (progressReporter.isSetup()) {
progressReporter.start();
}
}
private void stopTimerAndProgressReporter() {
timer.stop();
progressReporter.stop();
}
private void clearAll() {
stop();
playQueue.clear();
}
private void clearEnqueued() {
PlayPayload.Stream top = playQueue.poll();
playQueue.clear();
if (top != null) {
playQueue.add(top);
}
}
private void stop() {
if (!playQueue.isEmpty() && isPlayingOrPaused()) {
stopTimerAndProgressReporter();
// 要把播放策略中的对应的那一条删除了
mediaPlayer.stop();
}
}
private boolean isPlaying() {
return (audioPlayStateReport.getState() == AudioPlayStateReport.AudioPlayerState.PLAYING
|| audioPlayStateReport.getState() == AudioPlayStateReport.AudioPlayerState.PAUSED
|| audioPlayStateReport.getState() == AudioPlayStateReport.AudioPlayerState.BUFFER_UNDERRUN);
}
private boolean isPlayingOrPaused() {
return isPlaying() || audioPlayStateReport.getState() == AudioPlayStateReport.AudioPlayerState.PAUSED;
}
@Override
public void release() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer.removeMediaPlayerListener(mediaPlayerListener);
}
stopTimerAndProgressReporter();
audioPlayerListeners.clear();
}
/**
* 播放上报时需要的信息
*/
private AudioPlayStateReport.AudioPlayStateReportListener audioPlayStateReportListener =
new AudioPlayStateReport.AudioPlayStateReportListener() {
@Override
public String getCurrentStreamToken() {
return latestStreamToken;
}
@Override
public long getCurrentOffsetInMilliseconds() {
return getCurrentOffsetInMillisecondsByTime();
}
@Override
public long getStutterDurationInMilliseconds() {
// 缓冲时间ms
return bufferingEndMilliseconds - bufferingStartMilliseconds;
}
};
private long getCurrentOffsetInMillisecondsByTime() {
AudioPlayStateReport.AudioPlayerState playerActivity = audioPlayStateReport.getState();
long offset;
switch (playerActivity) {
case PLAYING:
case PAUSED:
case BUFFER_UNDERRUN:
case STOPPED:
case FINISHED:
offset = timer.getOffsetInMilliseconds();
break;
case IDLE:
default:
offset = 0;
}
LogUtil.d(TAG, "getCurrentOffsetInMilliseconds offset:" + offset);
return offset;
}
private static class ProgressReportDelayEventRunnable implements Runnable {
private final AudioPlayStateReport audioPlayStateReport;
ProgressReportDelayEventRunnable(AudioPlayStateReport audioPlayStateReport) {
this.audioPlayStateReport = audioPlayStateReport;
}
@Override
public void run() {
audioPlayStateReport.reportProgressDelay();
}
}
private static class ProgressReportIntervalEventRunnable implements Runnable {
private final AudioPlayStateReport audioPlayStateReport;
ProgressReportIntervalEventRunnable(AudioPlayStateReport audioPlayStateReport) {
this.audioPlayStateReport = audioPlayStateReport;
}
@Override
public void run() {
audioPlayStateReport.reportProgressInterval();
}
}
private void fireOnPrepared() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onPrepared();
}
}
private void fireOnRelease() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onRelease();
}
}
private void fireOnPlaying() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onPlaying();
}
}
private void fireOnPaused() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onPaused();
}
}
private void fireOnStopped() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onStopped();
}
}
private void fireOnCompletion() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onCompletion();
}
}
private void fireOnError(String error, IMediaPlayer.ErrorType errorType) {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onError(error, errorType);
}
}
private void fireOnBufferingUpdate(int percent) {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onBufferingUpdate(percent);
}
}
private void fireBufferingStart() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onBufferingStart();
}
}
private void fireBufferingEnd() {
for (IMediaPlayer.IMediaPlayerListener listener : audioPlayerListeners) {
listener.onBufferingEnd();
}
}
public void addAudioPlayListener(IMediaPlayer.IMediaPlayerListener listener) {
audioPlayerListeners.add(listener);
}
}
