2 Révisions

Auteur SHA1 Message Date
  ykrank cd46619a58 解决编译问题 il y a 5 ans
  ykrank 0017271c28 整合Video 到 flutter il y a 5 ans

+ 2
- 3
android/build.gradle Voir le fichier

@@ -2,7 +2,7 @@ group 'com.shuangyubang.mofun_flutter_plugin_video'
2 2
 version '1.0-SNAPSHOT'
3 3
 
4 4
 buildscript {
5
-    ext.kotlin_version = '1.3.21'
5
+    ext.kotlin_version = '1.3.31'
6 6
     repositories {
7 7
         google()
8 8
         jcenter()
@@ -25,7 +25,7 @@ apply plugin: 'com.android.library'
25 25
 apply plugin: 'kotlin-android'
26 26
 
27 27
 android {
28
-    compileSdkVersion 27
28
+    compileSdkVersion 28
29 29
 
30 30
     sourceSets {
31 31
         main.java.srcDirs += 'src/main/kotlin'
@@ -45,7 +45,6 @@ android {
45 45
 
46 46
 dependencies {
47 47
     implementation fileTree(dir: 'libs', include: ['*.jar'])
48
-    implementation 'com.android.support:appcompat-v7:27.1.1'
49 48
     implementation 'com.android.support.constraint:constraint-layout:1.1.3'
50 49
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
51 50
 }

+ 0
- 187
android/src/main/java/com/fgodt/mixplayer/Render.java Voir le fichier

@@ -1,187 +0,0 @@
1
-package com.fgodt.mixplayer;
2
-
3
-import android.content.Context;
4
-import android.content.res.Configuration;
5
-import android.media.AudioAttributes;
6
-import android.media.AudioFormat;
7
-import android.media.AudioManager;
8
-import android.media.AudioTrack;
9
-import android.opengl.GLSurfaceView;
10
-import android.support.v7.app.AppCompatActivity;
11
-import android.util.Log;
12
-import android.widget.FrameLayout;
13
-
14
-import java.sql.Time;
15
-import java.util.Arrays;
16
-import java.util.Date;
17
-
18
-import static java.lang.Thread.*;
19
-
20
-/**
21
- * Created by rt-zl on 2019/2/15.
22
- */
23
-
24
-public class Render extends VideoRender {
25
-
26
-
27
-
28
-    static {
29
-        System.loadLibrary("mixplayer");
30
-    }
31
-
32
-    private long handler = 0;
33
-
34
-    private native int setRenderHandler(long handler);
35
-
36
-    int asample, ach, abit;
37
-    public int mixUpdateAudio(byte[] data, int size,double pts){
38
-        playTime = pts;
39
-        synchronized (audioBuf) {
40
-            if (audioBufLen - audioHas < size) {
41
-                return -1;
42
-            }
43
-            int len = audioBufLen - audioWPos;
44
-            len = len > size ? size :len;
45
-            System.arraycopy(data,0,audioBuf,audioWPos,len);
46
-            audioWPos += len;
47
-            audioHas +=len;
48
-            if(audioWPos == audioBufLen){
49
-                audioWPos = 0;
50
-            }
51
-            if(len<size){
52
-                int les = size - len;
53
-                System.arraycopy(data,len,audioBuf,audioWPos,les);
54
-                audioWPos +=les;
55
-                audioHas += les;
56
-            }
57
-            return 0;
58
-        }
59
-    }
60
-
61
-
62
-    public int mixOpen(int vWidth,int vHeight,int asample, int ach, int abit){
63
-        if(vWidth>0){
64
-            Log.d("MIXOPEN","has video");
65
-            nativeInit(getScreenWidth(),getScreenHeight());
66
-            this.abit = abit;
67
-            this.asample = asample;
68
-            this.ach = ach;
69
-            initAudioTrack();
70
-            return  0;
71
-        }
72
-        return -1;
73
-    }
74
-
75
-
76
-    public void mixClose(){
77
-        super.close();
78
-    }
79
-
80
-    public void renderClose(){
81
-        play = false;
82
-        if (audioTrack!=null){
83
-            audioTrack.stop();
84
-            totalAudioBuf = 0;
85
-        }
86
-    }
87
-
88
-    public Render(Context context){
89
-        super(context);
90
-    }
91
-
92
-    public void init(long handler){
93
-        this.handler = handler;
94
-        setMixHander(handler);
95
-        this.setRenderHandler(handler);
96
-    }
97
-
98
-    public double getPlaytime() {
99
-        return  playTime;
100
-    }
101
-
102
-    AudioTrack audioTrack ;
103
-
104
-    byte[] audioBuf ;
105
-    long totalAudioBuf;
106
-    int audioHas;
107
-    int audioRPos, audioWPos;
108
-    int audioBufLen = 4096*3;
109
-    long silenceLen = 0;
110
-    boolean play = false;
111
-    double playTime = 0;
112
-    byte[] silencBuf;
113
-    private void initAudioTrack(){
114
-        silencBuf = new byte[4096];
115
-        audioBuf = new byte[audioBufLen];
116
-        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,asample,
117
-                AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT,audioBufLen,AudioTrack.MODE_STREAM);
118
-        play = true;
119
-        new Thread(new Runnable() {
120
-            @Override
121
-            public void run() {
122
-                int wsize = 1024;
123
-                long posPre = 0;
124
-                boolean needUpdate = false;
125
-                int retryCount = 0;
126
-                while (play) {
127
-                    synchronized (audioBuf) {
128
-                        long pos = audioTrack.getPlaybackHeadPosition();
129
-                        // 16bit 2channels = 4 Byte
130
-                        pos *= 4;
131
-                        pos -= silenceLen;
132
-                        if(posPre != pos){
133
-                            posPre = pos;
134
-                            retryCount = 0;
135
-                        }else {
136
-                            if(retryCount > 5)
137
-                                needUpdate = true;
138
-                            retryCount ++;
139
-                        }
140
-                        if (audioHas >= wsize && totalAudioBuf - pos < audioBufLen || pos <= 0 ||needUpdate ) {
141
-                            needUpdate = false;
142
-                            int len = audioBufLen - audioRPos;
143
-                            len = len > wsize? wsize:len;
144
-                            int ret = audioTrack.write(audioBuf, audioRPos, len);
145
-                            audioRPos += ret;
146
-                            audioHas -= ret;
147
-                            totalAudioBuf+=ret;
148
-                            if (audioRPos == audioBufLen) {
149
-                                audioRPos = 0;
150
-                            }
151
-                        }else {
152
-                        //    if (pos == totalAudioBuf && audioHas < wsize){
153
-                        //        int c = audioTrack.write(silencBuf,0,2048);
154
-                        //        silenceLen +=c;
155
-                        //    }
156
-                        }
157
-                        if(ispause){
158
-                            if(audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PAUSED) {
159
-                                audioTrack.pause();
160
-                                audioTrack.setVolume(0);
161
-                            }
162
-                        }else{
163
-                            if(audioTrack.getPlayState()!=AudioTrack.PLAYSTATE_PLAYING) {
164
-                                audioTrack.play();
165
-                                audioTrack.setVolume(volume);
166
-                            }
167
-                        }
168
-                    }
169
-                    try {
170
-                        sleep(3);
171
-                    }catch (Exception ex){
172
-                    }
173
-                }
174
-                Log.d("mixplayer","audio close");
175
-            }
176
-        }).start();
177
-        audioTrack.play();
178
-    }
179
-
180
-    @Override
181
-    protected void finalize() throws Throwable {
182
-        super.finalize();
183
-        play = false;
184
-        renderClose();
185
-    }
186
-
187
-}

+ 0
- 252
android/src/main/java/com/fogdt/mixsample/MainActivity.java Voir le fichier

@@ -1,252 +0,0 @@
1
-package com.fogdt.mixsample;
2
-
3
-import android.Manifest;
4
-import android.app.ActivityManager;
5
-import android.app.AlertDialog;
6
-import android.content.DialogInterface;
7
-import android.content.pm.PackageManager;
8
-import android.content.res.Configuration;
9
-import android.media.AudioFormat;
10
-import android.media.AudioRecord;
11
-import android.media.MediaCodecInfo;
12
-import android.media.MediaRecorder;
13
-import android.os.Environment;
14
-import android.os.PersistableBundle;
15
-import android.support.annotation.Nullable;
16
-import android.support.v4.app.ActivityCompat;
17
-import android.support.v4.content.ContextCompat;
18
-import android.support.v7.app.AppCompatActivity;
19
-import android.os.Bundle;
20
-import android.text.TextPaint;
21
-import android.util.DisplayMetrics;
22
-import android.util.Log;
23
-import android.view.Gravity;
24
-import android.view.SurfaceHolder;
25
-import android.view.SurfaceView;
26
-import android.view.View;
27
-import android.view.ViewGroup;
28
-import android.view.WindowManager;
29
-import android.widget.EditText;
30
-import android.widget.FrameLayout;
31
-import android.widget.TextView;
32
-import android.widget.Toast;
33
-
34
-import com.fgodt.mixplayer.Core;
35
-import com.fgodt.mixplayer.Player;
36
-import com.fgodt.mixplayer.mixPlayerCallBack;
37
-
38
-import java.io.File;
39
-
40
-
41
-public class MainActivity extends AppCompatActivity implements mixPlayerCallBack {
42
-
43
-
44
-    private long handler =0;
45
-
46
-    AudioRecord audioRecord = null;
47
-    @Override
48
-    protected void onCreate(Bundle savedInstanceState) {
49
-        super.onCreate(savedInstanceState);
50
-
51
-
52
-
53
-        setContentView(R.layout.activity_main);
54
-
55
-
56
-
57
-        // Example of a call to a native method
58
-        TextView tv = (TextView) findViewById(R.id.sample_text);
59
-
60
-        tv.setText("el");
61
-        getPermission();
62
-
63
-
64
-        if(savedInstanceState == null)
65
-            test();
66
-    }
67
-
68
-    @Override
69
-    public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
70
-        super.onWindowAttributesChanged(params);
71
-    }
72
-
73
-    private Player mixplayer ;
74
-    private void test(){
75
-
76
-        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,
77
-                FrameLayout.LayoutParams.WRAP_CONTENT);
78
-        params.gravity = Gravity.TOP;
79
-        params.height = 1080*9/16;
80
-        mixplayer = new Player(this);
81
-        mixplayer.setMixCall(this);
82
-        mixplayer.addMixSource("mix1",0,-1);
83
-        mixplayer.addMixSource("silence",0,-1);
84
-       // mixplayer.selectAudioSource("silence");
85
-       // mixplayer.selectAudioSource("mix1");
86
-        addContentView(mixplayer,params);
87
-        editText = findViewById(R.id.editText);
88
-    }
89
-
90
-    private String getPath(){
91
-        String  file = Environment.getExternalStoragePublicDirectory(
92
-                Environment.DIRECTORY_MOVIES)+"" ;
93
-        return file;
94
-    }
95
-
96
-    private EditText editText;
97
-    public void playClick(View sender){
98
-        if(!hasRWPermission) {
99
-            getPermission();
100
-            return;
101
-        }
102
-        mixplayer.open(editText.getText().toString());
103
-    }
104
-
105
-    public void savedClick(View view){
106
-        //mixplayer.selectAudioSource(editText.getText().toString());
107
-        mixplayer.saveFile(editText.getText().toString(),getPath()+"/mixtest.mp4");
108
-        //mixplayer.saveFile(editText.getText().toString(),"rtmp://192.168.3.10/live/abc1234");
109
-    }
110
-
111
-    //10s pcm data
112
-    int totalLen = 4*44100*10;
113
-    byte[] pcmData = new byte[4*44100*10];
114
-    int pcmWritePos = 0;
115
-    public void closeClick(View sender){
116
-       mixplayer.stop();
117
-    }
118
-
119
-    public void recorderClick(View view){
120
-        if(!hasRcordPermission) {
121
-            getPermission();
122
-            return;
123
-        }
124
-
125
-
126
-        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,44100, AudioFormat.CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT,4096*3);
127
-        if(audioRecord.getState() != AudioRecord.STATE_INITIALIZED){
128
-            Toast.makeText(this,"初始化录音失败",Toast.LENGTH_SHORT).show();
129
-            return;
130
-        }
131
-
132
-        if(audioRecord != null){
133
-            audioRecord.startRecording();
134
-            pcmWritePos = 0;
135
-            pcmReadPos = 0;
136
-            new Thread(new Runnable() {
137
-                @Override
138
-                public void run() {
139
-                    while (pcmWritePos<totalLen){
140
-                        int wlen = totalLen-pcmWritePos > 4096? 4096:totalLen-pcmWritePos;
141
-                        int ret = audioRecord.read(pcmData,pcmWritePos,wlen);
142
-                        pcmWritePos+=ret;
143
-                    }
144
-                    audioRecord.stop();
145
-                }
146
-            }).start();
147
-        }
148
-    }
149
-
150
-    public void playerAndResume(View view){
151
-        if(mixplayer.getStatus() == mixplayer.STATUS_PAUSE){
152
-            mixplayer.resume();
153
-        }else if(mixplayer.getStatus() == mixplayer.STATUS_RUNNING){
154
-            mixplayer.pause();
155
-        }
156
-    }
157
-
158
-    @Override
159
-    protected void onSaveInstanceState(Bundle outState) {
160
-        super.onSaveInstanceState(outState);
161
-    }
162
-
163
-
164
-    @Override
165
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {
166
-        super.onRestoreInstanceState(savedInstanceState);
167
-    }
168
-
169
-
170
-    int pcmReadPos = 0;
171
-    @Override
172
-    public int mix_call(String name, byte[] data, int len) {
173
-        if(name.equals("silence")){
174
-            return 0;
175
-        }
176
-        if(totalLen - pcmReadPos < len){
177
-            return 0;
178
-        }else {
179
-            System.arraycopy(pcmData,pcmReadPos,data,0,len);
180
-            pcmReadPos += len;
181
-            return len;
182
-        }
183
-    }
184
-
185
-    @Override
186
-    public void closeMsg(String msg) {
187
-        Toast.makeText(this,"MIX CLOSE",Toast.LENGTH_SHORT).show();
188
-    }
189
-
190
-
191
-    private boolean hasRWPermission = false;
192
-    private boolean hasRcordPermission = false;
193
-
194
-    private  void getPermission(){
195
-        int has = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
196
-        if (has == PackageManager.PERMISSION_GRANTED){
197
-            hasRWPermission = true;
198
-        }else {
199
-            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE  },1);
200
-        }
201
-
202
-        has = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.RECORD_AUDIO);
203
-        if (has == PackageManager.PERMISSION_GRANTED){
204
-            hasRcordPermission = true;
205
-        }else {
206
-            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.RECORD_AUDIO},2);
207
-        }
208
-    }
209
-
210
-    @Override
211
-    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){
212
-        if(requestCode == 1){
213
-            if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
214
-                hasRWPermission = true;
215
-            }else {
216
-                if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)){
217
-                    new AlertDialog.Builder(this)
218
-                            .setMessage("need read write permisson")
219
-                            .setPositiveButton("ok",(d,w)->{
220
-                                if (w == DialogInterface.BUTTON_POSITIVE){
221
-                                    ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
222
-                                }
223
-                            })
224
-                            .setNegativeButton("cancle",null)
225
-                            .setTitle("需要提供相应权限")
226
-                            .create()
227
-                            .show();
228
-                }
229
-            }
230
-        }
231
-        if (requestCode == 2){
232
-            if(grantResults.length>0&&grantResults[0] == PackageManager.PERMISSION_GRANTED){
233
-                hasRcordPermission = true;
234
-            }else {
235
-                if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.RECORD_AUDIO)){
236
-                    new AlertDialog.Builder(this)
237
-                            .setMessage("need record audio permisson")
238
-                            .setPositiveButton("ok",(dialog,which)->{
239
-                                if (which == DialogInterface.BUTTON_POSITIVE){
240
-                                    ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.RECORD_AUDIO},1);
241
-                                }
242
-                            })
243
-                            .setNegativeButton("cancle",null)
244
-                            .setTitle("需要录音权限")
245
-                            .create()
246
-                            .show();
247
-                }
248
-            }
249
-        }
250
-    }
251
-
252
-}

android/src/main/java/com/fgodt/mixplayer/Core.java → android/src/main/java/com/shuangyubang/mofun_flutter_plugin_video/mixplayer/Core.java Voir le fichier

@@ -1,11 +1,7 @@
1
-package com.fgodt.mixplayer;
1
+package com.shuangyubang.mofun_flutter_plugin_video.mixplayer;
2 2
 
3 3
 import android.util.Log;
4 4
 import android.view.Surface;
5
-import android.view.SurfaceHolder;
6
-
7
-
8
-import java.util.Arrays;
9 5
 
10 6
 /**
11 7
  * Created by rt-zl on 2019/2/15.

android/src/main/java/com/fgodt/mixplayer/Player.java → android/src/main/java/com/shuangyubang/mofun_flutter_plugin_video/mixplayer/Player.java Voir le fichier

@@ -1,4 +1,4 @@
1
-package com.fgodt.mixplayer;
1
+package com.shuangyubang.mofun_flutter_plugin_video.mixplayer;
2 2
 
3 3
 import android.content.Context;
4 4
 
@@ -12,12 +12,12 @@ import java.util.ArrayList;
12 12
 public class Player extends Render {
13 13
 
14 14
 
15
-    public final int STATUS_ERROR = 0;
16
-    public final int STATUS_STOP = 1;
17
-    public final int STATUS_START = 2;
18
-    public final int STATUS_PAUSE = 3;
19
-    public final int STATUS_BUFFING = 4;
20
-    public final int STATUS_RUNNING = 5;
15
+    public static final int STATUS_ERROR = 0;
16
+    public static final int STATUS_STOP = 1;
17
+    public static final int STATUS_START = 2;
18
+    public static final int STATUS_PAUSE = 3;
19
+    public static final int STATUS_BUFFING = 4;
20
+    public static final int STATUS_RUNNING = 5;
21 21
 
22 22
     private Core mixCore;
23 23
     private Core enMixCore;

+ 188
- 0
android/src/main/java/com/shuangyubang/mofun_flutter_plugin_video/mixplayer/Render.java Voir le fichier

@@ -0,0 +1,188 @@
1
+package com.shuangyubang.mofun_flutter_plugin_video.mixplayer;
2
+
3
+import android.content.Context;
4
+import android.media.AudioFormat;
5
+import android.media.AudioManager;
6
+import android.media.AudioTrack;
7
+import android.os.Build;
8
+import android.util.Log;
9
+
10
+import static java.lang.Thread.sleep;
11
+
12
+/**
13
+ * Created by rt-zl on 2019/2/15.
14
+ */
15
+
16
+public class Render extends VideoRender {
17
+
18
+
19
+    static {
20
+        System.loadLibrary("mixplayer");
21
+    }
22
+
23
+    private long handler = 0;
24
+
25
+    private native int setRenderHandler(long handler);
26
+
27
+    int asample, ach, abit;
28
+
29
+    public int mixUpdateAudio(byte[] data, int size, double pts) {
30
+        playTime = pts;
31
+        synchronized (audioBufLock) {
32
+            if (audioBufLen - audioHas < size) {
33
+                return -1;
34
+            }
35
+            int len = audioBufLen - audioWPos;
36
+            len = len > size ? size : len;
37
+            System.arraycopy(data, 0, audioBuf, audioWPos, len);
38
+            audioWPos += len;
39
+            audioHas += len;
40
+            if (audioWPos == audioBufLen) {
41
+                audioWPos = 0;
42
+            }
43
+            if (len < size) {
44
+                int les = size - len;
45
+                System.arraycopy(data, len, audioBuf, audioWPos, les);
46
+                audioWPos += les;
47
+                audioHas += les;
48
+            }
49
+            return 0;
50
+        }
51
+    }
52
+
53
+
54
+    public int mixOpen(int vWidth, int vHeight, int asample, int ach, int abit) {
55
+        if (vWidth > 0) {
56
+            Log.d("MIXOPEN", "has video");
57
+            nativeInit(getScreenWidth(), getScreenHeight());
58
+            this.abit = abit;
59
+            this.asample = asample;
60
+            this.ach = ach;
61
+            initAudioTrack();
62
+            return 0;
63
+        }
64
+        return -1;
65
+    }
66
+
67
+
68
+    public void mixClose() {
69
+        super.close();
70
+    }
71
+
72
+    public void renderClose() {
73
+        play = false;
74
+        if (audioTrack != null) {
75
+            audioTrack.stop();
76
+            totalAudioBuf = 0;
77
+        }
78
+    }
79
+
80
+    public Render(Context context) {
81
+        super(context);
82
+    }
83
+
84
+    public void init(long handler) {
85
+        this.handler = handler;
86
+        setMixHander(handler);
87
+        this.setRenderHandler(handler);
88
+    }
89
+
90
+    public double getPlaytime() {
91
+        return playTime;
92
+    }
93
+
94
+    AudioTrack audioTrack;
95
+
96
+    final Object audioBufLock = new Object();
97
+    byte[] audioBuf;
98
+    long totalAudioBuf;
99
+    int audioHas;
100
+    int audioRPos, audioWPos;
101
+    int audioBufLen = 4096 * 3;
102
+    long silenceLen = 0;
103
+    boolean play = false;
104
+    double playTime = 0;
105
+    byte[] silencBuf;
106
+
107
+    private void initAudioTrack() {
108
+        silencBuf = new byte[4096];
109
+        synchronized (audioBufLock) {
110
+            audioBuf = new byte[audioBufLen];
111
+        }
112
+        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, asample,
113
+                AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, audioBufLen, AudioTrack.MODE_STREAM);
114
+        play = true;
115
+        new Thread(() -> {
116
+            int wsize = 1024;
117
+            long posPre = 0;
118
+            boolean needUpdate = false;
119
+            int retryCount = 0;
120
+            while (play) {
121
+                synchronized (audioBufLock) {
122
+                    long pos = audioTrack.getPlaybackHeadPosition();
123
+                    // 16bit 2channels = 4 Byte
124
+                    pos *= 4;
125
+                    pos -= silenceLen;
126
+                    if (posPre != pos) {
127
+                        posPre = pos;
128
+                        retryCount = 0;
129
+                    } else {
130
+                        if (retryCount > 5)
131
+                            needUpdate = true;
132
+                        retryCount++;
133
+                    }
134
+                    if (audioHas >= wsize && totalAudioBuf - pos < audioBufLen || pos <= 0 || needUpdate) {
135
+                        needUpdate = false;
136
+                        int len = audioBufLen - audioRPos;
137
+                        len = len > wsize ? wsize : len;
138
+                        int ret = audioTrack.write(audioBuf, audioRPos, len);
139
+                        audioRPos += ret;
140
+                        audioHas -= ret;
141
+                        totalAudioBuf += ret;
142
+                        if (audioRPos == audioBufLen) {
143
+                            audioRPos = 0;
144
+                        }
145
+                    } else {
146
+                        //    if (pos == totalAudioBuf && audioHas < wsize){
147
+                        //        int c = audioTrack.write(silencBuf,0,2048);
148
+                        //        silenceLen +=c;
149
+                        //    }
150
+                    }
151
+                    if (ispause) {
152
+                        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PAUSED) {
153
+                            audioTrack.pause();
154
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
155
+                                audioTrack.setVolume(0);
156
+                            } else {
157
+                                audioTrack.setStereoVolume(0, 0);
158
+                            }
159
+                        }
160
+                    } else {
161
+                        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
162
+                            audioTrack.play();
163
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
164
+                                audioTrack.setVolume(volume);
165
+                            } else {
166
+                                audioTrack.setStereoVolume(volume, volume);
167
+                            }
168
+                        }
169
+                    }
170
+                }
171
+                try {
172
+                    sleep(3);
173
+                } catch (Exception ignored) {
174
+                }
175
+            }
176
+            Log.d("mixplayer", "audio close");
177
+        }).start();
178
+        audioTrack.play();
179
+    }
180
+
181
+    @Override
182
+    protected void finalize() throws Throwable {
183
+        super.finalize();
184
+        play = false;
185
+        renderClose();
186
+    }
187
+
188
+}

android/src/main/java/com/fgodt/mixplayer/VideoRender.java → android/src/main/java/com/shuangyubang/mofun_flutter_plugin_video/mixplayer/VideoRender.java Voir le fichier

@@ -1,4 +1,4 @@
1
-package com.fgodt.mixplayer;
1
+package com.shuangyubang.mofun_flutter_plugin_video.mixplayer;
2 2
 
3 3
 
4 4
 

android/src/main/java/com/fgodt/mixplayer/mixPlayerCallBack.java → android/src/main/java/com/shuangyubang/mofun_flutter_plugin_video/mixplayer/mixPlayerCallBack.java Voir le fichier

@@ -1,4 +1,4 @@
1
-package com.fgodt.mixplayer;
1
+package com.shuangyubang.mofun_flutter_plugin_video.mixplayer;
2 2
 
3 3
 public interface mixPlayerCallBack{
4 4
     public int mix_call(String name, byte[] data, int len);

+ 88
- 23
android/src/main/kotlin/com/shuangyubang/mofun_flutter_plugin_video/MofunRealVideoView.kt Voir le fichier

@@ -1,49 +1,114 @@
1 1
 package com.shuangyubang.mofun_flutter_plugin_video
2 2
 
3 3
 import android.content.Context
4
-import android.view.Gravity
4
+import android.media.AudioFormat
5
+import android.media.AudioRecord
6
+import android.media.MediaRecorder
5 7
 import android.view.View
6
-import android.widget.EditText
7 8
 import android.widget.FrameLayout
8
-import android.widget.LinearLayout
9
-import com.fgodt.mixplayer.Player
10
-import com.fgodt.mixplayer.mixPlayerCallBack
9
+import android.widget.Toast
10
+import com.shuangyubang.mofun_flutter_plugin_video.mixplayer.Player
11
+import com.shuangyubang.mofun_flutter_plugin_video.mixplayer.mixPlayerCallBack
11 12
 
12 13
 class MofunRealVideoView(val context: Context) : mixPlayerCallBack {
13 14
 
14
-    private var trueView:FrameLayout? = null
15
+    private var trueView: FrameLayout? = null
16
+    private var mixplayer: Player? = null
17
+    private var url: String? = null
15 18
 
16
-    fun getView(): View? {
19
+    //10s pcm data
20
+    private var totalLen = 4 * 44100 * 10
21
+    private var pcmData = ByteArray(4 * 44100 * 10)
22
+    private var pcmWritePos = 0
23
+
24
+    private var pcmReadPos = 0
25
+
26
+    fun getView(): View {
17 27
         synchronized(MofunRealVideoView::class) {
18
-            if (trueView == null){
28
+            if (trueView == null) {
19 29
                 initView()
20 30
             }
21 31
         }
22
-        return trueView
32
+        return trueView!!
33
+    }
34
+
35
+    fun dispose(){
36
+        mixplayer?.stop()
23 37
     }
24 38
 
25
-    private fun initView(){
39
+    private fun initView() {
26 40
         trueView = FrameLayout(context)
27 41
         val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
28
-                FrameLayout.LayoutParams.WRAP_CONTENT)
29
-        params.gravity = Gravity.TOP
30
-        params.height = 1080 * 9 / 16
31
-        val mixplayer = Player(context)
32
-        mixplayer.setMixCall(this)
33
-        mixplayer.addMixSource("mix1", 0.0, -1.0)
34
-        mixplayer.addMixSource("silence", 0.0, -1.0)
35
-        // mixplayer.selectAudioSource("silence");
36
-        // mixplayer.selectAudioSource("mix1");
37
-
38
-        trueView?.addView(mixplayer, params)
42
+                FrameLayout.LayoutParams.MATCH_PARENT)
43
+        mixplayer = Player(context)
44
+        mixplayer?.apply {
45
+            this.setMixCall(this@MofunRealVideoView)
46
+            this.addMixSource("mix1", 0.0, -1.0)
47
+            this.addMixSource("silence", 0.0, -1.0)
48
+            // mixplayer.selectAudioSource("silence");
49
+            // mixplayer.selectAudioSource("mix1");
39 50
 
51
+            trueView?.addView(this, params)
52
+        }
40 53
     }
41 54
 
42 55
     override fun mix_call(name: String?, data: ByteArray?, len: Int): Int {
43
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
56
+        if (name == "silence") {
57
+            return 0
58
+        }
59
+        if (totalLen - pcmReadPos < len) {
60
+            return 0
61
+        } else {
62
+            System.arraycopy(pcmData, pcmReadPos, data, 0, len)
63
+            pcmReadPos += len
64
+            return len
65
+        }
44 66
     }
45 67
 
46 68
     override fun closeMsg(msg: String?) {
47
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
69
+        Toast.makeText(context, "MIX CLOSE", Toast.LENGTH_SHORT).show()
70
+    }
71
+
72
+    fun play(url: String) {
73
+        this.url = url
74
+        mixplayer?.open(url)
75
+    }
76
+
77
+    fun save(output: String) {
78
+        if (url != null)
79
+            mixplayer?.saveFile(url, output)
80
+    }
81
+
82
+    fun stop() {
83
+        mixplayer?.stop()
84
+    }
85
+
86
+    fun record() {
87
+        val audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, 4096 * 3)
88
+        if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
89
+            Toast.makeText(context, "初始化录音失败", Toast.LENGTH_SHORT).show()
90
+            return
91
+        }
92
+
93
+        audioRecord.startRecording()
94
+        pcmWritePos = 0
95
+        pcmReadPos = 0
96
+        Thread(Runnable {
97
+            while (pcmWritePos < totalLen) {
98
+                val wlen = if (totalLen - pcmWritePos > 4096) 4096 else totalLen - pcmWritePos
99
+                val ret = audioRecord.read(pcmData, pcmWritePos, wlen)
100
+                pcmWritePos += ret
101
+            }
102
+            audioRecord.stop()
103
+        }).start()
104
+
105
+    }
106
+
107
+    fun playerAndResume() {
108
+        if (mixplayer?.status == Player.STATUS_PAUSE) {
109
+            mixplayer?.resume()
110
+        } else if (mixplayer?.status == Player.STATUS_RUNNING) {
111
+            mixplayer?.pause()
112
+        }
48 113
     }
49 114
 }

+ 40
- 9
android/src/main/kotlin/com/shuangyubang/mofun_flutter_plugin_video/MofunVideoView.kt Voir le fichier

@@ -4,6 +4,7 @@ import android.content.Context
4 4
 import android.graphics.Color
5 5
 import android.view.View
6 6
 import android.widget.TextView
7
+import android.widget.Toast
7 8
 import io.flutter.plugin.common.BinaryMessenger
8 9
 import io.flutter.plugin.common.MethodCall
9 10
 import io.flutter.plugin.common.MethodChannel
@@ -17,35 +18,65 @@ const val Channel_Mofun_Video_Info = "plugins.shuangyunbang.mofun/mofunvideoview
17 18
 class MofunVideoView
18 19
     : PlatformView, MethodChannel.MethodCallHandler {
19 20
 
20
-    private val textView: TextView
21
+    private val videoView: MofunRealVideoView
21 22
 
22 23
     constructor(context: Context, messenger: BinaryMessenger, id: Int, creationParams: Any?) {
23
-        textView = TextView(context)
24
-        textView.setBackgroundColor(Color.BLUE)
24
+        videoView = MofunRealVideoView(context)
25 25
         MethodChannel(messenger, Channel_Mofun_Video_Info + "_$id").setMethodCallHandler(this)
26 26
     }
27 27
 
28 28
     override fun getView(): View {
29
-        return textView
29
+        return videoView.getView()
30 30
     }
31 31
 
32 32
     override fun dispose() {
33
-
33
+        videoView.dispose()
34 34
     }
35 35
 
36 36
     override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
37 37
         when (methodCall.method) {
38
-            "setText" -> setText(methodCall, result)
38
+            "toast" -> toast(methodCall, result)
39
+            "play" -> play(methodCall, result)
40
+            "stop" -> stop(methodCall, result)
41
+            "save" -> save(methodCall, result)
42
+            "record" -> record(methodCall, result)
43
+            "playerAndResume" -> playerAndResume(methodCall, result)
39 44
             else -> result.notImplemented()
40 45
         }
41 46
     }
42 47
 
43
-    private fun setText(methodCall: MethodCall, result: MethodChannel.Result) {
44
-        val text = methodCall.arguments as String
45
-        textView.text = text
48
+    private fun toast(methodCall: MethodCall, result: MethodChannel.Result) {
49
+        val url = methodCall.arguments as String
50
+        Toast.makeText(videoView.context, url, Toast.LENGTH_SHORT).show()
51
+        result.success(null)
52
+    }
53
+
54
+    private fun play(methodCall: MethodCall, result: MethodChannel.Result) {
55
+        val url = methodCall.arguments as String
56
+        videoView.play(url)
46 57
         result.success(null)
47 58
     }
48 59
 
60
+    private fun stop(methodCall: MethodCall, result: MethodChannel.Result) {
61
+        videoView.stop()
62
+        result.success(null)
63
+    }
64
+
65
+    private fun save(methodCall: MethodCall, result: MethodChannel.Result) {
66
+        val output = methodCall.arguments as String
67
+        videoView.save(output)
68
+        result.success(null)
69
+    }
70
+
71
+    private fun record(methodCall: MethodCall, result: MethodChannel.Result) {
72
+        videoView.record()
73
+        result.success(null)
74
+    }
75
+
76
+    private fun playerAndResume(methodCall: MethodCall, result: MethodChannel.Result) {
77
+        videoView.playerAndResume()
78
+        result.success(null)
79
+    }
49 80
 }
50 81
 
51 82
 class MofunVideoViewFactory(val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {

+ 8
- 2
example/android/app/build.gradle Voir le fichier

@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
26 26
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 27
 
28 28
 android {
29
-    compileSdkVersion 27
29
+    compileSdkVersion 28
30 30
 
31 31
     sourceSets {
32 32
         main.java.srcDirs += 'src/main/kotlin'
@@ -40,10 +40,11 @@ android {
40 40
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 41
         applicationId "com.shuangyubang.mofun_flutter_plugin_video_example"
42 42
         minSdkVersion 16
43
-        targetSdkVersion 27
43
+        targetSdkVersion 28
44 44
         versionCode flutterVersionCode.toInteger()
45 45
         versionName flutterVersionName
46 46
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
47
+
47 48
     }
48 49
 
49 50
     buildTypes {
@@ -59,6 +60,11 @@ flutter {
59 60
     source '../..'
60 61
 }
61 62
 
63
+tasks.withType(JavaCompile) {
64
+    options.encoding = 'UTF-8'
65
+    options.compilerArgs << '-Xlint:unchecked,deprecation'
66
+}
67
+
62 68
 dependencies {
63 69
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 70
     testImplementation 'junit:junit:4.12'

+ 1
- 1
example/android/build.gradle Voir le fichier

@@ -1,5 +1,5 @@
1 1
 buildscript {
2
-    ext.kotlin_version = '1.3.21'
2
+    ext.kotlin_version = '1.3.31'
3 3
     repositories {
4 4
         google()
5 5
         jcenter()

+ 1
- 0
example/android/gradle.properties Voir le fichier

@@ -1 +1,2 @@
1 1
 org.gradle.jvmargs=-Xmx1536M
2
+android.useAndroidX=true

+ 68
- 23
example/lib/main.dart Voir le fichier

@@ -43,31 +43,76 @@ class _MyAppState extends State<MyApp> {
43 43
 
44 44
   @override
45 45
   Widget build(BuildContext context) {
46
+    const String defaultUrl = 'rtmp://live.hkstv.hk.lxdns.com/live/hks1';
47
+
46 48
     return MaterialApp(
47 49
       home: Scaffold(
48
-        appBar: AppBar(
49
-          title: const Text('Plugin example app'),
50
-        ),
51
-        body: Column(
52
-          children: <Widget>[
53
-            Container(
54
-              alignment: Alignment.center,
55
-              height: 48,
56
-              child: Text('Running on: $_platformVersion'),
57
-            ),
58
-            Container(
59
-              height: 100.0,
60
-              child: MofunVideoView(
61
-                onMofunVideoViewCreated: _onMofunVideoViewCreated,
62
-              ),
63
-            ),
64
-          ],
65
-        ),
66
-      ),
50
+          appBar: AppBar(
51
+            title: const Text('Plugin example app'),
52
+          ),
53
+          body: Builder(builder: (context) {
54
+            TextEditingController textEditingController =
55
+                TextEditingController(text: defaultUrl);
56
+            MofunVideoViewController controller;
57
+            String url;
58
+
59
+            return Column(
60
+              children: <Widget>[
61
+                Container(
62
+                  height: 400.0,
63
+                  child: MofunVideoView(
64
+                    onMofunVideoViewCreated: (c) {
65
+                      controller = c;
66
+                      c.toast('Mofun video!');
67
+                    },
68
+                  ),
69
+                ),
70
+                TextField(
71
+                    controller: textEditingController,
72
+                    onChanged: (s) => url = s),
73
+                Row(
74
+                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
75
+                  children: <Widget>[
76
+                    RaisedButton(
77
+                      onPressed: () {
78
+                        controller?.play(url ?? defaultUrl);
79
+                      },
80
+                      child: Text('Play'),
81
+                    ),
82
+                    RaisedButton(
83
+                      onPressed: () {
84
+                        controller?.stop();
85
+                      },
86
+                      child: Text('Stop'),
87
+                    ),
88
+                    RaisedButton(
89
+                      onPressed: () {
90
+                        controller?.toast('暂不支持');
91
+                      },
92
+                      child: Text('Save'),
93
+                    ),
94
+                  ],
95
+                ),
96
+                Row(
97
+                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
98
+                  children: <Widget>[
99
+                    RaisedButton(
100
+                      onPressed: () {
101
+                        controller?.record();
102
+                      },
103
+                      child: Text('Record'),
104
+                    ),
105
+                    RaisedButton(
106
+                      onPressed: () {
107
+                        controller?.playerAndResume();
108
+                      },
109
+                      child: Text('PlayerAndResume'),
110
+                    ),
111
+                  ],
112
+                ),
113
+              ],
114
+            );
115
+          })),
67 116
     );
68 117
   }
69 118
 }
70
-
71
-void _onMofunVideoViewCreated(MofunVideoViewController controller) {
72
-  controller.setText('Hello from Android!');
73
-}

+ 7
- 0
example/pubspec.lock Voir le fichier

@@ -81,6 +81,13 @@ packages:
81 81
       url: "https://pub.flutter-io.cn"
82 82
     source: hosted
83 83
     version: "1.5.0"
84
+  permission_handler:
85
+    dependency: transitive
86
+    description:
87
+      name: permission_handler
88
+      url: "https://pub.flutter-io.cn"
89
+    source: hosted
90
+    version: "3.0.1"
84 91
   quiver:
85 92
     dependency: transitive
86 93
     description:

+ 77
- 11
lib/mofun_video_view.dart Voir le fichier

@@ -3,6 +3,7 @@ import 'dart:async';
3 3
 import 'package:flutter/foundation.dart';
4 4
 import 'package:flutter/services.dart';
5 5
 import 'package:flutter/widgets.dart';
6
+import 'package:permission_handler/permission_handler.dart';
6 7
 
7 8
 typedef void MofunVideoViewCreatedCallback(MofunVideoViewController controller);
8 9
 
@@ -20,10 +21,11 @@ class MofunVideoView extends StatefulWidget {
20 21
   State<StatefulWidget> createState() {
21 22
     return _MofunVideoViewState();
22 23
   }
23
-
24 24
 }
25 25
 
26 26
 class _MofunVideoViewState extends State<MofunVideoView> {
27
+  MofunVideoViewController controller;
28
+
27 29
   @override
28 30
   Widget build(BuildContext context) {
29 31
     if (defaultTargetPlatform == TargetPlatform.android) {
@@ -31,7 +33,7 @@ class _MofunVideoViewState extends State<MofunVideoView> {
31 33
         viewType: _CHANNEL,
32 34
         onPlatformViewCreated: _onPlatformViewCreated,
33 35
       );
34
-    }else if (defaultTargetPlatform == TargetPlatform.iOS) {
36
+    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
35 37
       return UiKitView(
36 38
         viewType: _CHANNEL,
37 39
         onPlatformViewCreated: _onPlatformViewCreated,
@@ -42,21 +44,85 @@ class _MofunVideoViewState extends State<MofunVideoView> {
42 44
   }
43 45
 
44 46
   void _onPlatformViewCreated(int id) {
45
-    if (widget.onMofunVideoViewCreated == null) {
46
-      return;
47
+    if (controller == null || controller.id != id) {
48
+      controller = MofunVideoViewController._(id);
49
+    }
50
+
51
+    if (widget.onMofunVideoViewCreated != null) {
52
+      widget.onMofunVideoViewCreated(controller);
47 53
     }
48
-    widget.onMofunVideoViewCreated(new MofunVideoViewController._(id));
49 54
   }
50 55
 }
51 56
 
52 57
 class MofunVideoViewController {
53
-  MofunVideoViewController._(int id)
58
+  final int id;
59
+  final MethodChannel _channel;
60
+
61
+  bool _hasPermission = false;
62
+
63
+  MofunVideoViewController._(this.id)
54 64
       : _channel = new MethodChannel('${_CHANNEL}_$id');
55 65
 
56
-  final MethodChannel _channel;
66
+  ///检查权限。返回是否权限充足
67
+  Future<bool> _checkPermission() async {
68
+    List<PermissionGroup> needCheck = List();
69
+
70
+    PermissionHandler permissionHandler = PermissionHandler();
71
+    PermissionStatus permission =
72
+        await permissionHandler.checkPermissionStatus(PermissionGroup.storage);
73
+    if (permission != PermissionStatus.granted) {
74
+      needCheck.add(PermissionGroup.storage);
75
+    }
76
+    permission = await permissionHandler
77
+        .checkPermissionStatus(PermissionGroup.microphone);
78
+    if (permission != PermissionStatus.granted) {
79
+      needCheck.add(PermissionGroup.microphone);
80
+    }
81
+
82
+    if (needCheck.isEmpty) {
83
+      return true;
84
+    }
85
+    Map<PermissionGroup, PermissionStatus> noPermissions =
86
+        await permissionHandler.requestPermissions(needCheck);
87
+    noPermissions.removeWhere((key, value) => value == PermissionStatus.granted);
88
+
89
+    return noPermissions.isEmpty;
90
+  }
57 91
 
58
-  Future<void> setText(String text) async {
59
-    assert(text != null);
60
-    return _channel.invokeMethod('setText', text);
92
+  Future<bool> checkPermission() async{
93
+    if(!_hasPermission){
94
+      return _checkPermission();
95
+    }
96
+    return true;
97
+  }
98
+
99
+  Future<void> toast(String msg) async {
100
+    return _channel.invokeMethod('toast', msg);
101
+  }
102
+
103
+  Future<void> play(String url) async {
104
+    await checkPermission();
105
+    return _channel.invokeMethod('play', url);
106
+  }
107
+
108
+  Future<void> stop() async {
109
+    await checkPermission();
110
+    return _channel.invokeMethod('stop');
61 111
   }
62
-}
112
+
113
+  Future<void> save(String output) async {
114
+    await checkPermission();
115
+    return _channel.invokeMethod('save', output);
116
+  }
117
+
118
+  Future<void> record() async {
119
+    await checkPermission();
120
+    return _channel.invokeMethod('record');
121
+  }
122
+
123
+  Future<void> playerAndResume() async {
124
+    await checkPermission();
125
+    return _channel.invokeMethod('playerAndResume');
126
+  }
127
+
128
+}

+ 7
- 0
pubspec.lock Voir le fichier

@@ -67,6 +67,13 @@ packages:
67 67
       url: "https://pub.flutter-io.cn"
68 68
     source: hosted
69 69
     version: "1.5.0"
70
+  permission_handler:
71
+    dependency: "direct main"
72
+    description:
73
+      name: permission_handler
74
+      url: "https://pub.flutter-io.cn"
75
+    source: hosted
76
+    version: "3.0.1"
70 77
   quiver:
71 78
     dependency: transitive
72 79
     description:

+ 1
- 0
pubspec.yaml Voir le fichier

@@ -10,6 +10,7 @@ environment:
10 10
 dependencies:
11 11
   flutter:
12 12
     sdk: flutter
13
+  permission_handler: '^3.0.0'
13 14
 
14 15
 dev_dependencies:
15 16
   flutter_test: