From bd5851a5a3b1601f578579bb0decc90712240876 Mon Sep 17 00:00:00 2001 From: Gangphon Date: Thu, 9 May 2024 20:30:22 +0800 Subject: [PATCH] player --- XixunPlayer/app/build.gradle | 9 +- XixunPlayer/app/src/main/AndroidManifest.xml | 2 +- .../com/xixun/xixunplayer/MainActivity.java | 464 +++++++----------- .../main/java/com/xixun/xixunplayer/Prog.java | 112 ++++- .../java/com/xixun/xixunplayer/Server.java | 14 +- .../com/xixun/xixunplayer/SocketThread.java | 235 +++++++++ .../java/com/xixun/xixunplayer/SrcScroll.java | 19 +- .../java/com/xixun/xixunplayer/SrcSensor.java | 6 +- .../java/com/xixun/xixunplayer/SrcVideo.java | 11 +- .../java/com/xixun/xixunplayer/SrcVideo2.java | 1 - .../xixun/xixunplayer/SrcVideoSurface.java | 138 ++++++ .../main/java/com/xixun/xixunplayer/Util.java | 3 + .../app/src/main/res/values/themes.xml | 4 - 13 files changed, 681 insertions(+), 337 deletions(-) create mode 100644 XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SocketThread.java create mode 100644 XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java delete mode 100644 XixunPlayer/app/src/main/res/values/themes.xml diff --git a/XixunPlayer/app/build.gradle b/XixunPlayer/app/build.gradle index 1b5f3cb..6dddf53 100644 --- a/XixunPlayer/app/build.gradle +++ b/XixunPlayer/app/build.gradle @@ -11,8 +11,7 @@ android { minSdk 21 targetSdk 34 versionCode 1 - versionName "2.0.4" - + versionName "2.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -45,7 +44,7 @@ dependencies { implementation files('libs/ijkplayer-java-0.8.8.aar') implementation files('libs/ijkplayer-armv7a-0.8.8.aar') implementation files('libs/ijkplayer-arm64-0.8.8.aar') - implementation files('libs\\connService2.jar') + implementation files('libs/connService2.jar') } def getAppName() { @@ -55,8 +54,8 @@ def getAppName() { } // 修改 Apk 名 -android.applicationVariants.all { variant -> - variant.outputs.all { +android.applicationVariants.configureEach { variant -> + variant.outputs.configureEach { def fileName = "${getAppName()}-${versionName}.apk" outputFileName = fileName } diff --git a/XixunPlayer/app/src/main/AndroidManifest.xml b/XixunPlayer/app/src/main/AndroidManifest.xml index 6a47772..f9075c3 100644 --- a/XixunPlayer/app/src/main/AndroidManifest.xml +++ b/XixunPlayer/app/src/main/AndroidManifest.xml @@ -18,7 +18,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.XixunPlayer" > + android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" > diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java index 5fb55a5..696ef82 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -1,9 +1,7 @@ package com.xixun.xixunplayer; -import static android.view.View.VISIBLE; - import android.annotation.SuppressLint; -import android.app.ActivityManager; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -11,24 +9,18 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.graphics.BitmapFactory; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; -import android.os.StatFs; import android.os.StrictMode; import android.view.Choreographer; import android.view.View; -import android.webkit.WebView; -import androidx.activity.ComponentActivity; import androidx.annotation.NonNull; -import androidx.annotation.OptIn; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; -import androidx.media3.common.util.UnstableApi; import com.xixun.joey.aidlset.CardService; import com.xixun.xy.conn.aidl.ConnService; @@ -41,31 +33,21 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; import java.net.ServerSocket; -import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.HashSet; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; import gnph.util.Chsets; import gnph.util.IOs; import gnph.util.JSList; import gnph.util.JSMap; -import gnph.util.NumFmts; -import gnph.util.O; -public class MainActivity extends ComponentActivity implements Choreographer.FrameCallback, Runnable { +public class MainActivity extends Activity implements Choreographer.FrameCallback, Runnable { public static MainActivity ins; public Intent environIntent = new Intent(); HashSet environs = new HashSet<>(); BackView backView; - Prog progView; + Prog progView, insView; long launchMilli = System.currentTimeMillis(); long syncMs; int state; @@ -129,11 +111,10 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra Util.screenWidth = service.getScreenWidth(); Util.screenHeight = service.getScreenHeight(); Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight); - backView = new BackView(MainActivity.this, Util.screenWidth, Util.screenHeight); + setContentView(backView = new BackView(MainActivity.this, Util.screenWidth, Util.screenHeight)); state = 5; if(Util.isScreenOn) initProg(); else state = 8; - if(progView==null) setContentView(backView); } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); @@ -179,12 +160,16 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra Util.println(" IsScreenOn: "+Util.isScreenOn); if(! Util.isScreenOn) { state = 8; + if(insView!=null) { + insView.release(); + insView = null; + } if(progView!=null) { progView.release(); progView = null; - setContentView(backView); } - } else if(progView==null) initProg(); + setContentView(backView); + } else if(progView==null && insView==null) initProg(); } }, new IntentFilter("com.xixun.action.PAUSE_PLAYER")); @@ -226,8 +211,8 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra try { var code = intent.getIntExtra("code", 0); Util.println(" remote_control "+code); - if(progView == null || code > progView.pages.size()) return; - if(! progView.avas.isEmpty()) page(progView.curAva).hide(); + if(progView == null || ! progView.isShown() || code > progView.pages.size()) return; + if(! progView.avas.isEmpty()) progView.curAva().hide(); var millis = (System.currentTimeMillis()+999)/1000*1000; if(code > 0) { progView.avas.clear(); @@ -235,7 +220,7 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra progView.curAva = 0; progView.curTimes = 1; progView.waitTo = 0; //点播 - var page = page(0); + var page = progView.curAva(); page.setMillis(millis); if(state != 6) { setContentView(progView); @@ -316,9 +301,14 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra } public void stopProg() { - if(progView==null) return; - progView.release(); - progView = null; + if(insView!=null) { + insView.release(); + insView = null; + } + if(progView!=null) { + progView.release(); + progView = null; + } setContentView(backView); } public boolean delProgFile() { @@ -338,27 +328,40 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra return ok; } public void initProg() { + state = 1; + try { + Util.println("\nParse Insert Prog Json"); + var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/insert"))); + var task = root.jsmap("task"); + if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); + if(task!=null) { + var view = new Prog(task, this); + if(view.getChildCount()!=0) setContentView(insView = view); + } + } catch(FileNotFoundException ignored) { + } catch(Throwable e) { + state = 7; + Util.printStackTrace(e); + } try { Util.println("\nParse Prog Json"); var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); var task = root.jsmap("task"); - if(task==null) { - if(! root.containsKey("layers")) { - state = 3; - Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n"); - return; - } - task = new JSMap("items", new JSList<>(new JSMap("repeatTimes", 1, "_program", root))); + if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); + if(task==null) Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n"); + else { + var view = new Prog(task, this); + if(view.getChildCount()==0) Util.println(" Error: ChildCount==0\n"); + else setContentView(progView = view); } - var view = new Prog(task, this); - if(view.getChildCount()==0) { - state = 3; - Util.println(" Error: ChildCount==0\n"); - return; - } - if(progView!=null) progView.release(); - progView = view; - setContentView(progView); + } catch(FileNotFoundException e) { + Util.println(""+e); + } catch(Throwable e) { + state = 7; + Util.makeText(this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + if(insView!=null || progView!=null) { state = 5; Util.println("Init Sync"); syncProg((System.currentTimeMillis()+999)/1000*1000); @@ -366,14 +369,7 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra choreographer.postFrameCallback(this); canAdd = false; } - } catch (FileNotFoundException e) { - state = 3; - Util.println(""+e); - } catch (Throwable e) { - state = 7; - Util.makeText(this, Util.toStr(e)).show(); - Util.printStackTrace(e); - } + } else if(state != 7) state = 3; } public void initProg(byte[] json) { try { @@ -387,19 +383,25 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra Util.println(new String(json, Chsets.UTF8)); return; } - task = new JSMap("items", new JSList<>(new JSMap("repeatTimes", 1, "_program", root))); + task = new JSMap("items", new JSList<>(new JSMap("_program", root))); } var view = new Prog(task, this); if(view.getChildCount()==0) { - state = 7; + if(! view.isInsert) state = 7; Util.println(" Error: ChildCount==0\n"); Util.println(new String(json, Chsets.UTF8)); return; } - if(progView!=null) progView.release(); - progView = view; - setContentView(progView); - var fOut = new FileOutputStream(Util.programDir + "/program"); + if(view.isInsert) { + if(insView!=null) insView.release(); + insView = view; + setContentView(insView); + } else { + if(progView!=null) progView.release(); + progView = view; + setContentView(progView); + } + var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program")); fOut.write(json); fOut.flush(); fOut.getFD().sync(); @@ -424,11 +426,32 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra @Override public void doFrame(long frameTimeNanos) { - if(progView == null) { + if(progView == null && insView==null) { canAdd = true; return; } var milli = System.currentTimeMillis(); + if(insView!=null && ! insView.avas.isEmpty()) { + var lastPage = insView.curAva(); + if(milli >= lastPage.endMilli) { + lastPage.hide(); + if(--lastPage.repeatTimes<=0 && ++insView.curAva >= insView.avas.size()) { + var isDiff = milli-lastPage.endMilli>=1000; + Util.println("isDiff: "+isDiff+" endMs: "+lastPage.endMilli+" millis:"+milli); + syncProg(isDiff ? milli : lastPage.endMilli); + choreographer.postFrameCallback(this); + canAdd = false; + if(insView!=null) for(var call : insView.calls) call.doFrame(milli); + return; + } + insView.curAva().setMillis(lastPage.endMilli); + Util.println("curAva: "+insView.curAva+" endMs: "+insView.curAva().endMilli); + } else lastPage.showHideSrcs(milli); + choreographer.postFrameCallback(this); + canAdd = false; + for(var call : insView.calls) call.doFrame(milli); + return; + } if(progView.avas.isEmpty()) { if(milli >= progView.waitTo) { progView.waitTo = Long.MAX_VALUE; @@ -436,21 +459,20 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra syncProg(milli); } } else { - var lastPage = page(progView.curAva); + var lastPage = progView.curAva(); if(milli >= lastPage.endMilli) { lastPage.hide(); if(progView.waitTo > 0) { //waitTo==0 为点播,不换页 if(progView.curTimes < lastPage.repeatTimes) progView.curTimes++; else { progView.curTimes = 1; - progView.curAva++; - if(progView.curAva >= progView.avas.size()) { + if(++progView.curAva >= progView.avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; if(Util.buf.length()>1000000) Util.buf.replace(0, 100000, ""); Util.println("isDiff: "+isDiff+" endMs: "+lastPage.endMilli+" millis:"+milli); syncProg(isDiff ? milli : lastPage.endMilli); - if(progView.avas.isEmpty()) Util.println("after. No Avas"); - else Util.println("after. curAva: "+progView.curAva+" endMs: "+page(progView.curAva).endMilli); + if(! progView.isShown() || progView.avas.isEmpty()) Util.println("after. No Avas"); + else Util.println("after. curAva: "+progView.curAva+" endMs: "+progView.curAva().endMilli); choreographer.postFrameCallback(this); canAdd = false; for(var call : progView.calls) call.doFrame(milli); @@ -458,27 +480,9 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra } } } - page(progView.curAva).setMillis(lastPage.endMilli); - Util.println("curAva: "+progView.curAva+" endMs: "+page(progView.curAva).endMilli); - } else { - for(var layer : lastPage.layers) { - for(var src : layer.srcs) { - if(src.view.getVisibility()==VISIBLE) { - if(milli >= src.endMilli) src.hide(); - else src.doEff(); - } else if(milli < src.endMilli && milli >= src.startMilli) src.show(); - } - if(milli >= layer.endMilli) { - layer.endMilli += layer.dur; - for(var src : layer.srcs) { - src.endMilli += layer.dur; - src.startMilli += layer.dur; - if(src.startTime > 0) src.hide(); - else src.show(); - } - } - } - } + progView.curAva().setMillis(lastPage.endMilli); + Util.println("curAva: "+progView.curAva+" endMs: "+progView.curAva().endMilli); + } else lastPage.showHideSrcs(milli); } choreographer.postFrameCallback(this); canAdd = false; @@ -486,238 +490,98 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra } void syncProg(long milli) { - progView.curTimes = 1; - progView.avas.clear(); - var dur = 0; - for(int i=0; i hases = null; - ByteArrayOutputStream progJson = null; - while(true) { - var obj = JSMap.from(in); - var _type = obj.stnn("_type"); - Util.println("_type: "+_type); - if("consult".equals(_type)) { - JSList ids = obj.jslist("idList"); - hases = new HashSet<>(); - if(ids!=null) for(int i=0; i { - backView.cosImg = BitmapFactory.decodeFile(Util.backImgFile); - backView.invalidate(); - }); - } else if("imgFileEnd".equals(_type)) { - new JSMap("success", true).write(out); - } else if("proEnd".equals(_type)) { - new JSMap("success", progJson!=null).write(out); - if(progJson!=null) { - var json = progJson.toByteArray(); - progJson = null; - runOnUiThread(() -> initProg(json)); - } - } else if("DelPrograms".equals(_type)) { - var latch = new CountDownLatch(1); - var ok = new AtomicBoolean(false); - runOnUiThread(() -> { - ok.set(delProgFile()); - latch.countDown(); - }); - try { - latch.await(); - } catch (InterruptedException ignored) {} - new JSMap("success", ok.get()).write(out); - } else if("DelBackImg".equals(_type)) { - MainActivity.ins.runOnUiThread(() -> { - MainActivity.ins.backView.cosImg = null; - MainActivity.ins.backView.invalidate(); - }); - new JSMap("success", new File(Util.backImgFile).delete()).write(out); - } else if("getPlayerState".equals(_type)) { - var descs = Util.getState(state); - new JSMap( - "code", state, - "des_en", descs[0], - "des", descs[1] - ).write(out); - } else if("GetInfo".equals(_type)) { - var writer = new OutputStreamWriter(out); - var Fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"); - var dur = 0; - if(progView!=null) for(var page : progView.pages) dur += page.tDur; - writer.append("ProgSend: ").append(Fmt.format(new File(Util.programDir + "/program").lastModified())).append(" ProgDur: ").append(String.valueOf(dur)); - if(progView!=null) writer.append(" Pages: ").append(String.valueOf(progView.avas.size())).append(" / ").append(String.valueOf(progView.pages.size())); - writer.append("\n"); - writer.append(" Launch: ").append(Fmt.format(launchMilli)).append("\n"); - writer.append(" Sync: ").append(Fmt.format(syncMs)).append("\n"); - if(progView==null) writer.append(" ProgView is Null\n"); - else if(progView.avas.isEmpty()) writer.append(" No Avas\n"); - else writer.append("Page End: ").append(Fmt.format(page(progView.curAva).endMilli)).append(" CurPage: ").append(String.valueOf(progView.curAva)).append(" / ").append(String.valueOf(progView.avas.get(progView.curAva))).append("\n"); - writer.append(" Current: ").append(Fmt.format(System.currentTimeMillis())).append("\n"); - var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); - writer.append(" Disk: ").append(String.valueOf(statFs.getTotalBytes()/1000000)).append(" ").append(String.valueOf(statFs.getAvailableBytes()/1000000)).append(" ").append(String.valueOf(statFs.getFreeBytes()/1000000)).append(" (total avail free)\n"); - var actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); - var memoryInfo = new ActivityManager.MemoryInfo(); - actManager.getMemoryInfo(memoryInfo); - writer.append(" Memory: ").append(String.valueOf(memoryInfo.totalMem/1000000)).append(" ").append(String.valueOf(memoryInfo.availMem/1000000)).append(" ").append(String.valueOf(memoryInfo.threshold/1000000)).append(" ").append(String.valueOf(memoryInfo.lowMemory)).append(" (total avail threshold low)\n"); - var runtime = Runtime.getRuntime(); - writer.append("Runtime: ").append(String.valueOf(runtime.maxMemory()/1000000)).append(" ").append(String.valueOf(runtime.totalMemory()/1000000)).append(" ").append(NumFmts.cfix2().format(runtime.freeMemory()*0.000001)).append(" (max total free)\n"); - writer.append("/proc/stat\n"); - IOs.writeCloseIn(writer, new FileReader("/proc/stat")); - writer.append("\n"); - var latch = new CountDownLatch(1); - runOnUiThread(() -> { - if(progView!=null && ! progView.avas.isEmpty()) { - var page = page(progView.curAva); - for(var layer : page.layers) for(var src : layer.srcs) if(src.view.getVisibility()==VISIBLE) { - try { - if(src.view instanceof SrcVideo) { - var view = (SrcVideo) src.view; - if(view.ijkPlayer!=null) { - writer.append("VideoPlaying: ").append(String.valueOf(view.ijkPlayer.isPlaying())).append("\tCur/Dur: ").append(String.valueOf(view.ijkPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.ijkPlayer.getDuration())).append("\n"); - var mediaInfo = view.ijkPlayer.getMediaInfo(); - var vStream = mediaInfo.mMeta.mVideoStream; - writer.append(" BitRate: ").append(String.valueOf(view.ijkPlayer.getBitRate()/1000)).append("k\tFPS: ").append(String.valueOf(vStream.mFpsNum/(float)vStream.mFpsDen)).append(" (").append(String.valueOf(vStream.mFpsNum)).append("/").append(String.valueOf(vStream.mFpsDen)).append(")\n"); - var tracks = view.ijkPlayer.getTrackInfo(); - for(var track : tracks) writer.append(" ").append(track.getInfoInline()).append("\n"); - writer.append(" Format: ").append(mediaInfo.mMeta.mFormat).append("\n"); - writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append(" (").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append(")\n"); - writer.append("AudioDecoder: ").append(mediaInfo.mAudioDecoder).append(" ").append(mediaInfo.mAudioDecoderImpl).append("\n"); - //writer.append("PROFILE: ").append(mediaInfo.mMeta.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE)).append("\n"); - } else if(view.exoPlayer!=null) { - writer.append("VideoPlaying: ").append(String.valueOf(view.exoPlayer.isPlaying())).append(" ").append(String.valueOf(view.exoPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.exoPlayer.getDuration())).append("\n"); - writer.append(" BitRate: ").append(String.valueOf(view.bitRate/1000)).append("k\n"); - writer.append("PlaybackStat: ").append(view.getState()).append("\n"); - writer.append(" PlayerError: ").append(String.valueOf(view.exoPlayer.getPlayerError())).append("\n"); - writer.append(" VideoFormat: ").append(String.valueOf(view.exoPlayer.getVideoFormat())).append("\n"); - var cnt = view.exoPlayer.getRendererCount(); - for(int rr=0; rr (int) (f2.lastModified() - f1.lastModified())); - var writer = new OutputStreamWriter(out); - for(var file : files) writer.append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n'); - writer.append('\n'); - writer.flush(); - } else if("GetFile".equals(_type)) { - var name = obj.str("name"); - if(name!=null) IOs.writeCloseIn(out, new FileInputStream(Util.programDir+"/"+name)); - else new JSMap("code", 1, "msg", "name is null").write(out); - } - out.flush(); - Util.println("cmd end"); - } } catch (Throwable e) { - var emsg = e.getMessage(); - if(emsg!=null && ("Socket closed".equals(emsg) || emsg.endsWith("end-of-input"))) { - Util.println(emsg); - continue; - } MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.this, Util.toStr(e)).show()); Util.printStackTrace(e); - } finally { - O.close(in, out); - Util.println("conn end\n"); } } } catch (Throwable e) { diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java index 1b94895..0deb6da 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java @@ -2,11 +2,13 @@ package com.xixun.xixunplayer; import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Color; import android.net.Uri; import android.speech.tts.TextToSpeech; import android.view.Choreographer; import android.view.View; +import android.webkit.JavascriptInterface; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ImageView; @@ -31,10 +33,12 @@ public class Prog extends AbsLayout { ArrayList calls = new ArrayList<>(); int curAva, curTimes = 1; long waitTo = Long.MAX_VALUE; + boolean isInsert; @SuppressLint("SetJavaScriptEnabled") public Prog(JSMap task, Context context) { super(context); + isInsert = task.bool("insert"); JSList jpages = task.jslist("items"); JSList partLengths = task.jslist("partLengths"); var isVertical = task.bool("isVertical"); @@ -49,8 +53,9 @@ public class Prog extends AbsLayout { } var width = partObj.intg("width"); var height = partObj.intg("height"); - AbsLayout box = this; - if(partLengths!=null && partLengths.size() > 1) { + AbsLayout box; + if(partLengths==null || partLengths.size() <= 1) box = this; + else { box = new AbsLayout(context); addView(box, new AbsLayout.LayoutParams(0, 0, width, height)); var mask = new View(context); @@ -76,11 +81,15 @@ public class Prog extends AbsLayout { JSList layers = _program.jslist("layers"); var isSimple = _program.intg("version")==2; if(layers==null || layers.isEmpty()) continue; + if(isSimple) { + width = _program.intg("width", Util.screenWidth); + height = _program.intg("height", Util.screenHeight); + } var page = new Page(); page.name = _program.str("name"); page.repeatTimes = pageMap.intg("repeatTimes", 1); page.parse(pageMap.jslist("schedules")); - HashMap videoMap = new HashMap<>(); + HashMap videoMap = new HashMap<>(); for(int ll=layers.size()-1; ll>=0; ll--) { var layer = new Layer(); layer.isLoop = layers.get(ll).bool("repeat"); @@ -97,10 +106,22 @@ public class Prog extends AbsLayout { src.type = source.stnn("_type"); if(src.type.isEmpty()) continue; var timeSpan = source.intg("timeSpan")*1000; - if(timeSpan==0) continue; + if(timeSpan==0) { + Util.println("\nError: timeSpan is 0. _type: "+src.type); + continue; + } var geo = isSimple ? new AbsLayout.LayoutParams(0, 0, width, height) : new AbsLayout.LayoutParams(source.intg("left")+bdWidth, source.intg("top")+bdWidth, source.intg("width")-bdWidth-bdWidth, source.intg("height")-bdWidth-bdWidth); var notAudio = ! src.type.equals("Audio"); - if((geo.width<=0 || geo.height<=0 || (geo.y>=height && height>0) || (geo.x>=width && width>0)) && notAudio) continue; + if(notAudio) { + if(geo.width<=0 || geo.height<=0) { + Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height); + continue; + } + if(box != this && ((geo.y>=height && height>0) || (geo.x>=width && width>0))) { + Util.println("\nError: y>=height or x>=width. _type: "+src.type+" width: "+width+" height: "+height+" x: "+geo.x+" y: "+geo.y); + continue; + } + } src.startTime = isSimple ? (layer.srcs.isEmpty() ? 0 : layer.srcs.get(layer.srcs.size()-1).endTime) : source.intg("playTime")*1000; if(bdStart > src.startTime) bdStart = src.startTime; src.endTime = src.startTime + timeSpan; @@ -183,18 +204,18 @@ public class Prog extends AbsLayout { if(url==null) continue; } else if(id==null) continue; var key = isLive ? url : id + src.startTime + src.endTime; - var videoView = videoMap.get(key); - if(videoView!=null) { - var geoOld = (AbsLayout.LayoutParams) videoView.getLayoutParams(); + var exist = videoMap.get(key); + if(exist!=null) { + var geoOld = (AbsLayout.LayoutParams) exist.getLayoutParams(); if(geo.width*geo.height > geoOld.width*geoOld.height) { - videoView.setLayoutParams(geo); + exist.setLayoutParams(geo); geo = geoOld; } - src.view = new SrcCopy(context, videoView); + src.view = new SrcCopy(context, exist); ((SrcCopy) src.view).scaleX = 0; } else { src.view = new SrcVideo(context, isLive ? url : Util.programDir+"/"+id, source.intg("vol", 100) / 100.0f, isLive); - videoMap.put(key, (SrcVideo) src.view); + videoMap.put(key, src.view); } } else if(src.type.equals("Audio")) { if(id==null) continue; @@ -207,7 +228,7 @@ public class Prog extends AbsLayout { var speechRate = (float) source.dbl("voiceRate"); if(mode!=null ? mode.endsWith("roll") : (imgs.size()==1 && imgs.get(0).intg("picDuration")==0)) { var img = imgs.get(0); - src.view = new SrcScroll(this, img); + src.view = new SrcScroll(this, img, null); if(hasTTS) { src.text = img.str("text"); if(src.text!=null) { @@ -262,13 +283,17 @@ public class Prog extends AbsLayout { webView.setBackgroundColor(Color.TRANSPARENT); webView.setVerticalScrollBarEnabled(false); webView.setHorizontalScrollBarEnabled(false); - webView.setInitialScale(100); + webView.setInitialScale(source.intg("zoom", 100)); webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return false; } + @Override + public void onPageFinished(WebView view, String url) { + view.loadUrl("javascript:window.scrollTo("+source.str("offX", "0")+", "+source.str("offY", "0")+")"); + } }); webView.loadUrl(source.stnn("url")); src.view = webView; @@ -279,6 +304,41 @@ public class Prog extends AbsLayout { else if(src.type.startsWith("Weather")) src.view = new SrcWeather(context, source); else if(src.type.startsWith("VistorSource")) src.view = new SrcVisitor(this, source); else if(src.type.startsWith("MultiLineText")) src.view = new SrcSensor(this, source, geo.width, geo.height); + else if(src.type.startsWith("SingleLineText")) { + var webView = new WebView(context); + webView.setBackgroundColor(Color.TRANSPARENT); + webView.setVerticalScrollBarEnabled(false); + webView.setHorizontalScrollBarEnabled(false); + webView.setInitialScale(100); + webView.getSettings().setJavaScriptEnabled(true); + webView.setLayoutParams(new AbsLayout.LayoutParams(0, -geo.height, geo.width, geo.height)); + var html = source.stnn("html"); + var prefix = ""; + var suffix = ""; + source.put("effect", "to left"); + source.put("effectSpeed", source.intg("speed")); + var view = new SrcScroll(this, source, Bitmap.createBitmap(geo.width, geo.height, Bitmap.Config.ARGB_8888)); + src.view = view; + webView.addJavascriptInterface(new Object() { + @JavascriptInterface + public void setWidth(int width) { + MainActivity.ins.runOnUiThread(() -> { + var hhh = view.imgs.get(0).getHeight(); + var www = width + hhh; + webView.getLayoutParams().width = www; + view.imgs.set(0, Bitmap.createBitmap(www, hhh, Bitmap.Config.ARGB_8888)); + box.addView(webView); + view.webView = webView; + }); + } + }, "java"); + webView.loadDataWithBaseURL(null, prefix+html+suffix, "text/html", "UTF-8", null); + } else continue; if(src.view==null) continue; src.view.setVisibility(GONE); @@ -322,6 +382,9 @@ public class Prog extends AbsLayout { } } + Page curAva() { + return pages.get(avas.get(curAva)); + } void release() { try { setVisibility(GONE); @@ -528,7 +591,7 @@ public class Prog extends AbsLayout { for(var layer : layers) for(var src : layer.srcs) src.hide(); } - public void setMillis(long milli) { + void setMillis(long milli) { endMilli = milli + sDur; for(var layer : layers) { if(layer.isLoop) layer.endMilli = milli + layer.dur; @@ -539,7 +602,26 @@ public class Prog extends AbsLayout { } } } - public boolean isScheOn(long milli) { + void showHideSrcs(long milli) { + for(var layer : layers) { + for(var src : layer.srcs) { + if(src.view.getVisibility()==VISIBLE) { + if(milli >= src.endMilli) src.hide(); + else src.doEff(); + } else if(milli < src.endMilli && milli >= src.startMilli) src.show(); + } + if(milli >= layer.endMilli) { + layer.endMilli += layer.dur; + for(var src : layer.srcs) { + src.endMilli += layer.dur; + src.startMilli += layer.dur; + if(src.startTime > 0) src.hide(); + else src.show(); + } + } + } + } + boolean isScheOn(long milli) { if(sches==null) return false; var local = milli + Dates.zoneOff; var time = local % 86400000L; diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java index 4abb9a9..0a22f64 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java @@ -34,7 +34,7 @@ public class Server extends Service { @Override public String getProgramName() throws RemoteException { try { - var name = MainActivity.ins!=null && MainActivity.ins.progView!=null && ! MainActivity.ins.progView.avas.isEmpty() ? MainActivity.ins.page(MainActivity.ins.progView.curAva).name : null; + var name = MainActivity.ins!=null && MainActivity.ins.progView!=null && ! MainActivity.ins.progView.avas.isEmpty() ? MainActivity.ins.progView.curAva().name : null; Util.println("Server getProgramName. <-"+name); return name; } catch (Exception e) { @@ -257,10 +257,18 @@ public class Server extends Service { "commandId", commandId, "img", img ).toString(); + } else if(_type.equalsIgnoreCase("getProgramTask")) { + var task = JSMap.fromClose(new FileInputStream(Util.programDir+"/program")).jsmap("task"); + return new JSMap( + "_type", "ProgramTaskCallback", + "cardId", Util.getCardId(), + "commandId", commandId, + "task", task + ).toString(); } return new JSMap( "_type", "Error", - "errorMessage", "Unknow Type", + "errorMessage", "Unknown Type: "+_type, "cardId", Util.getCardId(), "commandId", commandId ).toString(); @@ -268,7 +276,7 @@ public class Server extends Service { Util.printStackTrace(e); return new JSMap( "_type", "Error", - "errorMessage", e.toString(), + "errorMessage", Util.toStr(e), "cardId", Util.getCardId(), "commandId", commandId ).toString(); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SocketThread.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SocketThread.java new file mode 100644 index 0000000..1fe7043 --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SocketThread.java @@ -0,0 +1,235 @@ +package com.xixun.xixunplayer; + +import static android.view.View.VISIBLE; + +import android.app.ActivityManager; +import android.graphics.BitmapFactory; +import android.os.Environment; +import android.os.StatFs; +import android.webkit.WebView; + +import androidx.annotation.OptIn; +import androidx.media3.common.util.UnstableApi; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.HashSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import gnph.util.IOs; +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.NumFmts; +import gnph.util.O; + +public class SocketThread extends Thread { + MainActivity main; + Socket socket; + InputStream in; + OutputStream out; + public SocketThread(MainActivity main, Socket socket) { + this.main = main; + this.socket = socket; + } + @OptIn(markerClass = UnstableApi.class) @Override + public void run() { + try { + socket.setSoTimeout(600000); + Util.socketThreads.add(this); + in = socket.getInputStream(); + out = socket.getOutputStream(); + HashSet hases = null; + ByteArrayOutputStream progJson = null; + while(true) { + var obj = JSMap.from(in); + var _type = obj.stnn("_type"); + Util.println("_type: "+_type); + if("consult".equals(_type)) { + JSList ids = obj.jslist("idList"); + hases = new HashSet<>(); + if(ids!=null) for(int i=0; i { + main.backView.cosImg = BitmapFactory.decodeFile(Util.backImgFile); + main.backView.invalidate(); + }); + } else if("imgFileEnd".equals(_type)) { + new JSMap("success", true).write(out); + } else if("proEnd".equals(_type)) { + new JSMap("success", progJson!=null).write(out); + if(progJson!=null) { + var json = progJson.toByteArray(); + progJson = null; + main.runOnUiThread(() -> main.initProg(json)); + } + } else if("DelPrograms".equals(_type)) { + var latch = new CountDownLatch(1); + var ok = new AtomicBoolean(false); + main.runOnUiThread(() -> { + ok.set(main.delProgFile()); + latch.countDown(); + }); + try { + latch.await(); + } catch (InterruptedException ignored) {} + new JSMap("success", ok.get()).write(out); + } else if("DelBackImg".equals(_type)) { + MainActivity.ins.runOnUiThread(() -> { + MainActivity.ins.backView.cosImg = null; + MainActivity.ins.backView.invalidate(); + }); + new JSMap("success", new File(Util.backImgFile).delete()).write(out); + } else if("getPlayerState".equals(_type)) { + var descs = Util.getState(main.state); + new JSMap( + "code", main.state, + "des_en", descs[0], + "des", descs[1] + ).write(out); + } else if("GetInfo".equals(_type)) { + var writer = new OutputStreamWriter(out); + var Fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"); + var dur = 0; + if(main.progView!=null) for(var page : main.progView.pages) dur += page.tDur; + writer.append("ProgSend: ").append(Fmt.format(new File(Util.programDir + "/program").lastModified())).append(" ProgDur: ").append(String.valueOf(dur)); + if(main.progView!=null) writer.append(" Pages: ").append(String.valueOf(main.progView.avas.size())).append(" / ").append(String.valueOf(main.progView.pages.size())); + writer.append("\n"); + writer.append(" Launch: ").append(Fmt.format(main.launchMilli)).append("\n"); + writer.append(" Sync: ").append(Fmt.format(main.syncMs)).append("\n"); + if(main.progView==null) writer.append(" ProgView is Null\n"); + else if(main.progView.avas.isEmpty()) writer.append(" No Avas\n"); + else writer.append("Page End: ").append(Fmt.format(main.progView.curAva().endMilli)).append(" CurPage: ").append(String.valueOf(main.progView.curAva)).append(" / ").append(String.valueOf(main.progView.avas.get(main.progView.curAva))).append("\n"); + writer.append(" Current: ").append(Fmt.format(System.currentTimeMillis())).append("\n"); + var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); + writer.append(" Disk: ").append(String.valueOf(statFs.getTotalBytes()/1000000)).append(" ").append(String.valueOf(statFs.getAvailableBytes()/1000000)).append(" ").append(String.valueOf(statFs.getFreeBytes()/1000000)).append(" (total avail free)\n"); + var actManager = (ActivityManager) main.getSystemService(main.ACTIVITY_SERVICE); + var memoryInfo = new ActivityManager.MemoryInfo(); + actManager.getMemoryInfo(memoryInfo); + writer.append(" Memory: ").append(String.valueOf(memoryInfo.totalMem/1000000)).append(" ").append(String.valueOf(memoryInfo.availMem/1000000)).append(" ").append(String.valueOf(memoryInfo.threshold/1000000)).append(" ").append(String.valueOf(memoryInfo.lowMemory)).append(" (total avail threshold low)\n"); + var runtime = Runtime.getRuntime(); + writer.append("Runtime: ").append(String.valueOf(runtime.maxMemory()/1000000)).append(" ").append(String.valueOf(runtime.totalMemory()/1000000)).append(" ").append(NumFmts.cfix2().format(runtime.freeMemory()*0.000001)).append(" (max total free)\n"); +// writer.append("/proc/stat\n"); +// IOs.writeCloseIn(writer, new FileReader("/proc/stat")); + writer.append("\nSockets ").append(String.valueOf(Util.socketThreads.size())).append("\n"); + for(var socket : Util.socketThreads) writer.append(" ").append(String.valueOf(socket.socket.getInetAddress())).append(":").append(String.valueOf(socket.socket.getPort())).append(" Buf:").append(String.valueOf(socket.socket.getReceiveBufferSize()/1000)).append("k SoLinger:").append(String.valueOf(socket.socket.getSoLinger())).append("\n"); + + writer.append("\n"); + var latch = new CountDownLatch(1); + main.runOnUiThread(() -> { + if(main.progView!=null && ! main.progView.avas.isEmpty()) { + var page = main.progView.curAva(); + for(var layer : page.layers) for(var src : layer.srcs) if(src.view.getVisibility()==VISIBLE) { + try { + if(src.view instanceof SrcVideo) { + var view = (SrcVideo) src.view; + if(view.ijkPlayer!=null) { + writer.append("VideoPlaying: ").append(String.valueOf(view.ijkPlayer.isPlaying())).append("\tCur/Dur: ").append(String.valueOf(view.ijkPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.ijkPlayer.getDuration())).append("\n"); + var mediaInfo = view.ijkPlayer.getMediaInfo(); + var vStream = mediaInfo.mMeta.mVideoStream; + writer.append(" BitRate: ").append(String.valueOf(view.ijkPlayer.getBitRate()/1000)).append("k\tFPS: ").append(String.valueOf(vStream.mFpsNum/(float)vStream.mFpsDen)).append(" (").append(String.valueOf(vStream.mFpsNum)).append("/").append(String.valueOf(vStream.mFpsDen)).append(")\n"); + var tracks = view.ijkPlayer.getTrackInfo(); + for(var track : tracks) writer.append(" ").append(track.getInfoInline()).append("\n"); + writer.append(" Format: ").append(mediaInfo.mMeta.mFormat).append("\n"); + writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append(" (").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append(")\n"); + writer.append("AudioDecoder: ").append(mediaInfo.mAudioDecoder).append(" ").append(mediaInfo.mAudioDecoderImpl).append("\n"); + //writer.append("PROFILE: ").append(mediaInfo.mMeta.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE)).append("\n"); + } else if(view.exoPlayer!=null) { + writer.append("VideoPlaying: ").append(String.valueOf(view.exoPlayer.isPlaying())).append(" ").append(String.valueOf(view.exoPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.exoPlayer.getDuration())).append("\n"); + writer.append(" BitRate: ").append(String.valueOf(view.bitRate/1000)).append("k\n"); + writer.append("PlaybackStat: ").append(view.getState()).append("\n"); + writer.append(" PlayerError: ").append(String.valueOf(view.exoPlayer.getPlayerError())).append("\n"); + writer.append(" VideoFormat: ").append(String.valueOf(view.exoPlayer.getVideoFormat())).append("\n"); + var cnt = view.exoPlayer.getRendererCount(); + for(int rr=0; rr (int) (f2.lastModified() - f1.lastModified())); + var writer = new OutputStreamWriter(out); + for(var file : files) writer.append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n'); + writer.append('\n'); + writer.flush(); + } else if("GetFile".equals(_type)) { + var name = obj.str("name"); + if(name!=null) IOs.writeCloseIn(out, new FileInputStream(Util.programDir+"/"+name)); + else new JSMap("code", 1, "msg", "name is null").write(out); + } + out.flush(); + Util.println("cmd end"); + } + } catch (Throwable e) { + var emsg = e.getMessage(); + if(emsg!=null && ("Socket closed".equals(emsg) || emsg.endsWith("end-of-input"))) { + Util.println(emsg); + return; + } + main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show()); + Util.printStackTrace(e); + } finally { + Util.socketThreads.remove(this); + O.close(in, out, socket); + Util.println("conn end\n"); + } + } +} diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcScroll.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcScroll.java index e1252b9..648ecdb 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcScroll.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcScroll.java @@ -7,6 +7,7 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.view.Choreographer; import android.view.View; +import android.webkit.WebView; import androidx.annotation.NonNull; @@ -19,14 +20,14 @@ public class SrcScroll extends View implements Choreographer.FrameCallback { ArrayList imgs = new ArrayList<>(); Rect rect = new Rect(); - int imgsWidth; int interval, cur, end, step; + WebView webView; char effect; - public SrcScroll(Prog prog, JSMap json) { + public SrcScroll(Prog prog, JSMap json, Bitmap img) { super(prog.getContext()); - var img = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id")); - imgsWidth = img.getWidth(); + if(img==null) img = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id")); + var imgsWidth = img.getWidth(); if(imgsWidth <= 8192) imgs.add(img); else { var rem = img.getWidth(); @@ -57,11 +58,21 @@ public class SrcScroll extends View implements Choreographer.FrameCallback { } if(effect!=0) prog.calls.add(this); } + int aaaaaa; @Override protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); if(imgs.isEmpty()) return; try { + if(webView!=null && aaaaaa++>30) { + var canva = new Canvas(imgs.get(0)); + webView.draw(canva); + webView.setVisibility(GONE); + webView = null; + freshCnt = cur = 0; + if(effect=='l') end = -(imgs.get(0).getWidth()-step); + else if(effect=='r') end = imgs.get(0).getWidth()-step; + } drawOther(canvas); } catch (Throwable e) { setVisibility(GONE); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java index 38cba51..5adff90 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java @@ -127,8 +127,10 @@ public class SrcSensor extends WebView implements IntentReceiver, Choreographer. @Override public void doFrame(long ms) { if(! isShown()) { - act.environs.remove(this); - isFirst = true; + if(! isFirst) { + act.environs.remove(this); + isFirst = true; + } return; } boolean needRefresh = false; diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java index 1188e17..457fca5 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java @@ -1,5 +1,6 @@ package com.xixun.xixunplayer; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.SurfaceTexture; import android.view.Surface; @@ -15,6 +16,7 @@ import androidx.media3.exoplayer.SeekParameters; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; +@SuppressLint("ViewConstructor") public class SrcVideo extends TextureView implements TextureView.SurfaceTextureListener { String path; @@ -44,7 +46,7 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL } void initIjk() { ijkPlayer = new IjkMediaPlayer(); - //ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1); ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); @@ -54,6 +56,7 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL ijkPlayer.setLooping(true); ijkPlayer.setVolume(vol, vol); ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ + ijkPlayer.setOnPreparedListener(null); bitRate = ijkPlayer.getBitRate(); if(bitRate > 12000000) { setSurfaceTextureListener(null); @@ -62,6 +65,10 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL } if(isShown()) start(); }); + ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{ + Util.println(" Video Error: "+var1+" "+var2+" "+var3); + return true; + }); ijkPlayer.prepareAsync(); } catch (Throwable e) { Util.makeText(getContext(), Util.toStr(e)).show(); @@ -71,7 +78,7 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL } @Override public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { - setSurfaceTextureListener(null); + Util.println(" onSurfaceTextureAvailable "+(ijkPlayer==null?"ijkPlayer==null":"")); if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface)); } @Override diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java index 6b0a181..5ff8678 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java @@ -1,6 +1,5 @@ package com.xixun.xixunplayer; -import android.content.Context; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.view.Choreographer; diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java new file mode 100644 index 0000000..e95a8d1 --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java @@ -0,0 +1,138 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import androidx.annotation.NonNull; +import androidx.annotation.OptIn; +import androidx.media3.common.MediaItem; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.exoplayer.SeekParameters; + +import tv.danmaku.ijk.media.player.IMediaPlayer; +import tv.danmaku.ijk.media.player.IjkMediaPlayer; + +@SuppressLint("ViewConstructor") +public class SrcVideoSurface extends SurfaceView implements SurfaceHolder.Callback { + + String path; + float vol; + IjkMediaPlayer ijkPlayer; + ExoPlayer exoPlayer; + long bitRate; + boolean isLive; + + public SrcVideoSurface(Context context, String path, float vol, boolean isLive) { + super(context); + this.path = path; + this.vol = vol; + this.isLive = isLive; + initIjk(); + } + + @OptIn(markerClass = UnstableApi.class) + void initExo() { + exoPlayer = new ExoPlayer.Builder(getContext()).build(); + exoPlayer.setMediaItem(MediaItem.fromUri(path)); + exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); + exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC); + exoPlayer.setVolume(vol); + exoPlayer.setVideoSurfaceView(this); + exoPlayer.prepare(); + } + void initIjk() { + ijkPlayer = new IjkMediaPlayer(); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); + try { + getHolder().addCallback(this); + ijkPlayer.setDataSource(path); + ijkPlayer.setLooping(true); + ijkPlayer.setVolume(vol, vol); + ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ + ijkPlayer.setOnPreparedListener(null); + bitRate = ijkPlayer.getBitRate(); + if(bitRate > 12000000) { + getHolder().removeCallback(this); + release(); + initExo(); + } + if(isShown()) start(); + }); + ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{ + Util.println(" Video Error: "+var1+" "+var2+" "+var3); + return true; + }); + ijkPlayer.prepareAsync(); + } catch (Throwable e) { + Util.makeText(getContext(), Util.toStr(e)).show(); + Util.printStackTrace(e); + ijkPlayer = null; + } + } + @Override + public void surfaceCreated(@NonNull SurfaceHolder holder) { + Util.println(" surfaceCreated "+(ijkPlayer==null?"ijkPlayer==null":"")); + if(ijkPlayer!=null) ijkPlayer.setDisplay(holder); + } + @Override + public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {} + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder holder) {} + + void start() { + if(ijkPlayer!=null) { + ijkPlayer.seekTo(0); + ijkPlayer.start(); + } else if(exoPlayer!=null) { + if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare(); + exoPlayer.play(); + } + } + void pause() { + if(ijkPlayer!=null) { + ijkPlayer.pause(); + ijkPlayer.seekTo(0); + } + if(exoPlayer!=null && exoPlayer.isPlaying()) { + exoPlayer.pause(); + exoPlayer.seekToDefaultPosition(); + } + } + void release() { + if(ijkPlayer!=null) { + ijkPlayer.release(); + ijkPlayer = null; + } + if(exoPlayer!=null) { + exoPlayer.release(); + exoPlayer = null; + } + } + + String getState() { + if(exoPlayer!=null) { + var state = exoPlayer.getPlaybackState(); + if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE"; + if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING"; + if(state==ExoPlayer.STATE_READY) return "STATE_READY"; + if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED"; + } + return "null"; + } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + if(isVisible) { + start(); + if(isLive) ijkPlayer.setVolume(vol, vol); + } else if(isLive) ijkPlayer.setVolume(0, 0); + else pause(); + } +} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java index f62a598..c6a57e6 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Random; import java.util.Set; +import java.util.Vector; import java.util.concurrent.CountDownLatch; import gnph.util.IOs; @@ -24,6 +25,7 @@ public class Util { public static int screenWidth, screenHeight; public static boolean isScreenOn; + public static final Vector socketThreads = new Vector<>(); public static final HashMap stateDescs = new HashMap<>(); static { stateDescs.put(1, new String[]{"Initialize", "初始化"}); @@ -43,6 +45,7 @@ public class Util { public static void println(String msg) { System.out.println(msg); + if(buf.length()>1000000) buf.replace(0, 100000, ""); buf.append(msg).append("\n"); } public static void printStackTrace(Throwable e) { diff --git a/XixunPlayer/app/src/main/res/values/themes.xml b/XixunPlayer/app/src/main/res/values/themes.xml deleted file mode 100644 index 3a1eec4..0000000 --- a/XixunPlayer/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file