Browse Source

整合Video 到 flutter

ykrank 5 years ago
parent
commit
0017271c28

+ 2
- 2
android/build.gradle View File

2
 version '1.0-SNAPSHOT'
2
 version '1.0-SNAPSHOT'
3
 
3
 
4
 buildscript {
4
 buildscript {
5
-    ext.kotlin_version = '1.3.21'
5
+    ext.kotlin_version = '1.3.31'
6
     repositories {
6
     repositories {
7
         google()
7
         google()
8
         jcenter()
8
         jcenter()
25
 apply plugin: 'kotlin-android'
25
 apply plugin: 'kotlin-android'
26
 
26
 
27
 android {
27
 android {
28
-    compileSdkVersion 27
28
+    compileSdkVersion 28
29
 
29
 
30
     sourceSets {
30
     sourceSets {
31
         main.java.srcDirs += 'src/main/kotlin'
31
         main.java.srcDirs += 'src/main/kotlin'

+ 6
- 6
android/src/main/java/com/fgodt/mixplayer/Player.java View File

12
 public class Player extends Render {
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
     private Core mixCore;
22
     private Core mixCore;
23
     private Core enMixCore;
23
     private Core enMixCore;

+ 57
- 56
android/src/main/java/com/fgodt/mixplayer/Render.java View File

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

+ 0
- 252
android/src/main/java/com/fogdt/mixsample/MainActivity.java View File

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
-}

+ 87
- 20
android/src/main/kotlin/com/shuangyubang/mofun_flutter_plugin_video/MofunRealVideoView.kt View File

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

+ 40
- 9
android/src/main/kotlin/com/shuangyubang/mofun_flutter_plugin_video/MofunVideoView.kt View File

4
 import android.graphics.Color
4
 import android.graphics.Color
5
 import android.view.View
5
 import android.view.View
6
 import android.widget.TextView
6
 import android.widget.TextView
7
+import android.widget.Toast
7
 import io.flutter.plugin.common.BinaryMessenger
8
 import io.flutter.plugin.common.BinaryMessenger
8
 import io.flutter.plugin.common.MethodCall
9
 import io.flutter.plugin.common.MethodCall
9
 import io.flutter.plugin.common.MethodChannel
10
 import io.flutter.plugin.common.MethodChannel
17
 class MofunVideoView
18
 class MofunVideoView
18
     : PlatformView, MethodChannel.MethodCallHandler {
19
     : PlatformView, MethodChannel.MethodCallHandler {
19
 
20
 
20
-    private val textView: TextView
21
+    private val videoView: MofunRealVideoView
21
 
22
 
22
     constructor(context: Context, messenger: BinaryMessenger, id: Int, creationParams: Any?) {
23
     constructor(context: Context, messenger: BinaryMessenger, id: Int, creationParams: Any?) {
23
-        textView = TextView(context)
24
-        textView.setBackgroundColor(Color.BLUE)
24
+        videoView = MofunRealVideoView(context)
25
         MethodChannel(messenger, Channel_Mofun_Video_Info + "_$id").setMethodCallHandler(this)
25
         MethodChannel(messenger, Channel_Mofun_Video_Info + "_$id").setMethodCallHandler(this)
26
     }
26
     }
27
 
27
 
28
     override fun getView(): View {
28
     override fun getView(): View {
29
-        return textView
29
+        return videoView.getView()
30
     }
30
     }
31
 
31
 
32
     override fun dispose() {
32
     override fun dispose() {
33
-
33
+        videoView.dispose()
34
     }
34
     }
35
 
35
 
36
     override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
36
     override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
37
         when (methodCall.method) {
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
             else -> result.notImplemented()
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
         result.success(null)
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
 class MofunVideoViewFactory(val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
82
 class MofunVideoViewFactory(val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {

+ 2
- 2
example/android/app/build.gradle View File

26
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27
 
27
 
28
 android {
28
 android {
29
-    compileSdkVersion 27
29
+    compileSdkVersion 28
30
 
30
 
31
     sourceSets {
31
     sourceSets {
32
         main.java.srcDirs += 'src/main/kotlin'
32
         main.java.srcDirs += 'src/main/kotlin'
40
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
40
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41
         applicationId "com.shuangyubang.mofun_flutter_plugin_video_example"
41
         applicationId "com.shuangyubang.mofun_flutter_plugin_video_example"
42
         minSdkVersion 16
42
         minSdkVersion 16
43
-        targetSdkVersion 27
43
+        targetSdkVersion 28
44
         versionCode flutterVersionCode.toInteger()
44
         versionCode flutterVersionCode.toInteger()
45
         versionName flutterVersionName
45
         versionName flutterVersionName
46
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
46
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

+ 1
- 1
example/android/build.gradle View File

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

+ 68
- 23
example/lib/main.dart View File

43
 
43
 
44
   @override
44
   @override
45
   Widget build(BuildContext context) {
45
   Widget build(BuildContext context) {
46
+    const String defaultUrl = 'rtmp://live.hkstv.hk.lxdns.com/live/hks1';
47
+
46
     return MaterialApp(
48
     return MaterialApp(
47
       home: Scaffold(
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 View File

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

+ 1
- 0
example/pubspec.yaml View File

12
   # The following adds the Cupertino Icons font to your application.
12
   # The following adds the Cupertino Icons font to your application.
13
   # Use with the CupertinoIcons class for iOS style icons.
13
   # Use with the CupertinoIcons class for iOS style icons.
14
   cupertino_icons: ^0.1.2
14
   cupertino_icons: ^0.1.2
15
+  permission_handler: ^3.0.0
15
 
16
 
16
 dev_dependencies:
17
 dev_dependencies:
17
   flutter_test:
18
   flutter_test:

+ 77
- 11
lib/mofun_video_view.dart View File

3
 import 'package:flutter/foundation.dart';
3
 import 'package:flutter/foundation.dart';
4
 import 'package:flutter/services.dart';
4
 import 'package:flutter/services.dart';
5
 import 'package:flutter/widgets.dart';
5
 import 'package:flutter/widgets.dart';
6
+import 'package:permission_handler/permission_handler.dart';
6
 
7
 
7
 typedef void MofunVideoViewCreatedCallback(MofunVideoViewController controller);
8
 typedef void MofunVideoViewCreatedCallback(MofunVideoViewController controller);
8
 
9
 
20
   State<StatefulWidget> createState() {
21
   State<StatefulWidget> createState() {
21
     return _MofunVideoViewState();
22
     return _MofunVideoViewState();
22
   }
23
   }
23
-
24
 }
24
 }
25
 
25
 
26
 class _MofunVideoViewState extends State<MofunVideoView> {
26
 class _MofunVideoViewState extends State<MofunVideoView> {
27
+  MofunVideoViewController controller;
28
+
27
   @override
29
   @override
28
   Widget build(BuildContext context) {
30
   Widget build(BuildContext context) {
29
     if (defaultTargetPlatform == TargetPlatform.android) {
31
     if (defaultTargetPlatform == TargetPlatform.android) {
31
         viewType: _CHANNEL,
33
         viewType: _CHANNEL,
32
         onPlatformViewCreated: _onPlatformViewCreated,
34
         onPlatformViewCreated: _onPlatformViewCreated,
33
       );
35
       );
34
-    }else if (defaultTargetPlatform == TargetPlatform.iOS) {
36
+    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
35
       return UiKitView(
37
       return UiKitView(
36
         viewType: _CHANNEL,
38
         viewType: _CHANNEL,
37
         onPlatformViewCreated: _onPlatformViewCreated,
39
         onPlatformViewCreated: _onPlatformViewCreated,
42
   }
44
   }
43
 
45
 
44
   void _onPlatformViewCreated(int id) {
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
 class MofunVideoViewController {
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
       : _channel = new MethodChannel('${_CHANNEL}_$id');
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 View File

67
       url: "https://pub.flutter-io.cn"
67
       url: "https://pub.flutter-io.cn"
68
     source: hosted
68
     source: hosted
69
     version: "1.5.0"
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
   quiver:
77
   quiver:
71
     dependency: transitive
78
     dependency: transitive
72
     description:
79
     description:

+ 1
- 0
pubspec.yaml View File

10
 dependencies:
10
 dependencies:
11
   flutter:
11
   flutter:
12
     sdk: flutter
12
     sdk: flutter
13
+  permission_handler: '^3.0.0'
13
 
14
 
14
 dev_dependencies:
15
 dev_dependencies:
15
   flutter_test:
16
   flutter_test: