yangchong il y a 4 ans
Parent
commit
b001ef00d8

+ 1 - 0
AudioPlayer/.gitignore

@@ -0,0 +1 @@
+/build

+ 26 - 0
AudioPlayer/build.gradle

@@ -0,0 +1,26 @@
+apply plugin: 'com.android.library'
+apply from: rootProject.projectDir.absolutePath + "/VideoGradle/video.gradle"
+
+
+android {
+    compileSdkVersion project.ext.androidCompileSdkVersion
+    buildToolsVersion project.ext.androidBuildToolsVersion
+    defaultConfig {
+        minSdkVersion project.ext.androidMinSdkVersion
+        targetSdkVersion project.ext.androidTargetSdkVersion
+        versionCode 1
+        versionName "1.0.0"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation project.ext.AppDependencies['appcompat']
+}

+ 0 - 0
AudioPlayer/consumer-rules.pro


+ 21 - 0
AudioPlayer/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
AudioPlayer/src/androidTest/java/com/yc/audioplayer/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.yc.audioplayer;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.yc.audioplayer.test", appContext.getPackageName());
+    }
+}

+ 5 - 0
AudioPlayer/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.yc.audioplayer">
+
+</manifest>

+ 8 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/AbstractAudio.java

@@ -0,0 +1,8 @@
+package com.yc.audioplayer;
+
+
+public abstract class AbstractAudio implements IAudio, IPlayListener {
+
+    public final Object mMutex = new Object();
+
+}

+ 94 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/AudioManager.java

@@ -0,0 +1,94 @@
+package com.yc.audioplayer;
+
+import android.content.Context;
+
+
+public class AudioManager extends AbstractAudio {
+
+    private final IAudio mTtsEngine;
+    private final IAudio mMediaPlayer;
+    private PlayData mCurrentData;
+    private IAudio mCurrentAudio;
+    private PlayStateListener mPlayStateListener;
+
+    public AudioManager() {
+        mTtsEngine = IAudioService.getInstance();
+        mMediaPlayer = new MediaAudio();
+    }
+    @Override
+    public void init(IPlayListener next, Context context) {
+        mTtsEngine.init(next, context);
+        mMediaPlayer.init(next, context);
+    }
+
+    @Override
+    public void play(PlayData data) {
+        if (null != mPlayStateListener) {
+            mPlayStateListener.onStartPlay();
+        }
+        this.mCurrentData = data;
+        this.mCurrentAudio = data.mPlayTts ? mTtsEngine : mMediaPlayer;
+        this.mCurrentAudio.play(data);
+    }
+
+    /**
+     * 暂停播放内容
+     */
+    @Override
+    public void stop() {
+        if (mCurrentAudio != null) {
+            mCurrentAudio.stop();
+            mCurrentData = null;
+            synchronized (mMutex) {
+                mMutex.notifyAll();
+            }
+        }
+    }
+
+    @Override
+    public void release() {
+        mTtsEngine.release();
+        mMediaPlayer.release();
+    }
+
+    @Override
+    public void pause() {
+        mCurrentAudio.pause();
+    }
+
+    @Override
+    public void resumeSpeaking() {
+        mCurrentAudio.resumeSpeaking();
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return mCurrentAudio != null && mCurrentAudio.isPlaying();
+    }
+
+    @Override
+    public void onCompleted() {
+        if (mCurrentData != null && mCurrentData.getNext() != null) {
+            mCurrentData = mCurrentData.getNext();
+            play(mCurrentData);
+        } else {
+            synchronized (mMutex) {
+                mMutex.notifyAll();
+            }
+            if (null != mPlayStateListener) {
+                mPlayStateListener.onCompletePlay();
+            }
+        }
+    }
+
+    public void setPlayStateListener(PlayStateListener playStateListener) {
+        this.mPlayStateListener = playStateListener;
+    }
+
+    public interface PlayStateListener {
+
+        void onStartPlay();
+
+        void onCompletePlay();
+    }
+}

+ 83 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/AudioTaskDispatcher.java

@@ -0,0 +1,83 @@
+package com.yc.audioplayer;
+
+import android.os.Process;
+
+import com.didi.foundation.sdk.log.LogService;
+import com.didi.sdk.logging.Logger;
+
+/**
+ * @author nate
+ * @since 2018/8/28.
+ */
+public class AudioTaskDispatcher implements IPlayListener {
+
+    private Logger mLogger = LogService.getLogger(AudioTaskDispatcher.class.getSimpleName());
+    private TtsDeque mTaskDeque ;
+    private PlayData mCurrentPlayData;
+    private AbstractAudio mAudioManager;
+
+    private boolean mRunning = true;
+    private Thread mTtsThread;
+
+    private static class Holder {
+        private static final AudioTaskDispatcher INSTANCE = new AudioTaskDispatcher();
+    }
+
+    private AudioTaskDispatcher() {
+    }
+
+    public static AudioTaskDispatcher getInstance() {
+        return Holder.INSTANCE;
+    }
+
+    @Override
+    public void onCompleted() {
+        mAudioManager.onCompleted();
+    }
+
+    public void initialize(final AudioManager manager) {
+        this.mAudioManager = manager;
+        this.mTaskDeque = new TtsDeque();
+        this.mRunning = true;
+        mLogger.debug("AudioTaskDispatcher initialize: ");
+        this.mTtsThread = new Thread() {
+            @Override
+            public void run() {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
+                while (mRunning) {
+                    try {
+                        mLogger.debug("AudioTaskDispatcher is running ");
+                        mCurrentPlayData = mTaskDeque.get();
+                        mAudioManager.play(mCurrentPlayData);
+                        synchronized (manager.mMutex) {
+                            mLogger.debug("AudioTaskDispatcher is wait  " + mCurrentPlayData.getTts());
+                            manager.mMutex.wait();
+                        }
+                    } catch (InterruptedException e) {
+                        e.printStackTrace();
+                        return;
+                    }
+                }
+            }
+        };
+        this.mTtsThread.start();
+    }
+
+    public void addTask(PlayData data) {
+        if (data == null) {
+            return;
+        }
+        if (mCurrentPlayData != null && data.mPriority.ordinal() > mCurrentPlayData.mPriority.ordinal()) {
+            mAudioManager.stop();
+        }
+        mLogger.debug("AudioTaskDispatcher data: " + data.getTts() + data.mPriority);
+        mTaskDeque.add(data);
+    }
+
+    public void release() {
+        mRunning = false;
+        mTaskDeque.clear();
+        mTtsThread.interrupt();
+    }
+
+}

+ 47 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/IAudio.java

@@ -0,0 +1,47 @@
+package com.yc.audioplayer;
+
+import android.content.Context;
+
+
+/**
+ * 音频播放接口定义
+ */
+public interface IAudio {
+
+    void init(IPlayListener next, Context context);
+
+    /**
+     * 播放数据
+     *
+     * @param data {@link PlayData}
+     */
+    void play(PlayData data);
+
+    /**
+     * 停止播放
+     */
+    void stop();
+
+    /**
+     * 释放音频内容
+     */
+    void release();
+
+    /**
+     * 暂停播放
+     */
+    void pause();
+
+    /**
+     * 回复播放
+     */
+    void resumeSpeaking();
+
+    /**
+     * 是否正在播放
+     *
+     * @return true 是 false 否
+     */
+    boolean isPlaying();
+
+}

+ 10 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/IPlayListener.java

@@ -0,0 +1,10 @@
+package com.yc.audioplayer;
+
+
+public interface IPlayListener {
+
+    /**
+     * 播放完成
+     */
+    void onCompleted();
+}

+ 142 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/MediaAudio.java

@@ -0,0 +1,142 @@
+package com.yc.audioplayer;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+
+
+public class MediaAudio extends AbstractAudio {
+
+    private IPlayListener mPlayListener;
+    private Context mContext;
+    private Logger mLogger = LogService.getLogger(MediaAudio.class.getSimpleName());
+
+    private MediaPlayer mMediaPlayer;
+    private boolean mPause = false;
+
+    /**
+     * 完成/出错时的监听接口
+     */
+    private OnCompletionListener mOnCompletionListener = new OnCompletionListener() {
+
+        @Override
+        public void onCompletion(MediaPlayer player) {
+            if (mMediaPlayer != null && player != null && mMediaPlayer == player) {
+                try {
+                    mMediaPlayer.stop();
+                    mMediaPlayer.release();
+                } catch (IllegalStateException e) {
+                    e.printStackTrace();
+                } finally {
+                    mMediaPlayer = null;
+                }
+            }
+            onCompleted();
+        }
+    };
+
+    MediaAudio() {
+    }
+
+    @Override
+    public void init(IPlayListener next, Context context) {
+        this.mPlayListener = next;
+        this.mContext = context;
+    }
+
+    /**
+     * 播放raw资源
+     */
+    @Override
+    public void play(PlayData data) {
+        mLogger.debug("MediaPlay: play resourceId is" + data.getRawId());
+        if (data.getRawId() <= 0) {
+            return;
+        }
+        if (mMediaPlayer == null) {
+            try {
+                mMediaPlayer = new MediaPlayer();
+                AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(data.getRawId());
+
+//                Uri sound = Uri.parse("android.resource://" + mContext.getPackageName() + "/" + data.getRawId());//  res/raw文件中的url地址。
+                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); //set streaming according to ur needs
+                mMediaPlayer.setOnCompletionListener(mOnCompletionListener);
+//                mMediaPlayer.reset();
+                mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+                mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+                    @Override
+                    public void onPrepared(MediaPlayer mp) {
+                        try {
+                            mMediaPlayer.start();
+                        } catch (Throwable e) {
+                            e.printStackTrace();
+                        }
+                    }
+                });
+                mMediaPlayer.prepare();
+            } catch (Throwable e) {
+                e.printStackTrace();
+                mLogger.debug("MediaPlay: play fail");
+                onCompleted();
+            }
+        }
+    }
+
+    /**
+     * 停止播放
+     */
+    @Override
+    public void stop() {
+        if (mMediaPlayer != null) {
+            synchronized (mMediaPlayer) {
+                try {
+                    mMediaPlayer.stop();
+                    mMediaPlayer.release();
+                } catch (IllegalStateException e) {
+                    e.printStackTrace();
+                } finally {
+                    mMediaPlayer = null;
+                }
+            }
+        }
+    }
+
+    @Override
+    public void release() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+    }
+
+    @Override
+    public void pause() {
+        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+            mMediaPlayer.pause();
+            mPause = true;
+        }
+    }
+
+    @Override
+    public void resumeSpeaking() {
+        if (mMediaPlayer != null && mPause) {
+            mMediaPlayer.start();
+            mPause = false;
+        }
+    }
+
+    @Override
+    public boolean isPlaying() {
+        return mMediaPlayer != null && mMediaPlayer.isPlaying();
+    }
+
+    @Override
+    public void onCompleted() {
+        if (mPlayListener != null) {
+            mPlayListener.onCompleted();
+        }
+    }
+
+}

+ 118 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/PlayData.java

@@ -0,0 +1,118 @@
+package com.yc.audioplayer;
+
+/**
+ * 链表结果的播放数据
+ */
+public class PlayData {
+
+    public enum TtsPriority {
+
+        NORMAL_PRIORITY,
+
+        MIDDLE_PRIORITY,
+
+        HIGH_PRIORITY
+    }
+
+    TtsPriority mPriority = TtsPriority.NORMAL_PRIORITY;
+
+    boolean mPlayTts = true;
+
+    private String mTts;
+
+    private int mRawId;
+
+    private PlayData mCurrent;
+
+    private PlayData mNext;
+
+    PlayData() {
+        mCurrent = this;
+        mCurrent.mNext = mNext;
+    }
+
+    PlayData(int mRawId) {
+        this();
+        this.mRawId = mRawId;
+        this.mPlayTts = false;
+    }
+
+    PlayData(String mTts) {
+        this();
+        this.mTts = mTts;
+        this.mPlayTts = true;
+    }
+
+    public String getTts() {
+        return mTts;
+    }
+
+    public int getRawId() {
+        return mRawId;
+    }
+
+    public PlayData getNext() {
+        return mNext;
+    }
+
+    public static class Builder {
+
+        private PlayData mHeaderPlayData;
+
+        private PlayData mCurrentPlayData;
+        private TtsPriority mPriority;
+
+        public Builder(TtsPriority priority) {
+            this.mPriority = priority;
+        }
+
+        public Builder() {
+        }
+
+        public Builder tts(String string) {
+            PlayData data = new PlayData(string);
+            if (mHeaderPlayData == null) {
+                mHeaderPlayData = data;
+                mCurrentPlayData = data;
+            } else {
+                mCurrentPlayData.mNext = data;
+                mCurrentPlayData = data;
+            }
+            if (mPriority != null) {
+                data.mPriority = mPriority;
+            }
+            return this;
+        }
+
+        public Builder rawId(int rawId) {
+            PlayData data = new PlayData(rawId);
+            if (mHeaderPlayData == null) {
+                if (mPriority != null) {
+                    data.mPriority = mPriority;
+                }
+                mHeaderPlayData = data;
+                mCurrentPlayData = data;
+            } else {
+                mCurrentPlayData.mNext = data;
+                mCurrentPlayData = data;
+            }
+            return this;
+        }
+
+        public PlayData build() {
+            return mHeaderPlayData;
+        }
+
+    }
+
+    @Override
+    public String toString() {
+        return "PlayData{" +
+            "priority=" + mPriority +
+            ", mTts='" + mTts + '\'' +
+            ", mRawId=" + mRawId +
+            ", mNext=" + mNext +
+            '}';
+    }
+
+}

+ 72 - 0
AudioPlayer/src/main/java/com/yc/audioplayer/TtsDeque.java

@@ -0,0 +1,72 @@
+package com.yc.audioplayer;
+
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class TtsDeque {
+
+    private Logger mLogger = LogService.getLogger(TtsDeque.class.getSimpleName());
+    private Lock mLock = new ReentrantLock();
+    private final Condition mNotEmpty = mLock.newCondition();
+    private LinkedBlockingDeque<PlayData> mHighDeque = new LinkedBlockingDeque<>();
+    private LinkedBlockingDeque<PlayData> mMiddleDeque = new LinkedBlockingDeque<>();
+    private LinkedBlockingDeque<PlayData> mNormalDeque = new LinkedBlockingDeque<>();
+
+    public void add(PlayData tts) {
+        mLock.lock();
+        try {
+            switch (tts.mPriority) {
+                case HIGH_PRIORITY:
+                    mHighDeque.add(tts);
+                    mLogger.debug("TTS queue add high: " + tts.getTts());
+                    break;
+                case MIDDLE_PRIORITY:
+                    mMiddleDeque.add(tts);
+                    mLogger.debug("TTS queue add  middle: " + tts.getTts());
+                    break;
+                case NORMAL_PRIORITY:
+                    mNormalDeque.add(tts);
+                    mLogger.debug("TTS queue add  normal: " + tts.getTts());
+                    break;
+            }
+            mNotEmpty.signal();
+        } finally {
+            mLock.unlock();
+        }
+    }
+
+    public PlayData get() throws InterruptedException {
+        PlayData data;
+        mLock.lock();
+        try {
+            while ((data = getTts()) == null) {
+                mLogger.debug("TTS queue no data to play ");
+                mNotEmpty.await();
+            }
+            mLogger.debug("TTS queue  will play is" + data.getTts() + " rawId " + data.getRawId());
+        } finally {
+            mLock.unlock();
+        }
+        return data;
+    }
+
+    public PlayData getTts() {
+        PlayData tts = mHighDeque.poll();
+        if (tts == null) {
+            tts = mMiddleDeque.poll();
+        }
+        if (tts == null) {
+            tts = mNormalDeque.poll();
+        }
+        mLogger.debug("TTS queue get data is " + tts);
+        return tts;
+    }
+
+    public void clear() {
+        mHighDeque.clear();
+        mMiddleDeque.clear();
+        mNormalDeque.clear();
+    }
+}

+ 17 - 0
AudioPlayer/src/test/java/com/yc/audioplayer/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.yc.audioplayer;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 3 - 0
settings.gradle

@@ -20,4 +20,7 @@ include ':VideoCache'
 //视频播放器
 include ':VideoPlayer'
 include ':Demo'
+//视频工具
 include ':VideoTool'
+//音频播放
+include ':AudioPlayer'