@ -1 +0,0 @@ |
|||||||
/build |
|
@ -1,52 +0,0 @@ |
|||||||
apply plugin: 'com.android.application' |
|
||||||
|
|
||||||
android { |
|
||||||
compileSdkVersion rootProject.ext.compileSdkVersion |
|
||||||
buildToolsVersion rootProject.ext.buildToolsVersion |
|
||||||
|
|
||||||
lintOptions { |
|
||||||
abortOnError false |
|
||||||
} |
|
||||||
defaultConfig { |
|
||||||
applicationId "com.frank.living" |
|
||||||
minSdkVersion rootProject.ext.minSdkVersion |
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion |
|
||||||
versionCode rootProject.ext.versionCode |
|
||||||
versionName rootProject.ext.versionName |
|
||||||
flavorDimensions "800400" |
|
||||||
|
|
||||||
ndk { |
|
||||||
abiFilters 'armeabi-v7a', 'arm64-v8a' |
|
||||||
} |
|
||||||
} |
|
||||||
buildTypes { |
|
||||||
release { |
|
||||||
minifyEnabled false |
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
|
||||||
} |
|
||||||
} |
|
||||||
productFlavors { |
|
||||||
all32 { minSdkVersion rootProject.ext.minSdkVersion } |
|
||||||
all64 { minSdkVersion rootProject.ext.minSdkVersion } |
|
||||||
} |
|
||||||
|
|
||||||
sourceSets { |
|
||||||
main { |
|
||||||
jniLibs.srcDirs = ['libs'] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
dependencies { |
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs') |
|
||||||
implementation "androidx.appcompat:appcompat:$rootProject.appcompatVersion" |
|
||||||
implementation "androidx.preference:preference:$rootProject.preferenceVersion" |
|
||||||
implementation "androidx.annotation:annotation:$rootProject.annotationVersion" |
|
||||||
implementation "androidx.recyclerview:recyclerview:$rootProject.recyclerviewVersion" |
|
||||||
|
|
||||||
implementation "com.squareup:otto:$rootProject.ottoVersion" |
|
||||||
|
|
||||||
implementation "tv.danmaku.ijk.media:ijkplayer-java:$rootProject.ijkVersion" |
|
||||||
implementation "tv.danmaku.ijk.media:ijkplayer-exo:$rootProject.ijkVersion" |
|
||||||
|
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
# Add project specific ProGuard rules here. |
|
||||||
# By default, the flags in this file are appended to flags specified |
|
||||||
# in /opt/android/ADK/tools/proguard/proguard-android.txt |
|
||||||
# You can edit the include path and order by changing the proguardFiles |
|
||||||
# directive in build.gradle. |
|
||||||
# |
|
||||||
# For more details, see |
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html |
|
||||||
|
|
||||||
# Add any project specific keep options here: |
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following |
|
||||||
# and specify the fully qualified class name to the JavaScript interface |
|
||||||
# class: |
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
|
||||||
# public *; |
|
||||||
#} |
|
@ -1,43 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
package="com.frank.living"> |
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" /> |
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" /> |
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
|
||||||
|
|
||||||
<application |
|
||||||
android:allowBackup="true" |
|
||||||
android:icon="@mipmap/ic_launcher" |
|
||||||
android:label="@string/app_name" |
|
||||||
android:theme="@style/AppTheme"> |
|
||||||
|
|
||||||
<activity |
|
||||||
android:name="com.frank.living.activity.RtspLiveActivity" |
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize" |
|
||||||
android:label="@string/app_name" |
|
||||||
android:screenOrientation="landscape" |
|
||||||
android:theme="@style/FullscreenTheme"> |
|
||||||
<intent-filter> |
|
||||||
<action android:name="android.intent.action.MAIN" /> |
|
||||||
<action android:name="android.intent.action.VIEW" /> |
|
||||||
<category android:name="android.intent.category.LAUNCHER" /> |
|
||||||
</intent-filter> |
|
||||||
</activity> |
|
||||||
|
|
||||||
<activity |
|
||||||
android:name="com.frank.living.activity.MultiScreenActivity" |
|
||||||
android:configChanges="orientation|keyboardHidden|screenSize" |
|
||||||
android:label="@string/app_name" |
|
||||||
android:screenOrientation="landscape" |
|
||||||
android:theme="@style/FullscreenTheme"> |
|
||||||
<!--<intent-filter>--> |
|
||||||
<!--<action android:name="android.intent.action.MAIN" />--> |
|
||||||
<!--<action android:name="android.intent.action.VIEW" />--> |
|
||||||
<!--<category android:name="android.intent.category.LAUNCHER" />--> |
|
||||||
<!--</intent-filter>--> |
|
||||||
</activity> |
|
||||||
|
|
||||||
</application> |
|
||||||
|
|
||||||
</manifest> |
|
@ -1,469 +0,0 @@ |
|||||||
package com.frank.living.activity; |
|
||||||
|
|
||||||
import android.content.BroadcastReceiver; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.Intent; |
|
||||||
import android.content.IntentFilter; |
|
||||||
import android.os.PowerManager; |
|
||||||
import android.os.Process; |
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity; |
|
||||||
|
|
||||||
import android.os.Bundle; |
|
||||||
import android.text.TextUtils; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
import com.frank.living.R; |
|
||||||
import com.frank.living.constant.Constants; |
|
||||||
import com.frank.living.listener.OnDoubleClickListener; |
|
||||||
import com.frank.living.listener.IjkPlayerListener; |
|
||||||
import com.frank.living.widget.IjkVideoView; |
|
||||||
|
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.TreeMap; |
|
||||||
|
|
||||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
|
||||||
|
|
||||||
/** |
|
||||||
* multi screen living |
|
||||||
* Created by xufulong on 2019/01/04 |
|
||||||
*/ |
|
||||||
public class MultiScreenActivity extends AppCompatActivity { |
|
||||||
|
|
||||||
private static final String TAG = MultiScreenActivity.class.getSimpleName(); |
|
||||||
|
|
||||||
private IjkVideoView mVideoView1; |
|
||||||
private IjkVideoView mVideoView2; |
|
||||||
private IjkVideoView mVideoView3; |
|
||||||
private IjkVideoView mVideoView4; |
|
||||||
|
|
||||||
private View divider1; |
|
||||||
private View divider2; |
|
||||||
|
|
||||||
private IMediaPlayer ijkPlayer; |
|
||||||
private CustomReceiver customReceiver; |
|
||||||
private String ipAddress; |
|
||||||
|
|
||||||
//is multi-screen mode or full-screen mode
|
|
||||||
private boolean isMultiScreen; |
|
||||||
//relationship of between client id and channel number
|
|
||||||
private HashMap<String, Integer> clientMap = new HashMap<>(); |
|
||||||
//record each channel state
|
|
||||||
private TreeMap<Integer, Boolean> channelMap = new TreeMap<>(); |
|
||||||
|
|
||||||
private String url = ""; |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onCreate(Bundle savedInstanceState) { |
|
||||||
super.onCreate(savedInstanceState); |
|
||||||
setContentView(R.layout.activity_multi_screen); |
|
||||||
|
|
||||||
parseIntent(); |
|
||||||
registerBroadcast(); |
|
||||||
|
|
||||||
initView(); |
|
||||||
initListener(); |
|
||||||
setupView(); |
|
||||||
wakeUp(); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private void initView() { |
|
||||||
mVideoView1 = findViewById(R.id.video_view1); |
|
||||||
mVideoView2 = findViewById(R.id.video_view2); |
|
||||||
mVideoView3 = findViewById(R.id.video_view3); |
|
||||||
mVideoView4 = findViewById(R.id.video_view4); |
|
||||||
|
|
||||||
divider1 = findViewById(R.id.divider1); |
|
||||||
divider2 = findViewById(R.id.divider2); |
|
||||||
} |
|
||||||
|
|
||||||
private void initListener() { |
|
||||||
mVideoView1.setOnTouchListener(new OnDoubleClickListener(new OnDoubleClickListener.OnDoubleClick() { |
|
||||||
@Override |
|
||||||
public void onDouble() { |
|
||||||
changeScreenMode(1); |
|
||||||
} |
|
||||||
})); |
|
||||||
mVideoView2.setOnTouchListener(new OnDoubleClickListener(new OnDoubleClickListener.OnDoubleClick() { |
|
||||||
@Override |
|
||||||
public void onDouble() { |
|
||||||
changeScreenMode(2); |
|
||||||
} |
|
||||||
})); |
|
||||||
mVideoView3.setOnTouchListener(new OnDoubleClickListener(new OnDoubleClickListener.OnDoubleClick() { |
|
||||||
@Override |
|
||||||
public void onDouble() { |
|
||||||
changeScreenMode(3); |
|
||||||
} |
|
||||||
})); |
|
||||||
mVideoView4.setOnTouchListener(new OnDoubleClickListener(new OnDoubleClickListener.OnDoubleClick() { |
|
||||||
@Override |
|
||||||
public void onDouble() { |
|
||||||
changeScreenMode(4); |
|
||||||
} |
|
||||||
})); |
|
||||||
} |
|
||||||
|
|
||||||
private void setupView() { |
|
||||||
//default full screen
|
|
||||||
enterFullScreen(1); |
|
||||||
mVideoView1.setVideoPath(url); |
|
||||||
mVideoView1.setIjkPlayerListener(new IjkPlayerListener() { |
|
||||||
@Override |
|
||||||
public void onIjkPlayer(IjkMediaPlayer ijkMediaPlayer) { |
|
||||||
//setting ijkPlayer option
|
|
||||||
setOptions(ijkMediaPlayer); |
|
||||||
} |
|
||||||
}); |
|
||||||
mVideoView1.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() { |
|
||||||
@Override |
|
||||||
public void onPrepared(IMediaPlayer iMediaPlayer) { |
|
||||||
|
|
||||||
ijkPlayer = iMediaPlayer; |
|
||||||
} |
|
||||||
}); |
|
||||||
mVideoView1.start(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* parse params |
|
||||||
*/ |
|
||||||
private void parseIntent() { |
|
||||||
for (int i = 1; i <= 4; i++) { |
|
||||||
channelMap.put(i, false); |
|
||||||
} |
|
||||||
clientMap.put(ipAddress, 1); |
|
||||||
channelMap.put(1, true); |
|
||||||
|
|
||||||
Intent intent = getIntent(); |
|
||||||
String url = intent.getStringExtra("url"); |
|
||||||
if (!TextUtils.isEmpty(url)) { |
|
||||||
this.url = url; |
|
||||||
} |
|
||||||
String ip = intent.getStringExtra("ip"); |
|
||||||
if (!TextUtils.isEmpty(ip)) { |
|
||||||
ipAddress = ip; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//wake up the screen
|
|
||||||
private void wakeUp() { |
|
||||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); |
|
||||||
if (powerManager == null) |
|
||||||
return; |
|
||||||
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
|
||||||
| PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG); |
|
||||||
wakeLock.acquire(1000); |
|
||||||
wakeLock.release(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* config the options of ijkPlayer |
|
||||||
*/ |
|
||||||
private void setOptions(IjkMediaPlayer ijkPlayer) { |
|
||||||
if (ijkPlayer == null) |
|
||||||
return; |
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", 1);//no extra optimize
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 200);//size of probe data
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1); |
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);//enable cache or not
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1); |
|
||||||
//0:disable 1:enable
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);//enable hardware decode or not
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);//auto rotate
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0); |
|
||||||
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 0);//max buffer size
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 2);//minimum frame size
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 30);//maximum cached duration
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1); |
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer"); |
|
||||||
//max analyzed duration
|
|
||||||
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100); |
|
||||||
//ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");//using tcp or udp
|
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* register broadcast |
|
||||||
*/ |
|
||||||
private void registerBroadcast() { |
|
||||||
customReceiver = new CustomReceiver(); |
|
||||||
IntentFilter intentFilter = new IntentFilter(); |
|
||||||
intentFilter.addAction(Constants.ACTION_CLIENT_REMOVE); |
|
||||||
intentFilter.addAction(Constants.ACTION_CLIENT_ADD); |
|
||||||
registerReceiver(customReceiver, intentFilter); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* unregister broadcast |
|
||||||
*/ |
|
||||||
private void unregisterBroadcast() { |
|
||||||
if (customReceiver != null) { |
|
||||||
unregisterReceiver(customReceiver); |
|
||||||
customReceiver = null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* custom broadcast receiver |
|
||||||
*/ |
|
||||||
private class CustomReceiver extends BroadcastReceiver { |
|
||||||
@Override |
|
||||||
public void onReceive(Context context, Intent intent) { |
|
||||||
String action = intent.getAction(); |
|
||||||
if (action == null) |
|
||||||
return; |
|
||||||
Log.e(TAG, "onReceive=" + action); |
|
||||||
switch (action) { |
|
||||||
case Constants.ACTION_CLIENT_REMOVE://remove client
|
|
||||||
int num = intent.getIntExtra("clientNum", 0); |
|
||||||
if (num == 0) { |
|
||||||
Process.killProcess(Process.myPid()); |
|
||||||
} else if (num > 0) { |
|
||||||
String ipAddress = intent.getStringExtra("ipAddress"); |
|
||||||
int target = clientMap.get(ipAddress); |
|
||||||
removeClient(target); |
|
||||||
clientMap.remove(ipAddress); |
|
||||||
channelMap.put(target, false); |
|
||||||
if (num == 1) { |
|
||||||
int castingChannel = getCastingChannel(); |
|
||||||
enterFullScreen(castingChannel); |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
case Constants.ACTION_CLIENT_ADD://add client
|
|
||||||
int clientNum = intent.getIntExtra("clientNum", 0); |
|
||||||
String otherUrl = intent.getStringExtra("url"); |
|
||||||
String ipAddress = intent.getStringExtra("ip"); |
|
||||||
//select the idle channel
|
|
||||||
int channel = selectIdleChannel(clientNum); |
|
||||||
clientMap.put(ipAddress, channel); |
|
||||||
channelMap.put(channel, true); |
|
||||||
addClient(channel, otherUrl); |
|
||||||
//switch multi-screen mode
|
|
||||||
if (clientNum == 2) { |
|
||||||
exitFullScreen(); |
|
||||||
} |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* select the first idle channel |
|
||||||
* |
|
||||||
* @param clientNum clientNum |
|
||||||
* @return idleChannel |
|
||||||
*/ |
|
||||||
private int selectIdleChannel(int clientNum) { |
|
||||||
for (int channel = 1; channel < clientNum; channel++) { |
|
||||||
if (!channelMap.get(channel)) { |
|
||||||
return channel; |
|
||||||
} |
|
||||||
} |
|
||||||
return clientNum; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* get current casting channel |
|
||||||
* |
|
||||||
* @return idleChannel |
|
||||||
*/ |
|
||||||
private int getCastingChannel() { |
|
||||||
for (int channel = 1; channel <= 4; channel++) { |
|
||||||
if (channelMap.get(channel)) { |
|
||||||
return channel; |
|
||||||
} |
|
||||||
} |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* add client to casting |
|
||||||
* |
|
||||||
* @param target target |
|
||||||
* @param clientUrl clientUrl |
|
||||||
*/ |
|
||||||
private void addClient(int target, String clientUrl) { |
|
||||||
switch (target) { |
|
||||||
case 1: |
|
||||||
mVideoView1.setVisibility(View.VISIBLE); |
|
||||||
mVideoView1.setVideoPath(clientUrl); |
|
||||||
mVideoView1.start(); |
|
||||||
break; |
|
||||||
case 2: |
|
||||||
mVideoView2.setVisibility(View.VISIBLE); |
|
||||||
mVideoView2.setVideoPath(clientUrl); |
|
||||||
mVideoView2.start(); |
|
||||||
break; |
|
||||||
case 3: |
|
||||||
mVideoView3.setVisibility(View.VISIBLE); |
|
||||||
mVideoView3.setVideoPath(clientUrl); |
|
||||||
mVideoView3.start(); |
|
||||||
break; |
|
||||||
case 4: |
|
||||||
mVideoView4.setVisibility(View.VISIBLE); |
|
||||||
mVideoView4.setVideoPath(clientUrl); |
|
||||||
mVideoView4.start(); |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* remove client |
|
||||||
* |
|
||||||
* @param target target |
|
||||||
*/ |
|
||||||
private void removeClient(int target) { |
|
||||||
switch (target) { |
|
||||||
case 1: |
|
||||||
mVideoView1.stopPlayback(); |
|
||||||
mVideoView1.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
case 2: |
|
||||||
mVideoView2.stopPlayback(); |
|
||||||
mVideoView2.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
case 3: |
|
||||||
mVideoView3.stopPlayback(); |
|
||||||
mVideoView3.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
case 4: |
|
||||||
mVideoView4.stopPlayback(); |
|
||||||
mVideoView4.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* hide divider in multi-screen mode |
|
||||||
*/ |
|
||||||
private void hideDivider() { |
|
||||||
divider1.setVisibility(View.GONE); |
|
||||||
divider2.setVisibility(View.GONE); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* show divider in multi-screen mode |
|
||||||
*/ |
|
||||||
private void showDivider() { |
|
||||||
divider1.setVisibility(View.VISIBLE); |
|
||||||
divider2.setVisibility(View.VISIBLE); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* enter full-screen mode |
|
||||||
* |
|
||||||
* @param channel channel |
|
||||||
*/ |
|
||||||
private void enterFullScreen(int channel) { |
|
||||||
hideDivider(); |
|
||||||
switch (channel) { |
|
||||||
case 1: |
|
||||||
mVideoView1.setRenderViewVisible(); |
|
||||||
mVideoView2.setRenderViewGone(); |
|
||||||
mVideoView3.setRenderViewGone(); |
|
||||||
mVideoView4.setRenderViewGone(); |
|
||||||
|
|
||||||
mVideoView1.setVisibility(View.VISIBLE); |
|
||||||
mVideoView2.setVisibility(View.GONE); |
|
||||||
mVideoView3.setVisibility(View.GONE); |
|
||||||
mVideoView4.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
case 2: |
|
||||||
mVideoView1.setRenderViewGone(); |
|
||||||
mVideoView2.setRenderViewVisible(); |
|
||||||
mVideoView3.setRenderViewGone(); |
|
||||||
mVideoView4.setRenderViewGone(); |
|
||||||
|
|
||||||
mVideoView1.setVisibility(View.GONE); |
|
||||||
mVideoView2.setVisibility(View.VISIBLE); |
|
||||||
mVideoView3.setVisibility(View.GONE); |
|
||||||
mVideoView4.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
case 3: |
|
||||||
mVideoView1.setRenderViewGone(); |
|
||||||
mVideoView2.setRenderViewGone(); |
|
||||||
mVideoView3.setRenderViewVisible(); |
|
||||||
mVideoView4.setRenderViewGone(); |
|
||||||
|
|
||||||
mVideoView1.setVisibility(View.GONE); |
|
||||||
mVideoView2.setVisibility(View.GONE); |
|
||||||
mVideoView3.setVisibility(View.VISIBLE); |
|
||||||
mVideoView4.setVisibility(View.GONE); |
|
||||||
break; |
|
||||||
case 4: |
|
||||||
mVideoView1.setRenderViewGone(); |
|
||||||
mVideoView2.setRenderViewGone(); |
|
||||||
mVideoView3.setRenderViewGone(); |
|
||||||
mVideoView4.setRenderViewVisible(); |
|
||||||
|
|
||||||
mVideoView1.setVisibility(View.GONE); |
|
||||||
mVideoView2.setVisibility(View.GONE); |
|
||||||
mVideoView3.setVisibility(View.GONE); |
|
||||||
mVideoView4.setVisibility(View.VISIBLE); |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* exit full-screen mode |
|
||||||
*/ |
|
||||||
private void exitFullScreen() { |
|
||||||
mVideoView1.setRenderViewVisible(); |
|
||||||
mVideoView2.setRenderViewVisible(); |
|
||||||
mVideoView3.setRenderViewVisible(); |
|
||||||
mVideoView4.setRenderViewVisible(); |
|
||||||
|
|
||||||
showDivider(); |
|
||||||
mVideoView1.setVisibility(View.VISIBLE); |
|
||||||
mVideoView2.setVisibility(View.VISIBLE); |
|
||||||
mVideoView3.setVisibility(View.VISIBLE); |
|
||||||
mVideoView4.setVisibility(View.VISIBLE); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* switch screen mode |
|
||||||
* |
|
||||||
* @param channel channel |
|
||||||
*/ |
|
||||||
private void changeScreenMode(int channel) { |
|
||||||
isMultiScreen = !isMultiScreen; |
|
||||||
if (isMultiScreen) { |
|
||||||
enterFullScreen(channel); |
|
||||||
} else { |
|
||||||
exitFullScreen(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onDestroy() { |
|
||||||
super.onDestroy(); |
|
||||||
|
|
||||||
unregisterBroadcast(); |
|
||||||
|
|
||||||
mVideoView1.stopPlayback(); |
|
||||||
mVideoView1.release(true); |
|
||||||
mVideoView2.stopPlayback(); |
|
||||||
mVideoView2.release(true); |
|
||||||
mVideoView3.stopPlayback(); |
|
||||||
mVideoView3.release(true); |
|
||||||
mVideoView4.stopPlayback(); |
|
||||||
mVideoView4.release(true); |
|
||||||
|
|
||||||
IjkMediaPlayer.native_profileEnd(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
@ -1,154 +0,0 @@ |
|||||||
package com.frank.living.activity; |
|
||||||
|
|
||||||
import android.Manifest; |
|
||||||
import android.content.pm.PackageManager; |
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.os.Bundle; |
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity; |
|
||||||
|
|
||||||
import android.os.Environment; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.View; |
|
||||||
import android.widget.ImageButton; |
|
||||||
import android.widget.TableLayout; |
|
||||||
|
|
||||||
import com.frank.living.R; |
|
||||||
import com.frank.living.listener.IjkPlayerListener; |
|
||||||
|
|
||||||
import androidx.core.app.ActivityCompat; |
|
||||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
|
||||||
|
|
||||||
import com.frank.living.util.PhotoUtil; |
|
||||||
import com.frank.living.widget.IjkVideoView; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
public class RtspLiveActivity extends AppCompatActivity implements IjkPlayerListener, View.OnClickListener { |
|
||||||
|
|
||||||
private final static String TAG = RtspLiveActivity.class.getSimpleName(); |
|
||||||
|
|
||||||
private IjkMediaPlayer ijkMediaPlayer; |
|
||||||
private IjkVideoView mVideoView; |
|
||||||
private ImageButton btnPlay; |
|
||||||
private ImageButton btnSound; |
|
||||||
private boolean isPause; |
|
||||||
private boolean isSilence; |
|
||||||
private String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}; |
|
||||||
|
|
||||||
private final static String url = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"; |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onCreate(Bundle savedInstanceState) { |
|
||||||
super.onCreate(savedInstanceState); |
|
||||||
setContentView(R.layout.activity_live); |
|
||||||
|
|
||||||
requestPermission(); |
|
||||||
init(); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private void init() { |
|
||||||
IjkMediaPlayer.loadLibrariesOnce(null); |
|
||||||
IjkMediaPlayer.native_profileBegin("libijkplayer.so"); |
|
||||||
|
|
||||||
TableLayout mHudView = findViewById(R.id.hud_view); |
|
||||||
mVideoView = findViewById(R.id.video_view); |
|
||||||
mVideoView.setHudView(mHudView); |
|
||||||
mVideoView.setIjkPlayerListener(this); |
|
||||||
mVideoView.setVideoPath(url); |
|
||||||
mVideoView.start(); |
|
||||||
|
|
||||||
btnPlay = findViewById(R.id.btn_play); |
|
||||||
btnPlay.setOnClickListener(this); |
|
||||||
btnSound = findViewById(R.id.btn_sound); |
|
||||||
btnSound.setOnClickListener(this); |
|
||||||
ImageButton btnScreenShot = findViewById(R.id.btn_screenshot); |
|
||||||
btnScreenShot.setOnClickListener(this); |
|
||||||
} |
|
||||||
|
|
||||||
private void requestPermission() { |
|
||||||
if (ActivityCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) { |
|
||||||
ActivityCompat.requestPermissions(this, permissions, 1234); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void initOptions() { |
|
||||||
if (ijkMediaPlayer == null) |
|
||||||
return; |
|
||||||
Log.e(TAG, "initOptions"); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "fast", 1); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 200); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "flush_packets", 1); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1); |
|
||||||
//0:disable 1:enable
|
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", 0); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", 2); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 30); |
|
||||||
//input buffer:don't limit the input buffer size (useful with realtime streams)
|
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1); |
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "fflags", "nobuffer"); |
|
||||||
//ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");
|
|
||||||
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzedmaxduration", 100); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onIjkPlayer(IjkMediaPlayer ijkMediaPlayer) { |
|
||||||
this.ijkMediaPlayer = ijkMediaPlayer; |
|
||||||
initOptions(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onClick(View v) { |
|
||||||
switch (v.getId()) { |
|
||||||
case R.id.btn_play: |
|
||||||
isPause = !isPause; |
|
||||||
if (isPause) {//pause
|
|
||||||
mVideoView.pause(); |
|
||||||
btnPlay.setBackgroundResource(R.drawable.ic_play); |
|
||||||
} else {//resume
|
|
||||||
mVideoView.start(); |
|
||||||
btnPlay.setBackgroundResource(R.drawable.ic_pause); |
|
||||||
} |
|
||||||
break; |
|
||||||
case R.id.btn_sound: |
|
||||||
isSilence = !isSilence; |
|
||||||
if (ijkMediaPlayer == null) |
|
||||||
return; |
|
||||||
if (isSilence) { |
|
||||||
ijkMediaPlayer.setVolume(0, 0); |
|
||||||
btnSound.setBackgroundResource(R.drawable.ic_sound); |
|
||||||
} else { |
|
||||||
ijkMediaPlayer.setVolume(50, 50); |
|
||||||
btnSound.setBackgroundResource(R.drawable.ic_silence); |
|
||||||
} |
|
||||||
break; |
|
||||||
case R.id.btn_screenshot: |
|
||||||
if (mVideoView != null) { |
|
||||||
Bitmap currentFrame = mVideoView.getCurrentFrame(); |
|
||||||
if (currentFrame != null) { |
|
||||||
String photoName = "img_" + System.currentTimeMillis() + ".jpg"; |
|
||||||
String photoPath = Environment.getExternalStorageDirectory().getPath() + File.separator + photoName; |
|
||||||
PhotoUtil.savePhoto(currentFrame, photoPath, this); |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onStop() { |
|
||||||
super.onStop(); |
|
||||||
|
|
||||||
mVideoView.stopPlayback(); |
|
||||||
mVideoView.release(true); |
|
||||||
IjkMediaPlayer.native_profileEnd(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,108 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.config; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.content.SharedPreferences; |
|
||||||
import android.preference.PreferenceManager; |
|
||||||
import com.frank.living.R; |
|
||||||
|
|
||||||
public class Settings { |
|
||||||
private Context mAppContext; |
|
||||||
private SharedPreferences mSharedPreferences; |
|
||||||
|
|
||||||
public static final int PV_PLAYER__Auto = 0; |
|
||||||
public static final int PV_PLAYER__AndroidMediaPlayer = 1; |
|
||||||
public static final int PV_PLAYER__IjkMediaPlayer = 2; |
|
||||||
public static final int PV_PLAYER__IjkExoMediaPlayer = 3; |
|
||||||
|
|
||||||
public Settings(Context context) { |
|
||||||
mAppContext = context.getApplicationContext(); |
|
||||||
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mAppContext); |
|
||||||
} |
|
||||||
|
|
||||||
public int getPlayer() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_player); |
|
||||||
String value = mSharedPreferences.getString(key, ""); |
|
||||||
try { |
|
||||||
return Integer.valueOf(value).intValue(); |
|
||||||
} catch (NumberFormatException e) { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getUsingMediaCodec() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_using_media_codec); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getUsingMediaCodecAutoRotate() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_using_media_codec_auto_rotate); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getMediaCodecHandleResolutionChange() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_media_codec_handle_resolution_change); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getUsingOpenSLES() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_using_opensl_es); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public String getPixelFormat() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_pixel_format); |
|
||||||
return mSharedPreferences.getString(key, ""); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getEnableNoView() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_enable_no_view); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getEnableSurfaceView() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_enable_surface_view); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getEnableTextureView() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_enable_texture_view); |
|
||||||
return mSharedPreferences.getBoolean(key, true); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getEnableDetachedSurfaceTextureView() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_enable_detached_surface_texture); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean getUsingMediaDataSource() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_using_mediadatasource); |
|
||||||
return mSharedPreferences.getBoolean(key, false); |
|
||||||
} |
|
||||||
|
|
||||||
public String getLastDirectory() { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_last_directory); |
|
||||||
return mSharedPreferences.getString(key, "/"); |
|
||||||
} |
|
||||||
|
|
||||||
public void setLastDirectory(String path) { |
|
||||||
String key = mAppContext.getString(R.string.pref_key_last_directory); |
|
||||||
mSharedPreferences.edit().putString(key, path).apply(); |
|
||||||
} |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
package com.frank.living.constant; |
|
||||||
|
|
||||||
public class Constants { |
|
||||||
|
|
||||||
//broadcast of add client
|
|
||||||
public final static String ACTION_CLIENT_ADD = "action.client.ADD"; |
|
||||||
//broadcast of remove client
|
|
||||||
public final static String ACTION_CLIENT_REMOVE = "action.client.REMOVE"; |
|
||||||
|
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
package com.frank.living.listener; |
|
||||||
|
|
||||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
* Created by frank on 2019/1/4. |
|
||||||
*/ |
|
||||||
|
|
||||||
public interface IjkPlayerListener { |
|
||||||
|
|
||||||
void onIjkPlayer(IjkMediaPlayer ijkMediaPlayer); |
|
||||||
|
|
||||||
} |
|
@ -1,59 +0,0 @@ |
|||||||
package com.frank.living.listener; |
|
||||||
|
|
||||||
import android.view.MotionEvent; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
/** |
|
||||||
* double click listener |
|
||||||
* Created by xufulong on 2019/2/27. |
|
||||||
*/ |
|
||||||
|
|
||||||
public class OnDoubleClickListener implements View.OnTouchListener { |
|
||||||
|
|
||||||
private final static long DOUBLE_TIME = 500; |
|
||||||
private long firstClick; |
|
||||||
private long secondClick; |
|
||||||
private int clickCount; |
|
||||||
|
|
||||||
private OnDoubleClick onDoubleClick; |
|
||||||
|
|
||||||
public OnDoubleClickListener(OnDoubleClick onDoubleClick) { |
|
||||||
this.onDoubleClick = onDoubleClick; |
|
||||||
} |
|
||||||
|
|
||||||
public interface OnDoubleClick { |
|
||||||
void onDouble(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean onTouch(View v, MotionEvent event) { |
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP) { |
|
||||||
return v.performClick(); |
|
||||||
} else if (event.getAction() == MotionEvent.ACTION_DOWN) { |
|
||||||
clickCount++; |
|
||||||
if (clickCount == 1) { |
|
||||||
firstClick = System.currentTimeMillis(); |
|
||||||
} else if (clickCount == 2) { |
|
||||||
secondClick = System.currentTimeMillis(); |
|
||||||
if (secondClick - firstClick <= DOUBLE_TIME) { |
|
||||||
firstClick = 0; |
|
||||||
clickCount = 1; |
|
||||||
if (onDoubleClick != null) { |
|
||||||
onDoubleClick.onDouble(); |
|
||||||
} |
|
||||||
} else { |
|
||||||
firstClick = secondClick; |
|
||||||
clickCount = 1; |
|
||||||
} |
|
||||||
secondClick = 0; |
|
||||||
} else { |
|
||||||
clickCount = 0; |
|
||||||
firstClick = 0; |
|
||||||
secondClick = 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
package com.frank.living.util; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.graphics.Bitmap; |
|
||||||
import android.os.Looper; |
|
||||||
import android.text.TextUtils; |
|
||||||
import android.widget.Toast; |
|
||||||
|
|
||||||
import java.io.FileNotFoundException; |
|
||||||
import java.io.FileOutputStream; |
|
||||||
import java.io.IOException; |
|
||||||
|
|
||||||
/** |
|
||||||
* the tool of photo |
|
||||||
* Created by frank on 2019/12/31 |
|
||||||
*/ |
|
||||||
|
|
||||||
public class PhotoUtil { |
|
||||||
|
|
||||||
public static void savePhoto(Bitmap bitmap, String path, Context context) { |
|
||||||
savePhoto(bitmap, path, context, 100); |
|
||||||
} |
|
||||||
|
|
||||||
public static void savePhoto(Bitmap bitmap, String path, Context context, int quality) { |
|
||||||
if (bitmap == null || TextUtils.isEmpty(path) || context == null) { |
|
||||||
return; |
|
||||||
} |
|
||||||
if (quality <= 0 || quality > 100) { |
|
||||||
quality = 100; |
|
||||||
} |
|
||||||
FileOutputStream fileOutputStream = null; |
|
||||||
try { |
|
||||||
fileOutputStream = new FileOutputStream(path); |
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, fileOutputStream); |
|
||||||
fileOutputStream.flush(); |
|
||||||
if (Looper.myLooper() == Looper.getMainLooper()) { |
|
||||||
Toast.makeText(context.getApplicationContext(), "save success:" + path, Toast.LENGTH_SHORT).show(); |
|
||||||
} |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} finally { |
|
||||||
if (fileOutputStream != null) { |
|
||||||
try { |
|
||||||
fileOutputStream.close(); |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,87 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.annotation.Nullable; |
|
||||||
import androidx.appcompat.app.ActionBar; |
|
||||||
import android.util.AttributeSet; |
|
||||||
import android.view.View; |
|
||||||
import android.widget.MediaController; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
|
|
||||||
public class AndroidMediaController extends MediaController implements IMediaController { |
|
||||||
private ActionBar mActionBar; |
|
||||||
|
|
||||||
public AndroidMediaController(Context context, AttributeSet attrs) { |
|
||||||
super(context, attrs); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public AndroidMediaController(Context context, boolean useFastForward) { |
|
||||||
super(context, useFastForward); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public AndroidMediaController(Context context) { |
|
||||||
super(context); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
private void initView(Context context) { |
|
||||||
} |
|
||||||
|
|
||||||
public void setSupportActionBar(@Nullable ActionBar actionBar) { |
|
||||||
mActionBar = actionBar; |
|
||||||
if (isShowing()) { |
|
||||||
actionBar.show(); |
|
||||||
} else { |
|
||||||
actionBar.hide(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void show() { |
|
||||||
super.show(); |
|
||||||
if (mActionBar != null) |
|
||||||
mActionBar.show(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void hide() { |
|
||||||
super.hide(); |
|
||||||
if (mActionBar != null) |
|
||||||
mActionBar.hide(); |
|
||||||
for (View view : mShowOnceArray) |
|
||||||
view.setVisibility(View.GONE); |
|
||||||
mShowOnceArray.clear(); |
|
||||||
} |
|
||||||
|
|
||||||
//----------
|
|
||||||
// Extends
|
|
||||||
//----------
|
|
||||||
private ArrayList<View> mShowOnceArray = new ArrayList<View>(); |
|
||||||
|
|
||||||
public void showOnce(@NonNull View view) { |
|
||||||
mShowOnceArray.add(view); |
|
||||||
view.setVisibility(View.VISIBLE); |
|
||||||
show(); |
|
||||||
} |
|
||||||
} |
|
@ -1,57 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
import java.io.RandomAccessFile; |
|
||||||
|
|
||||||
import tv.danmaku.ijk.media.player.misc.IMediaDataSource; |
|
||||||
|
|
||||||
public class FileMediaDataSource implements IMediaDataSource { |
|
||||||
private RandomAccessFile mFile; |
|
||||||
private long mFileSize; |
|
||||||
|
|
||||||
public FileMediaDataSource(File file) throws IOException { |
|
||||||
mFile = new RandomAccessFile(file, "r"); |
|
||||||
mFileSize = mFile.length(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException { |
|
||||||
if (mFile.getFilePointer() != position) |
|
||||||
mFile.seek(position); |
|
||||||
|
|
||||||
if (size == 0) |
|
||||||
return 0; |
|
||||||
|
|
||||||
return mFile.read(buffer, 0, size); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public long getSize() throws IOException { |
|
||||||
return mFileSize; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void close() throws IOException { |
|
||||||
mFileSize = 0; |
|
||||||
mFile.close(); |
|
||||||
mFile = null; |
|
||||||
} |
|
||||||
} |
|
@ -1,42 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.view.View; |
|
||||||
import android.widget.MediaController; |
|
||||||
|
|
||||||
public interface IMediaController { |
|
||||||
void hide(); |
|
||||||
|
|
||||||
boolean isShowing(); |
|
||||||
|
|
||||||
void setAnchorView(View view); |
|
||||||
|
|
||||||
void setEnabled(boolean enabled); |
|
||||||
|
|
||||||
void setMediaPlayer(MediaController.MediaPlayerControl player); |
|
||||||
|
|
||||||
void show(int timeout); |
|
||||||
|
|
||||||
void show(); |
|
||||||
|
|
||||||
//----------
|
|
||||||
// Extends
|
|
||||||
//----------
|
|
||||||
void showOnce(View view); |
|
||||||
} |
|
@ -1,87 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.graphics.SurfaceTexture; |
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.annotation.Nullable; |
|
||||||
import android.view.Surface; |
|
||||||
import android.view.SurfaceHolder; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
|
||||||
|
|
||||||
public interface IRenderView { |
|
||||||
int AR_ASPECT_FIT_PARENT = 0; // without clip
|
|
||||||
int AR_ASPECT_FILL_PARENT = 1; // may clip
|
|
||||||
int AR_ASPECT_WRAP_CONTENT = 2; |
|
||||||
int AR_MATCH_PARENT = 3; |
|
||||||
int AR_16_9_FIT_PARENT = 4; |
|
||||||
int AR_4_3_FIT_PARENT = 5; |
|
||||||
|
|
||||||
View getView(); |
|
||||||
|
|
||||||
boolean shouldWaitForResize(); |
|
||||||
|
|
||||||
void setVideoSize(int videoWidth, int videoHeight); |
|
||||||
|
|
||||||
void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen); |
|
||||||
|
|
||||||
void setVideoRotation(int degree); |
|
||||||
|
|
||||||
void setAspectRatio(int aspectRatio); |
|
||||||
|
|
||||||
void addRenderCallback(@NonNull IRenderCallback callback); |
|
||||||
|
|
||||||
void removeRenderCallback(@NonNull IRenderCallback callback); |
|
||||||
|
|
||||||
interface ISurfaceHolder { |
|
||||||
void bindToMediaPlayer(IMediaPlayer mp); |
|
||||||
|
|
||||||
@NonNull |
|
||||||
IRenderView getRenderView(); |
|
||||||
|
|
||||||
@Nullable |
|
||||||
SurfaceHolder getSurfaceHolder(); |
|
||||||
|
|
||||||
@Nullable |
|
||||||
Surface openSurface(); |
|
||||||
|
|
||||||
@Nullable |
|
||||||
SurfaceTexture getSurfaceTexture(); |
|
||||||
} |
|
||||||
|
|
||||||
interface IRenderCallback { |
|
||||||
/** |
|
||||||
* @param holder |
|
||||||
* @param width could be 0 |
|
||||||
* @param height could be 0 |
|
||||||
*/ |
|
||||||
void onSurfaceCreated(@NonNull ISurfaceHolder holder, int width, int height); |
|
||||||
|
|
||||||
/** |
|
||||||
* @param holder |
|
||||||
* @param format could be 0 |
|
||||||
* @param width |
|
||||||
* @param height |
|
||||||
*/ |
|
||||||
void onSurfaceChanged(@NonNull ISurfaceHolder holder, int format, int width, int height); |
|
||||||
|
|
||||||
void onSurfaceDestroyed(@NonNull ISurfaceHolder holder); |
|
||||||
} |
|
||||||
} |
|
@ -1,164 +0,0 @@ |
|||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.annotation.SuppressLint; |
|
||||||
import android.content.Context; |
|
||||||
import android.os.Handler; |
|
||||||
import android.os.Message; |
|
||||||
import android.util.SparseArray; |
|
||||||
import android.view.View; |
|
||||||
import android.widget.TableLayout; |
|
||||||
|
|
||||||
import com.frank.living.R; |
|
||||||
|
|
||||||
import java.util.Locale; |
|
||||||
|
|
||||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.MediaPlayerProxy; |
|
||||||
|
|
||||||
public class InfoHudViewHolder { |
|
||||||
private TableLayoutBinder mTableLayoutBinder; |
|
||||||
private SparseArray<View> mRowMap = new SparseArray<View>(); |
|
||||||
private IMediaPlayer mMediaPlayer; |
|
||||||
private long mLoadCost = 0; |
|
||||||
private long mSeekCost = 0; |
|
||||||
|
|
||||||
public InfoHudViewHolder(Context context, TableLayout tableLayout) { |
|
||||||
mTableLayoutBinder = new TableLayoutBinder(context, tableLayout); |
|
||||||
} |
|
||||||
|
|
||||||
private void appendSection(int nameId) { |
|
||||||
mTableLayoutBinder.appendSection(nameId); |
|
||||||
} |
|
||||||
|
|
||||||
private void appendRow(int nameId) { |
|
||||||
View rowView = mTableLayoutBinder.appendRow2(nameId, null); |
|
||||||
mRowMap.put(nameId, rowView); |
|
||||||
} |
|
||||||
|
|
||||||
private void setRowValue(int id, String value) { |
|
||||||
View rowView = mRowMap.get(id); |
|
||||||
if (rowView == null) { |
|
||||||
rowView = mTableLayoutBinder.appendRow2(id, value); |
|
||||||
mRowMap.put(id, rowView); |
|
||||||
} else { |
|
||||||
mTableLayoutBinder.setValueText(rowView, value); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void setMediaPlayer(IMediaPlayer mp) { |
|
||||||
mMediaPlayer = mp; |
|
||||||
if (mMediaPlayer != null) { |
|
||||||
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500); |
|
||||||
} else { |
|
||||||
mHandler.removeMessages(MSG_UPDATE_HUD); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static String formatedDurationMilli(long duration) { |
|
||||||
if (duration >= 1000) { |
|
||||||
return String.format(Locale.US, "%.2f sec", ((float)duration) / 1000); |
|
||||||
} else { |
|
||||||
return String.format(Locale.US, "%d msec", duration); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static String formatedSpeed(long bytes,long elapsed_milli) { |
|
||||||
if (elapsed_milli <= 0) { |
|
||||||
return "0 B/s"; |
|
||||||
} |
|
||||||
|
|
||||||
if (bytes <= 0) { |
|
||||||
return "0 B/s"; |
|
||||||
} |
|
||||||
|
|
||||||
float bytes_per_sec = ((float)bytes) * 1000.f / elapsed_milli; |
|
||||||
if (bytes_per_sec >= 1000 * 1000) { |
|
||||||
return String.format(Locale.US, "%.2f MB/s", ((float)bytes_per_sec) / 1000 / 1000); |
|
||||||
} else if (bytes_per_sec >= 1000) { |
|
||||||
return String.format(Locale.US, "%.1f KB/s", ((float)bytes_per_sec) / 1000); |
|
||||||
} else { |
|
||||||
return String.format(Locale.US, "%d B/s", (long)bytes_per_sec); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void updateLoadCost(long time) { |
|
||||||
mLoadCost = time; |
|
||||||
} |
|
||||||
|
|
||||||
public void updateSeekCost(long time) { |
|
||||||
mSeekCost = time; |
|
||||||
} |
|
||||||
|
|
||||||
private static String formatedSize(long bytes) { |
|
||||||
if (bytes >= 100 * 1000) { |
|
||||||
return String.format(Locale.US, "%.2f MB", ((float)bytes) / 1000 / 1000); |
|
||||||
} else if (bytes >= 100) { |
|
||||||
return String.format(Locale.US, "%.1f KB", ((float)bytes) / 1000); |
|
||||||
} else { |
|
||||||
return String.format(Locale.US, "%d B", bytes); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static final int MSG_UPDATE_HUD = 1; |
|
||||||
@SuppressLint("HandlerLeak") |
|
||||||
private Handler mHandler = new Handler() { |
|
||||||
@Override |
|
||||||
public void handleMessage(Message msg) { |
|
||||||
switch (msg.what) { |
|
||||||
case MSG_UPDATE_HUD: { |
|
||||||
InfoHudViewHolder holder = InfoHudViewHolder.this; |
|
||||||
IjkMediaPlayer mp = null; |
|
||||||
if (mMediaPlayer == null) |
|
||||||
break; |
|
||||||
if (mMediaPlayer instanceof IjkMediaPlayer) { |
|
||||||
mp = (IjkMediaPlayer) mMediaPlayer; |
|
||||||
} else if (mMediaPlayer instanceof MediaPlayerProxy) { |
|
||||||
MediaPlayerProxy proxy = (MediaPlayerProxy) mMediaPlayer; |
|
||||||
IMediaPlayer internal = proxy.getInternalMediaPlayer(); |
|
||||||
if (internal != null && internal instanceof IjkMediaPlayer) |
|
||||||
mp = (IjkMediaPlayer) internal; |
|
||||||
} |
|
||||||
if (mp == null) |
|
||||||
break; |
|
||||||
|
|
||||||
int vdec = mp.getVideoDecoder(); |
|
||||||
switch (vdec) { |
|
||||||
case IjkMediaPlayer.FFP_PROPV_DECODER_AVCODEC: |
|
||||||
setRowValue(R.string.vdec, "avcodec"); |
|
||||||
break; |
|
||||||
case IjkMediaPlayer.FFP_PROPV_DECODER_MEDIACODEC: |
|
||||||
setRowValue(R.string.vdec, "MediaCodec"); |
|
||||||
break; |
|
||||||
default: |
|
||||||
setRowValue(R.string.vdec, ""); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
float fpsOutput = mp.getVideoOutputFramesPerSecond(); |
|
||||||
float fpsDecode = mp.getVideoDecodeFramesPerSecond(); |
|
||||||
setRowValue(R.string.fps, String.format(Locale.US, "%.2f / %.2f", fpsDecode, fpsOutput)); |
|
||||||
|
|
||||||
long videoCachedDuration = mp.getVideoCachedDuration(); |
|
||||||
long audioCachedDuration = mp.getAudioCachedDuration(); |
|
||||||
long videoCachedBytes = mp.getVideoCachedBytes(); |
|
||||||
long audioCachedBytes = mp.getAudioCachedBytes(); |
|
||||||
long tcpSpeed = mp.getTcpSpeed(); |
|
||||||
long bitRate = mp.getBitRate(); |
|
||||||
long seekLoadDuration = mp.getSeekLoadDuration(); |
|
||||||
|
|
||||||
setRowValue(R.string.v_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(videoCachedDuration), formatedSize(videoCachedBytes))); |
|
||||||
setRowValue(R.string.a_cache, String.format(Locale.US, "%s, %s", formatedDurationMilli(audioCachedDuration), formatedSize(audioCachedBytes))); |
|
||||||
setRowValue(R.string.load_cost, String.format(Locale.US, "%d ms", mLoadCost)); |
|
||||||
setRowValue(R.string.seek_cost, String.format(Locale.US, "%d ms", mSeekCost)); |
|
||||||
setRowValue(R.string.seek_load_cost, String.format(Locale.US, "%d ms", seekLoadDuration)); |
|
||||||
setRowValue(R.string.tcp_speed, String.format(Locale.US, "%s", formatedSpeed(tcpSpeed, 1000))); |
|
||||||
setRowValue(R.string.bit_rate, String.format(Locale.US, "%.2f kbs", bitRate/1000f)); |
|
||||||
|
|
||||||
mHandler.removeMessages(MSG_UPDATE_HUD); |
|
||||||
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
} |
|
@ -1,248 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import android.view.View; |
|
||||||
|
|
||||||
import com.frank.living.R; |
|
||||||
|
|
||||||
import java.lang.ref.WeakReference; |
|
||||||
|
|
||||||
public final class MeasureHelper { |
|
||||||
private WeakReference<View> mWeakView; |
|
||||||
|
|
||||||
private int mVideoWidth; |
|
||||||
private int mVideoHeight; |
|
||||||
private int mVideoSarNum; |
|
||||||
private int mVideoSarDen; |
|
||||||
|
|
||||||
private int mVideoRotationDegree; |
|
||||||
|
|
||||||
private int mMeasuredWidth; |
|
||||||
private int mMeasuredHeight; |
|
||||||
|
|
||||||
private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT; |
|
||||||
|
|
||||||
public MeasureHelper(View view) { |
|
||||||
mWeakView = new WeakReference<View>(view); |
|
||||||
} |
|
||||||
|
|
||||||
public View getView() { |
|
||||||
if (mWeakView == null) |
|
||||||
return null; |
|
||||||
return mWeakView.get(); |
|
||||||
} |
|
||||||
|
|
||||||
public void setVideoSize(int videoWidth, int videoHeight) { |
|
||||||
mVideoWidth = videoWidth; |
|
||||||
mVideoHeight = videoHeight; |
|
||||||
} |
|
||||||
|
|
||||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { |
|
||||||
mVideoSarNum = videoSarNum; |
|
||||||
mVideoSarDen = videoSarDen; |
|
||||||
} |
|
||||||
|
|
||||||
public void setVideoRotation(int videoRotationDegree) { |
|
||||||
mVideoRotationDegree = videoRotationDegree; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Must be called by View.onMeasure(int, int) |
|
||||||
* |
|
||||||
* @param widthMeasureSpec |
|
||||||
* @param heightMeasureSpec |
|
||||||
*/ |
|
||||||
public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
|
||||||
//Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
|
|
||||||
// + MeasureSpec.toString(heightMeasureSpec) + ")");
|
|
||||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) { |
|
||||||
int tempSpec = widthMeasureSpec; |
|
||||||
widthMeasureSpec = heightMeasureSpec; |
|
||||||
heightMeasureSpec = tempSpec; |
|
||||||
} |
|
||||||
|
|
||||||
int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec); |
|
||||||
int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec); |
|
||||||
if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) { |
|
||||||
width = widthMeasureSpec; |
|
||||||
height = heightMeasureSpec; |
|
||||||
} else if (mVideoWidth > 0 && mVideoHeight > 0) { |
|
||||||
int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec); |
|
||||||
int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec); |
|
||||||
int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec); |
|
||||||
int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec); |
|
||||||
|
|
||||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) { |
|
||||||
float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize; |
|
||||||
float displayAspectRatio; |
|
||||||
switch (mCurrentAspectRatio) { |
|
||||||
case IRenderView.AR_16_9_FIT_PARENT: |
|
||||||
displayAspectRatio = 16.0f / 9.0f; |
|
||||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) |
|
||||||
displayAspectRatio = 1.0f / displayAspectRatio; |
|
||||||
break; |
|
||||||
case IRenderView.AR_4_3_FIT_PARENT: |
|
||||||
displayAspectRatio = 4.0f / 3.0f; |
|
||||||
if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) |
|
||||||
displayAspectRatio = 1.0f / displayAspectRatio; |
|
||||||
break; |
|
||||||
case IRenderView.AR_ASPECT_FIT_PARENT: |
|
||||||
case IRenderView.AR_ASPECT_FILL_PARENT: |
|
||||||
case IRenderView.AR_ASPECT_WRAP_CONTENT: |
|
||||||
default: |
|
||||||
displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight; |
|
||||||
if (mVideoSarNum > 0 && mVideoSarDen > 0) |
|
||||||
displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen; |
|
||||||
break; |
|
||||||
} |
|
||||||
boolean shouldBeWider = displayAspectRatio > specAspectRatio; |
|
||||||
|
|
||||||
switch (mCurrentAspectRatio) { |
|
||||||
case IRenderView.AR_ASPECT_FIT_PARENT: |
|
||||||
case IRenderView.AR_16_9_FIT_PARENT: |
|
||||||
case IRenderView.AR_4_3_FIT_PARENT: |
|
||||||
if (shouldBeWider) { |
|
||||||
// too wide, fix width
|
|
||||||
width = widthSpecSize; |
|
||||||
height = (int) (width / displayAspectRatio); |
|
||||||
} else { |
|
||||||
// too high, fix height
|
|
||||||
height = heightSpecSize; |
|
||||||
width = (int) (height * displayAspectRatio); |
|
||||||
} |
|
||||||
break; |
|
||||||
case IRenderView.AR_ASPECT_FILL_PARENT: |
|
||||||
if (shouldBeWider) { |
|
||||||
// not high enough, fix height
|
|
||||||
height = heightSpecSize; |
|
||||||
width = (int) (height * displayAspectRatio); |
|
||||||
} else { |
|
||||||
// not wide enough, fix width
|
|
||||||
width = widthSpecSize; |
|
||||||
height = (int) (width / displayAspectRatio); |
|
||||||
} |
|
||||||
break; |
|
||||||
case IRenderView.AR_ASPECT_WRAP_CONTENT: |
|
||||||
default: |
|
||||||
if (shouldBeWider) { |
|
||||||
// too wide, fix width
|
|
||||||
width = Math.min(mVideoWidth, widthSpecSize); |
|
||||||
height = (int) (width / displayAspectRatio); |
|
||||||
} else { |
|
||||||
// too high, fix height
|
|
||||||
height = Math.min(mVideoHeight, heightSpecSize); |
|
||||||
width = (int) (height * displayAspectRatio); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) { |
|
||||||
// the size is fixed
|
|
||||||
width = widthSpecSize; |
|
||||||
height = heightSpecSize; |
|
||||||
|
|
||||||
// for compatibility, we adjust size based on aspect ratio
|
|
||||||
if (mVideoWidth * height < width * mVideoHeight) { |
|
||||||
//Log.i("@@@", "image too wide, correcting");
|
|
||||||
width = height * mVideoWidth / mVideoHeight; |
|
||||||
} else if (mVideoWidth * height > width * mVideoHeight) { |
|
||||||
//Log.i("@@@", "image too tall, correcting");
|
|
||||||
height = width * mVideoHeight / mVideoWidth; |
|
||||||
} |
|
||||||
} else if (widthSpecMode == View.MeasureSpec.EXACTLY) { |
|
||||||
// only the width is fixed, adjust the height to match aspect ratio if possible
|
|
||||||
width = widthSpecSize; |
|
||||||
height = width * mVideoHeight / mVideoWidth; |
|
||||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { |
|
||||||
// couldn't match aspect ratio within the constraints
|
|
||||||
height = heightSpecSize; |
|
||||||
} |
|
||||||
} else if (heightSpecMode == View.MeasureSpec.EXACTLY) { |
|
||||||
// only the height is fixed, adjust the width to match aspect ratio if possible
|
|
||||||
height = heightSpecSize; |
|
||||||
width = height * mVideoWidth / mVideoHeight; |
|
||||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { |
|
||||||
// couldn't match aspect ratio within the constraints
|
|
||||||
width = widthSpecSize; |
|
||||||
} |
|
||||||
} else { |
|
||||||
// neither the width nor the height are fixed, try to use actual video size
|
|
||||||
width = mVideoWidth; |
|
||||||
height = mVideoHeight; |
|
||||||
if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) { |
|
||||||
// too tall, decrease both width and height
|
|
||||||
height = heightSpecSize; |
|
||||||
width = height * mVideoWidth / mVideoHeight; |
|
||||||
} |
|
||||||
if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) { |
|
||||||
// too wide, decrease both width and height
|
|
||||||
width = widthSpecSize; |
|
||||||
height = width * mVideoHeight / mVideoWidth; |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
// no size yet, just adopt the given spec sizes
|
|
||||||
} |
|
||||||
|
|
||||||
mMeasuredWidth = width; |
|
||||||
mMeasuredHeight = height; |
|
||||||
} |
|
||||||
|
|
||||||
public int getMeasuredWidth() { |
|
||||||
return mMeasuredWidth; |
|
||||||
} |
|
||||||
|
|
||||||
public int getMeasuredHeight() { |
|
||||||
return mMeasuredHeight; |
|
||||||
} |
|
||||||
|
|
||||||
public void setAspectRatio(int aspectRatio) { |
|
||||||
mCurrentAspectRatio = aspectRatio; |
|
||||||
} |
|
||||||
|
|
||||||
@NonNull |
|
||||||
public static String getAspectRatioText(Context context, int aspectRatio) { |
|
||||||
String text; |
|
||||||
switch (aspectRatio) { |
|
||||||
case IRenderView.AR_ASPECT_FIT_PARENT: |
|
||||||
text = context.getString(R.string.VideoView_ar_aspect_fit_parent); |
|
||||||
break; |
|
||||||
case IRenderView.AR_ASPECT_FILL_PARENT: |
|
||||||
text = context.getString(R.string.VideoView_ar_aspect_fill_parent); |
|
||||||
break; |
|
||||||
case IRenderView.AR_ASPECT_WRAP_CONTENT: |
|
||||||
text = context.getString(R.string.VideoView_ar_aspect_wrap_content); |
|
||||||
break; |
|
||||||
case IRenderView.AR_MATCH_PARENT: |
|
||||||
text = context.getString(R.string.VideoView_ar_match_parent); |
|
||||||
break; |
|
||||||
case IRenderView.AR_16_9_FIT_PARENT: |
|
||||||
text = context.getString(R.string.VideoView_ar_16_9_fit_parent); |
|
||||||
break; |
|
||||||
case IRenderView.AR_4_3_FIT_PARENT: |
|
||||||
text = context.getString(R.string.VideoView_ar_4_3_fit_parent); |
|
||||||
break; |
|
||||||
default: |
|
||||||
text = context.getString(R.string.N_A); |
|
||||||
break; |
|
||||||
} |
|
||||||
return text; |
|
||||||
} |
|
||||||
} |
|
@ -1,76 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.MediaPlayerProxy; |
|
||||||
import tv.danmaku.ijk.media.player.TextureMediaPlayer; |
|
||||||
|
|
||||||
public class MediaPlayerCompat { |
|
||||||
public static String getName(IMediaPlayer mp) { |
|
||||||
if (mp == null) { |
|
||||||
return "null"; |
|
||||||
} else if (mp instanceof TextureMediaPlayer) { |
|
||||||
StringBuilder sb = new StringBuilder("TextureMediaPlayer <"); |
|
||||||
IMediaPlayer internalMediaPlayer = ((TextureMediaPlayer) mp).getInternalMediaPlayer(); |
|
||||||
if (internalMediaPlayer == null) { |
|
||||||
sb.append("null>"); |
|
||||||
} else { |
|
||||||
sb.append(internalMediaPlayer.getClass().getSimpleName()); |
|
||||||
sb.append(">"); |
|
||||||
} |
|
||||||
return sb.toString(); |
|
||||||
} else { |
|
||||||
return mp.getClass().getSimpleName(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static IjkMediaPlayer getIjkMediaPlayer(IMediaPlayer mp) { |
|
||||||
IjkMediaPlayer ijkMediaPlayer = null; |
|
||||||
if (mp == null) { |
|
||||||
return null; |
|
||||||
} if (mp instanceof IjkMediaPlayer) { |
|
||||||
ijkMediaPlayer = (IjkMediaPlayer) mp; |
|
||||||
} else if (mp instanceof MediaPlayerProxy && ((MediaPlayerProxy) mp).getInternalMediaPlayer() instanceof IjkMediaPlayer) { |
|
||||||
ijkMediaPlayer = (IjkMediaPlayer) ((MediaPlayerProxy) mp).getInternalMediaPlayer(); |
|
||||||
} |
|
||||||
return ijkMediaPlayer; |
|
||||||
} |
|
||||||
|
|
||||||
public static void selectTrack(IMediaPlayer mp, int stream) { |
|
||||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); |
|
||||||
if (ijkMediaPlayer == null) |
|
||||||
return; |
|
||||||
ijkMediaPlayer.selectTrack(stream); |
|
||||||
} |
|
||||||
|
|
||||||
public static void deselectTrack(IMediaPlayer mp, int stream) { |
|
||||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); |
|
||||||
if (ijkMediaPlayer == null) |
|
||||||
return; |
|
||||||
ijkMediaPlayer.deselectTrack(stream); |
|
||||||
} |
|
||||||
|
|
||||||
public static int getSelectedTrack(IMediaPlayer mp, int trackType) { |
|
||||||
IjkMediaPlayer ijkMediaPlayer = getIjkMediaPlayer(mp); |
|
||||||
if (ijkMediaPlayer == null) |
|
||||||
return -1; |
|
||||||
return ijkMediaPlayer.getSelectedTrack(trackType); |
|
||||||
} |
|
||||||
} |
|
@ -1,286 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.annotation.TargetApi; |
|
||||||
import android.content.Context; |
|
||||||
import android.graphics.SurfaceTexture; |
|
||||||
import android.os.Build; |
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.annotation.Nullable; |
|
||||||
import android.util.AttributeSet; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.Surface; |
|
||||||
import android.view.SurfaceHolder; |
|
||||||
import android.view.SurfaceView; |
|
||||||
import android.view.View; |
|
||||||
import android.view.accessibility.AccessibilityEvent; |
|
||||||
import android.view.accessibility.AccessibilityNodeInfo; |
|
||||||
import java.lang.ref.WeakReference; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder; |
|
||||||
|
|
||||||
public class SurfaceRenderView extends SurfaceView implements IRenderView { |
|
||||||
private MeasureHelper mMeasureHelper; |
|
||||||
|
|
||||||
public SurfaceRenderView(Context context) { |
|
||||||
super(context); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public SurfaceRenderView(Context context, AttributeSet attrs) { |
|
||||||
super(context, attrs); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) { |
|
||||||
super(context, attrs, defStyleAttr); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
|
||||||
public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
|
||||||
super(context, attrs, defStyleAttr, defStyleRes); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
private void initView(Context context) { |
|
||||||
mMeasureHelper = new MeasureHelper(this); |
|
||||||
mSurfaceCallback = new SurfaceCallback(this); |
|
||||||
getHolder().addCallback(mSurfaceCallback); |
|
||||||
//noinspection deprecation
|
|
||||||
getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public View getView() { |
|
||||||
return this; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean shouldWaitForResize() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
//--------------------
|
|
||||||
// Layout & Measure
|
|
||||||
//--------------------
|
|
||||||
@Override |
|
||||||
public void setVideoSize(int videoWidth, int videoHeight) { |
|
||||||
if (videoWidth > 0 && videoHeight > 0) { |
|
||||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight); |
|
||||||
getHolder().setFixedSize(videoWidth, videoHeight); |
|
||||||
requestLayout(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { |
|
||||||
if (videoSarNum > 0 && videoSarDen > 0) { |
|
||||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen); |
|
||||||
requestLayout(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setVideoRotation(int degree) { |
|
||||||
Log.e("", "SurfaceView doesn't support rotation (" + degree + ")!\n"); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setAspectRatio(int aspectRatio) { |
|
||||||
mMeasureHelper.setAspectRatio(aspectRatio); |
|
||||||
requestLayout(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
|
||||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); |
|
||||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight()); |
|
||||||
} |
|
||||||
|
|
||||||
//--------------------
|
|
||||||
// SurfaceViewHolder
|
|
||||||
//--------------------
|
|
||||||
|
|
||||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder { |
|
||||||
private SurfaceRenderView mSurfaceView; |
|
||||||
private SurfaceHolder mSurfaceHolder; |
|
||||||
|
|
||||||
public InternalSurfaceHolder(@NonNull SurfaceRenderView surfaceView, |
|
||||||
@Nullable SurfaceHolder surfaceHolder) { |
|
||||||
mSurfaceView = surfaceView; |
|
||||||
mSurfaceHolder = surfaceHolder; |
|
||||||
} |
|
||||||
|
|
||||||
public void bindToMediaPlayer(IMediaPlayer mp) { |
|
||||||
if (mp != null) { |
|
||||||
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) && |
|
||||||
(mp instanceof ISurfaceTextureHolder)) { |
|
||||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp; |
|
||||||
textureHolder.setSurfaceTexture(null); |
|
||||||
} |
|
||||||
mp.setDisplay(mSurfaceHolder); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@NonNull |
|
||||||
@Override |
|
||||||
public IRenderView getRenderView() { |
|
||||||
return mSurfaceView; |
|
||||||
} |
|
||||||
|
|
||||||
@Nullable |
|
||||||
@Override |
|
||||||
public SurfaceHolder getSurfaceHolder() { |
|
||||||
return mSurfaceHolder; |
|
||||||
} |
|
||||||
|
|
||||||
@Nullable |
|
||||||
@Override |
|
||||||
public SurfaceTexture getSurfaceTexture() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Nullable |
|
||||||
@Override |
|
||||||
public Surface openSurface() { |
|
||||||
if (mSurfaceHolder == null) |
|
||||||
return null; |
|
||||||
return mSurfaceHolder.getSurface(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//-------------------------
|
|
||||||
// SurfaceHolder.Callback
|
|
||||||
//-------------------------
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void addRenderCallback(IRenderCallback callback) { |
|
||||||
mSurfaceCallback.addRenderCallback(callback); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void removeRenderCallback(IRenderCallback callback) { |
|
||||||
mSurfaceCallback.removeRenderCallback(callback); |
|
||||||
} |
|
||||||
|
|
||||||
private SurfaceCallback mSurfaceCallback; |
|
||||||
|
|
||||||
private static final class SurfaceCallback implements SurfaceHolder.Callback { |
|
||||||
private SurfaceHolder mSurfaceHolder; |
|
||||||
private boolean mIsFormatChanged; |
|
||||||
private int mFormat; |
|
||||||
private int mWidth; |
|
||||||
private int mHeight; |
|
||||||
|
|
||||||
private WeakReference<SurfaceRenderView> mWeakSurfaceView; |
|
||||||
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>(); |
|
||||||
|
|
||||||
public SurfaceCallback(@NonNull SurfaceRenderView surfaceView) { |
|
||||||
mWeakSurfaceView = new WeakReference<SurfaceRenderView>(surfaceView); |
|
||||||
} |
|
||||||
|
|
||||||
public void addRenderCallback(@NonNull IRenderCallback callback) { |
|
||||||
mRenderCallbackMap.put(callback, callback); |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = null; |
|
||||||
if (mSurfaceHolder != null) { |
|
||||||
if (surfaceHolder == null) |
|
||||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
|
||||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight); |
|
||||||
} |
|
||||||
|
|
||||||
if (mIsFormatChanged) { |
|
||||||
if (surfaceHolder == null) |
|
||||||
surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
|
||||||
callback.onSurfaceChanged(surfaceHolder, mFormat, mWidth, mHeight); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void removeRenderCallback(@NonNull IRenderCallback callback) { |
|
||||||
mRenderCallbackMap.remove(callback); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void surfaceCreated(SurfaceHolder holder) { |
|
||||||
mSurfaceHolder = holder; |
|
||||||
mIsFormatChanged = false; |
|
||||||
mFormat = 0; |
|
||||||
mWidth = 0; |
|
||||||
mHeight = 0; |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
|
||||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
|
||||||
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void surfaceDestroyed(SurfaceHolder holder) { |
|
||||||
mSurfaceHolder = null; |
|
||||||
mIsFormatChanged = false; |
|
||||||
mFormat = 0; |
|
||||||
mWidth = 0; |
|
||||||
mHeight = 0; |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
|
||||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
|
||||||
renderCallback.onSurfaceDestroyed(surfaceHolder); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void surfaceChanged(SurfaceHolder holder, int format, |
|
||||||
int width, int height) { |
|
||||||
mSurfaceHolder = holder; |
|
||||||
mIsFormatChanged = true; |
|
||||||
mFormat = format; |
|
||||||
mWidth = width; |
|
||||||
mHeight = height; |
|
||||||
|
|
||||||
// mMeasureHelper.setVideoSize(width, height);
|
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakSurfaceView.get(), mSurfaceHolder); |
|
||||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
|
||||||
renderCallback.onSurfaceChanged(surfaceHolder, format, width, height); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//--------------------
|
|
||||||
// Accessibility
|
|
||||||
//--------------------
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) { |
|
||||||
super.onInitializeAccessibilityEvent(event); |
|
||||||
event.setClassName(SurfaceRenderView.class.getName()); |
|
||||||
} |
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) |
|
||||||
@Override |
|
||||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { |
|
||||||
super.onInitializeAccessibilityNodeInfo(info); |
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { |
|
||||||
info.setClassName(SurfaceRenderView.class.getName()); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,131 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import androidx.appcompat.app.AlertDialog; |
|
||||||
import android.view.LayoutInflater; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewGroup; |
|
||||||
import android.widget.TableLayout; |
|
||||||
import android.widget.TextView; |
|
||||||
|
|
||||||
import com.frank.living.R; |
|
||||||
|
|
||||||
public class TableLayoutBinder { |
|
||||||
private Context mContext; |
|
||||||
public ViewGroup mTableView; |
|
||||||
public TableLayout mTableLayout; |
|
||||||
|
|
||||||
public TableLayoutBinder(Context context) { |
|
||||||
this(context, R.layout.table_media_info); |
|
||||||
} |
|
||||||
|
|
||||||
public TableLayoutBinder(Context context, int layoutResourceId) { |
|
||||||
mContext = context; |
|
||||||
mTableView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutResourceId, null); |
|
||||||
mTableLayout = (TableLayout) mTableView.findViewById(R.id.table); |
|
||||||
} |
|
||||||
|
|
||||||
public TableLayoutBinder(Context context, TableLayout tableLayout) { |
|
||||||
mContext = context; |
|
||||||
mTableView = tableLayout; |
|
||||||
mTableLayout = tableLayout; |
|
||||||
} |
|
||||||
|
|
||||||
public View appendRow1(String name, String value) { |
|
||||||
return appendRow(R.layout.table_media_info_row1, name, value); |
|
||||||
} |
|
||||||
|
|
||||||
public View appendRow1(int nameId, String value) { |
|
||||||
return appendRow1(mContext.getString(nameId), value); |
|
||||||
} |
|
||||||
|
|
||||||
public View appendRow2(String name, String value) { |
|
||||||
return appendRow(R.layout.table_media_info_row2, name, value); |
|
||||||
} |
|
||||||
|
|
||||||
public View appendRow2(int nameId, String value) { |
|
||||||
return appendRow2(mContext.getString(nameId), value); |
|
||||||
} |
|
||||||
|
|
||||||
public View appendSection(String name) { |
|
||||||
return appendRow(R.layout.table_media_info_section, name, null); |
|
||||||
} |
|
||||||
|
|
||||||
public View appendSection(int nameId) { |
|
||||||
return appendSection(mContext.getString(nameId)); |
|
||||||
} |
|
||||||
|
|
||||||
public View appendRow(int layoutId, String name, String value) { |
|
||||||
ViewGroup rowView = (ViewGroup) LayoutInflater.from(mContext).inflate(layoutId, mTableLayout, false); |
|
||||||
setNameValueText(rowView, name, value); |
|
||||||
|
|
||||||
mTableLayout.addView(rowView); |
|
||||||
return rowView; |
|
||||||
} |
|
||||||
|
|
||||||
public ViewHolder obtainViewHolder(View rowView) { |
|
||||||
ViewHolder viewHolder = (ViewHolder) rowView.getTag(); |
|
||||||
if (viewHolder == null) { |
|
||||||
viewHolder = new ViewHolder(); |
|
||||||
viewHolder.mNameTextView = (TextView) rowView.findViewById(R.id.name); |
|
||||||
viewHolder.mValueTextView = (TextView) rowView.findViewById(R.id.value); |
|
||||||
rowView.setTag(viewHolder); |
|
||||||
} |
|
||||||
return viewHolder; |
|
||||||
} |
|
||||||
|
|
||||||
public void setNameValueText(View rowView, String name, String value) { |
|
||||||
ViewHolder viewHolder = obtainViewHolder(rowView); |
|
||||||
viewHolder.setName(name); |
|
||||||
viewHolder.setValue(value); |
|
||||||
} |
|
||||||
|
|
||||||
public void setValueText(View rowView, String value) { |
|
||||||
ViewHolder viewHolder = obtainViewHolder(rowView); |
|
||||||
viewHolder.setValue(value); |
|
||||||
} |
|
||||||
|
|
||||||
public ViewGroup buildLayout() { |
|
||||||
return mTableView; |
|
||||||
} |
|
||||||
|
|
||||||
public AlertDialog.Builder buildAlertDialogBuilder() { |
|
||||||
AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(mContext); |
|
||||||
dlgBuilder.setView(buildLayout()); |
|
||||||
return dlgBuilder; |
|
||||||
} |
|
||||||
|
|
||||||
private static class ViewHolder { |
|
||||||
public TextView mNameTextView; |
|
||||||
public TextView mValueTextView; |
|
||||||
|
|
||||||
public void setName(String name) { |
|
||||||
if (mNameTextView != null) { |
|
||||||
mNameTextView.setText(name); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void setValue(String value) { |
|
||||||
if (mValueTextView != null) { |
|
||||||
mValueTextView.setText(value); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,368 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2015 Bilibili |
|
||||||
* Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com> |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.frank.living.widget; |
|
||||||
|
|
||||||
import android.annotation.TargetApi; |
|
||||||
import android.content.Context; |
|
||||||
import android.graphics.SurfaceTexture; |
|
||||||
import android.os.Build; |
|
||||||
import androidx.annotation.NonNull; |
|
||||||
import androidx.annotation.Nullable; |
|
||||||
import android.util.AttributeSet; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.Surface; |
|
||||||
import android.view.SurfaceHolder; |
|
||||||
import android.view.TextureView; |
|
||||||
import android.view.View; |
|
||||||
import android.view.accessibility.AccessibilityEvent; |
|
||||||
import android.view.accessibility.AccessibilityNodeInfo; |
|
||||||
import java.lang.ref.WeakReference; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
import tv.danmaku.ijk.media.player.IMediaPlayer; |
|
||||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHolder; |
|
||||||
import tv.danmaku.ijk.media.player.ISurfaceTextureHost; |
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) |
|
||||||
public class TextureRenderView extends TextureView implements IRenderView { |
|
||||||
private static final String TAG = "TextureRenderView"; |
|
||||||
private MeasureHelper mMeasureHelper; |
|
||||||
|
|
||||||
public TextureRenderView(Context context) { |
|
||||||
super(context); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public TextureRenderView(Context context, AttributeSet attrs) { |
|
||||||
super(context, attrs); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr) { |
|
||||||
super(context, attrs, defStyleAttr); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
|
||||||
public TextureRenderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
|
||||||
super(context, attrs, defStyleAttr, defStyleRes); |
|
||||||
initView(context); |
|
||||||
} |
|
||||||
|
|
||||||
private void initView(Context context) { |
|
||||||
mMeasureHelper = new MeasureHelper(this); |
|
||||||
mSurfaceCallback = new SurfaceCallback(this); |
|
||||||
setSurfaceTextureListener(mSurfaceCallback); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public View getView() { |
|
||||||
return this; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean shouldWaitForResize() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onDetachedFromWindow() { |
|
||||||
mSurfaceCallback.willDetachFromWindow(); |
|
||||||
super.onDetachedFromWindow(); |
|
||||||
mSurfaceCallback.didDetachFromWindow(); |
|
||||||
} |
|
||||||
|
|
||||||
//--------------------
|
|
||||||
// Layout & Measure
|
|
||||||
//--------------------
|
|
||||||
@Override |
|
||||||
public void setVideoSize(int videoWidth, int videoHeight) { |
|
||||||
if (videoWidth > 0 && videoHeight > 0) { |
|
||||||
mMeasureHelper.setVideoSize(videoWidth, videoHeight); |
|
||||||
requestLayout(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) { |
|
||||||
if (videoSarNum > 0 && videoSarDen > 0) { |
|
||||||
mMeasureHelper.setVideoSampleAspectRatio(videoSarNum, videoSarDen); |
|
||||||
requestLayout(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setVideoRotation(int degree) { |
|
||||||
mMeasureHelper.setVideoRotation(degree); |
|
||||||
setRotation(degree); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void setAspectRatio(int aspectRatio) { |
|
||||||
mMeasureHelper.setAspectRatio(aspectRatio); |
|
||||||
requestLayout(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
|
||||||
mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); |
|
||||||
setMeasuredDimension(mMeasureHelper.getMeasuredWidth(), mMeasureHelper.getMeasuredHeight()); |
|
||||||
} |
|
||||||
|
|
||||||
//--------------------
|
|
||||||
// TextureViewHolder
|
|
||||||
//--------------------
|
|
||||||
|
|
||||||
public IRenderView.ISurfaceHolder getSurfaceHolder() { |
|
||||||
return new InternalSurfaceHolder(this, mSurfaceCallback.mSurfaceTexture, mSurfaceCallback); |
|
||||||
} |
|
||||||
|
|
||||||
private static final class InternalSurfaceHolder implements IRenderView.ISurfaceHolder { |
|
||||||
private TextureRenderView mTextureView; |
|
||||||
private SurfaceTexture mSurfaceTexture; |
|
||||||
private ISurfaceTextureHost mSurfaceTextureHost; |
|
||||||
|
|
||||||
public InternalSurfaceHolder(@NonNull TextureRenderView textureView, |
|
||||||
@Nullable SurfaceTexture surfaceTexture, |
|
||||||
@NonNull ISurfaceTextureHost surfaceTextureHost) { |
|
||||||
mTextureView = textureView; |
|
||||||
mSurfaceTexture = surfaceTexture; |
|
||||||
mSurfaceTextureHost = surfaceTextureHost; |
|
||||||
} |
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) |
|
||||||
public void bindToMediaPlayer(IMediaPlayer mp) { |
|
||||||
if (mp == null) |
|
||||||
return; |
|
||||||
|
|
||||||
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) && |
|
||||||
(mp instanceof ISurfaceTextureHolder)) { |
|
||||||
ISurfaceTextureHolder textureHolder = (ISurfaceTextureHolder) mp; |
|
||||||
mTextureView.mSurfaceCallback.setOwnSurfaceTexture(false); |
|
||||||
|
|
||||||
SurfaceTexture surfaceTexture = textureHolder.getSurfaceTexture(); |
|
||||||
if (surfaceTexture != null) { |
|
||||||
mTextureView.setSurfaceTexture(surfaceTexture); |
|
||||||
} else { |
|
||||||
textureHolder.setSurfaceTexture(mSurfaceTexture); |
|
||||||
textureHolder.setSurfaceTextureHost(mTextureView.mSurfaceCallback); |
|
||||||
} |
|
||||||
} else { |
|
||||||
mp.setSurface(openSurface()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@NonNull |
|
||||||
@Override |
|
||||||
public IRenderView getRenderView() { |
|
||||||
return mTextureView; |
|
||||||
} |
|
||||||
|
|
||||||
@Nullable |
|
||||||
@Override |
|
||||||
public SurfaceHolder getSurfaceHolder() { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Nullable |
|
||||||
@Override |
|
||||||
public SurfaceTexture getSurfaceTexture() { |
|
||||||
return mSurfaceTexture; |
|
||||||
} |
|
||||||
|
|
||||||
@Nullable |
|
||||||
@Override |
|
||||||
public Surface openSurface() { |
|
||||||
if (mSurfaceTexture == null) |
|
||||||
return null; |
|
||||||
return new Surface(mSurfaceTexture); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//-------------------------
|
|
||||||
// SurfaceHolder.Callback
|
|
||||||
//-------------------------
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void addRenderCallback(IRenderCallback callback) { |
|
||||||
mSurfaceCallback.addRenderCallback(callback); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void removeRenderCallback(IRenderCallback callback) { |
|
||||||
mSurfaceCallback.removeRenderCallback(callback); |
|
||||||
} |
|
||||||
|
|
||||||
private SurfaceCallback mSurfaceCallback; |
|
||||||
|
|
||||||
private static final class SurfaceCallback implements TextureView.SurfaceTextureListener, ISurfaceTextureHost { |
|
||||||
private SurfaceTexture mSurfaceTexture; |
|
||||||
private boolean mIsFormatChanged; |
|
||||||
private int mWidth; |
|
||||||
private int mHeight; |
|
||||||
|
|
||||||
private boolean mOwnSurfaceTexture = true; |
|
||||||
private boolean mWillDetachFromWindow = false; |
|
||||||
private boolean mDidDetachFromWindow = false; |
|
||||||
|
|
||||||
private WeakReference<TextureRenderView> mWeakRenderView; |
|
||||||
private Map<IRenderCallback, Object> mRenderCallbackMap = new ConcurrentHashMap<IRenderCallback, Object>(); |
|
||||||
|
|
||||||
public SurfaceCallback(@NonNull TextureRenderView renderView) { |
|
||||||
mWeakRenderView = new WeakReference<TextureRenderView>(renderView); |
|
||||||
} |
|
||||||
|
|
||||||
public void setOwnSurfaceTexture(boolean ownSurfaceTexture) { |
|
||||||
mOwnSurfaceTexture = ownSurfaceTexture; |
|
||||||
} |
|
||||||
|
|
||||||
public void addRenderCallback(@NonNull IRenderCallback callback) { |
|
||||||
mRenderCallbackMap.put(callback, callback); |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = null; |
|
||||||
if (mSurfaceTexture != null) { |
|
||||||
if (surfaceHolder == null) |
|
||||||
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this); |
|
||||||
callback.onSurfaceCreated(surfaceHolder, mWidth, mHeight); |
|
||||||
} |
|
||||||
|
|
||||||
if (mIsFormatChanged) { |
|
||||||
if (surfaceHolder == null) |
|
||||||
surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), mSurfaceTexture, this); |
|
||||||
callback.onSurfaceChanged(surfaceHolder, 0, mWidth, mHeight); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void removeRenderCallback(@NonNull IRenderCallback callback) { |
|
||||||
mRenderCallbackMap.remove(callback); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { |
|
||||||
mSurfaceTexture = surface; |
|
||||||
mIsFormatChanged = false; |
|
||||||
mWidth = 0; |
|
||||||
mHeight = 0; |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this); |
|
||||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
|
||||||
renderCallback.onSurfaceCreated(surfaceHolder, 0, 0); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { |
|
||||||
mSurfaceTexture = surface; |
|
||||||
mIsFormatChanged = true; |
|
||||||
mWidth = width; |
|
||||||
mHeight = height; |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this); |
|
||||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
|
||||||
renderCallback.onSurfaceChanged(surfaceHolder, 0, width, height); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { |
|
||||||
mSurfaceTexture = surface; |
|
||||||
mIsFormatChanged = false; |
|
||||||
mWidth = 0; |
|
||||||
mHeight = 0; |
|
||||||
|
|
||||||
ISurfaceHolder surfaceHolder = new InternalSurfaceHolder(mWeakRenderView.get(), surface, this); |
|
||||||
for (IRenderCallback renderCallback : mRenderCallbackMap.keySet()) { |
|
||||||
renderCallback.onSurfaceDestroyed(surfaceHolder); |
|
||||||
} |
|
||||||
|
|
||||||
Log.d(TAG, "onSurfaceTextureDestroyed: destroy: " + mOwnSurfaceTexture); |
|
||||||
return mOwnSurfaceTexture; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) { |
|
||||||
} |
|
||||||
|
|
||||||
//-------------------------
|
|
||||||
// ISurfaceTextureHost
|
|
||||||
//-------------------------
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void releaseSurfaceTexture(SurfaceTexture surfaceTexture) { |
|
||||||
if (surfaceTexture == null) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: null"); |
|
||||||
} else if (mDidDetachFromWindow) { |
|
||||||
if (surfaceTexture != mSurfaceTexture) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release different SurfaceTexture"); |
|
||||||
surfaceTexture.release(); |
|
||||||
} else if (!mOwnSurfaceTexture) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): release detached SurfaceTexture"); |
|
||||||
surfaceTexture.release(); |
|
||||||
} else { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: didDetachFromWindow(): already released by TextureView"); |
|
||||||
} |
|
||||||
} else if (mWillDetachFromWindow) { |
|
||||||
if (surfaceTexture != mSurfaceTexture) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): release different SurfaceTexture"); |
|
||||||
surfaceTexture.release(); |
|
||||||
} else if (!mOwnSurfaceTexture) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): re-attach SurfaceTexture to TextureView"); |
|
||||||
setOwnSurfaceTexture(true); |
|
||||||
} else { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: willDetachFromWindow(): will released by TextureView"); |
|
||||||
} |
|
||||||
} else { |
|
||||||
if (surfaceTexture != mSurfaceTexture) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: alive: release different SurfaceTexture"); |
|
||||||
surfaceTexture.release(); |
|
||||||
} else if (!mOwnSurfaceTexture) { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: alive: re-attach SurfaceTexture to TextureView"); |
|
||||||
setOwnSurfaceTexture(true); |
|
||||||
} else { |
|
||||||
Log.d(TAG, "releaseSurfaceTexture: alive: will released by TextureView"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public void willDetachFromWindow() { |
|
||||||
Log.d(TAG, "willDetachFromWindow()"); |
|
||||||
mWillDetachFromWindow = true; |
|
||||||
} |
|
||||||
|
|
||||||
public void didDetachFromWindow() { |
|
||||||
Log.d(TAG, "didDetachFromWindow()"); |
|
||||||
mDidDetachFromWindow = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//--------------------
|
|
||||||
// Accessibility
|
|
||||||
//--------------------
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) { |
|
||||||
super.onInitializeAccessibilityEvent(event); |
|
||||||
event.setClassName(TextureRenderView.class.getName()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { |
|
||||||
super.onInitializeAccessibilityNodeInfo(info); |
|
||||||
info.setClassName(TextureRenderView.class.getName()); |
|
||||||
} |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
# This file is automatically generated by Android Tools. |
|
||||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED! |
|
||||||
# |
|
||||||
# This file must be checked in Version Control Systems. |
|
||||||
# |
|
||||||
# To customize properties used by the Ant build system edit |
|
||||||
# "ant.properties", and override values to adapt the script to your |
|
||||||
# project structure. |
|
||||||
# |
|
||||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): |
|
||||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt |
|
||||||
|
|
||||||
# Project target. |
|
||||||
target=android-22 |
|
||||||
android.library.reference.1=../../../player-arm64/src/main |
|
||||||
android.library.reference.2=../../../player-armv5/src/main |
|
||||||
android.library.reference.3=../../../player-armv7a/src/main |
|
||||||
android.library.reference.4=../../../player-x86/src/main |
|
||||||
android.library.reference.5=../../../player-java/src/main |
|
||||||
android.library.reference.6=../../../../contrib/appcompat |
|
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 4.3 KiB |
@ -1,54 +0,0 @@ |
|||||||
<RelativeLayout |
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
xmlns:tools="http://schemas.android.com/tools" |
|
||||||
tools:context="com.frank.living.activity.RtspLiveActivity" |
|
||||||
android:id="@+id/drawer_layout" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent"> |
|
||||||
|
|
||||||
<com.frank.living.widget.IjkVideoView |
|
||||||
android:id="@+id/video_view" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent" |
|
||||||
android:layout_gravity="center"> |
|
||||||
</com.frank.living.widget.IjkVideoView> |
|
||||||
|
|
||||||
<ImageButton |
|
||||||
android:id="@+id/btn_play" |
|
||||||
android:layout_width="30dp" |
|
||||||
android:layout_height="30dp" |
|
||||||
android:background="@drawable/ic_pause" |
|
||||||
android:layout_alignParentBottom="true" |
|
||||||
android:layout_centerHorizontal="true" |
|
||||||
android:layout_marginBottom="10dp"/> |
|
||||||
|
|
||||||
<ImageButton |
|
||||||
android:id="@+id/btn_sound" |
|
||||||
android:layout_width="30dp" |
|
||||||
android:layout_height="30dp" |
|
||||||
android:background="@drawable/ic_silence" |
|
||||||
android:layout_alignParentBottom="true" |
|
||||||
android:layout_alignParentEnd="true" |
|
||||||
android:layout_marginEnd="20dp" |
|
||||||
android:layout_marginBottom="10dp"/> |
|
||||||
|
|
||||||
<ImageButton |
|
||||||
android:id="@+id/btn_screenshot" |
|
||||||
android:layout_width="30dp" |
|
||||||
android:layout_height="30dp" |
|
||||||
android:background="@drawable/ic_screen_shot" |
|
||||||
android:layout_alignParentBottom="true" |
|
||||||
android:layout_toStartOf="@+id/btn_sound" |
|
||||||
android:layout_marginEnd="30dp" |
|
||||||
android:layout_marginBottom="10dp"/> |
|
||||||
|
|
||||||
<TableLayout |
|
||||||
android:id="@+id/hud_view" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_gravity="end|center_vertical" |
|
||||||
android:background="@color/ijk_transparent_dark" |
|
||||||
android:padding="8dp" |
|
||||||
android:visibility="gone"/> |
|
||||||
|
|
||||||
</RelativeLayout> |
|
@ -1,48 +0,0 @@ |
|||||||
<RelativeLayout |
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent"> |
|
||||||
|
|
||||||
<View |
|
||||||
android:id="@+id/divider1" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="1dp" |
|
||||||
android:background="@color/ijk_color_blue_800" |
|
||||||
android:layout_centerVertical="true"/> |
|
||||||
|
|
||||||
<View |
|
||||||
android:id="@+id/divider2" |
|
||||||
android:layout_width="1dp" |
|
||||||
android:layout_height="match_parent" |
|
||||||
android:background="@color/ijk_color_blue_800" |
|
||||||
android:layout_centerHorizontal="true"/> |
|
||||||
|
|
||||||
<com.frank.living.widget.IjkVideoView |
|
||||||
android:id="@+id/video_view1" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_above="@+id/divider1" |
|
||||||
android:layout_toStartOf="@id/divider2"/> |
|
||||||
|
|
||||||
<com.frank.living.widget.IjkVideoView |
|
||||||
android:id="@+id/video_view2" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_above="@+id/divider1" |
|
||||||
android:layout_toEndOf="@id/divider2"/> |
|
||||||
|
|
||||||
<com.frank.living.widget.IjkVideoView |
|
||||||
android:id="@+id/video_view3" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_below="@+id/divider1" |
|
||||||
android:layout_toStartOf="@id/divider2"/> |
|
||||||
|
|
||||||
<com.frank.living.widget.IjkVideoView |
|
||||||
android:id="@+id/video_view4" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:layout_below="@+id/divider1" |
|
||||||
android:layout_toEndOf="@id/divider2"/> |
|
||||||
|
|
||||||
</RelativeLayout> |
|
@ -1,13 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="fill_parent" > |
|
||||||
|
|
||||||
<TableLayout |
|
||||||
android:id="@+id/table" |
|
||||||
android:layout_width="fill_parent" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:shrinkColumns="1" > |
|
||||||
</TableLayout> |
|
||||||
|
|
||||||
</ScrollView> |
|
@ -1,21 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:layout_marginLeft="10dip" |
|
||||||
android:paddingBottom="2dip" |
|
||||||
android:paddingTop="2dip" > |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/name" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:textSize="15sp" /> |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/value" |
|
||||||
android:layout_width="fill_parent" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:paddingLeft="10dip" |
|
||||||
android:paddingRight="10dip" |
|
||||||
android:textSize="15sp" /> |
|
||||||
|
|
||||||
</TableRow> |
|
@ -1,24 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:paddingBottom="0dip" |
|
||||||
android:paddingLeft="16dip" |
|
||||||
android:paddingRight="16dip" |
|
||||||
android:paddingTop="0dip" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="wrap_content"> |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/name" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:textSize="12sp" /> |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/value" |
|
||||||
android:layout_width="fill_parent" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:paddingLeft="10dip" |
|
||||||
android:paddingRight="10dip" |
|
||||||
android:textSize="12sp" /> |
|
||||||
|
|
||||||
</TableRow> |
|
@ -1,15 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<TableRow xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:paddingBottom="2dip" |
|
||||||
android:paddingLeft="8dip" |
|
||||||
android:paddingRight="8dip" |
|
||||||
android:paddingTop="4dip" > |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/name" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:textSize="16sp" |
|
||||||
android:textStyle="bold" /> |
|
||||||
|
|
||||||
</TableRow> |
|
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 7.5 KiB |
@ -1,19 +0,0 @@ |
|||||||
<resources> |
|
||||||
|
|
||||||
<color name="black_overlay">#66000000</color> |
|
||||||
|
|
||||||
<color name="ijk_color_blue_main">@color/ijk_color_blue_500</color> |
|
||||||
<color name="ijk_color_blue_50">#E3F2FD</color> |
|
||||||
<color name="ijk_color_blue_100">#BBDEFB</color> |
|
||||||
<color name="ijk_color_blue_200">#90CAF9</color> |
|
||||||
<color name="ijk_color_blue_300">#64B5F6</color> |
|
||||||
<color name="ijk_color_blue_400">#42A5F5</color> |
|
||||||
<color name="ijk_color_blue_500">#2196F3</color> |
|
||||||
<color name="ijk_color_blue_600">#1E88E5</color> |
|
||||||
<color name="ijk_color_blue_700">#1976D2</color> |
|
||||||
<color name="ijk_color_blue_800">#1565C0</color> |
|
||||||
<color name="ijk_color_blue_900">#0D47A1</color> |
|
||||||
|
|
||||||
<color name="ijk_transparent_dark">#77000000</color> |
|
||||||
|
|
||||||
</resources> |
|
@ -1,8 +0,0 @@ |
|||||||
<resources> |
|
||||||
<!-- Default screen margins, per the Android Design guidelines. --> |
|
||||||
<dimen name="activity_horizontal_margin">16dp</dimen> |
|
||||||
<dimen name="activity_vertical_margin">16dp</dimen> |
|
||||||
|
|
||||||
<dimen name="ijk_horizontal_margin">@dimen/activity_horizontal_margin</dimen> |
|
||||||
<dimen name="ijk_vertical_margin">@dimen/activity_vertical_margin</dimen> |
|
||||||
</resources> |
|
@ -1,4 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
<item name="render_view" type="id"/> |
|
||||||
</resources> |
|
@ -1,73 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
|
|
||||||
<string name="app_name">OnLive</string> |
|
||||||
|
|
||||||
<string name="N_A">N/A</string> |
|
||||||
<string name="close">Close</string> |
|
||||||
<string name="exit">Exit</string> |
|
||||||
<string name="sample">Sample</string> |
|
||||||
<string name="recent">Recent</string> |
|
||||||
<string name="settings">Settings</string> |
|
||||||
<string name="tracks">Tracks</string> |
|
||||||
<string name="toggle_player">Player</string> |
|
||||||
<string name="toggle_render">Render</string> |
|
||||||
<string name="toggle_ratio">Scale</string> |
|
||||||
<string name="show_info">Info</string> |
|
||||||
<string name="vdec">vdec</string> |
|
||||||
<string name="fps">fps</string> |
|
||||||
<string name="v_cache">v-cache</string> |
|
||||||
<string name="a_cache">a-cache</string> |
|
||||||
<string name="load_cost">load-cost</string> |
|
||||||
<string name="seek_cost">seek_cost</string> |
|
||||||
<string name="seek_load_cost">seek_load_cost</string> |
|
||||||
<string name="tcp_speed">tcp_speed</string> |
|
||||||
<string name="bit_rate">bit_rate</string> |
|
||||||
|
|
||||||
<string name="media_information">Media Information</string> |
|
||||||
<string name="mi_player">Player</string> |
|
||||||
<string name="mi_media">Media</string> |
|
||||||
<string name="mi_profile_level">Profile level</string> |
|
||||||
<string name="mi_pixel_format">Pixel format</string> |
|
||||||
<string name="mi_resolution">Resolution</string> |
|
||||||
<string name="mi_length">Length</string> |
|
||||||
<string name="mi_stream_fmt1">Stream #%d</string> |
|
||||||
<string name="mi_type">Type</string> |
|
||||||
<string name="mi_language">Language</string> |
|
||||||
<string name="mi_codec">Codec</string> |
|
||||||
<string name="mi_frame_rate">Frame rate</string> |
|
||||||
<string name="mi_bit_rate">Bit rate</string> |
|
||||||
<string name="mi_sample_rate">Sample rate</string> |
|
||||||
<string name="mi_channels">Channels</string> |
|
||||||
<string name="mi__selected_video_track">*</string> |
|
||||||
<string name="mi__selected_audio_track">*</string> |
|
||||||
<string name="mi__selected_subtitle_track">*</string> |
|
||||||
|
|
||||||
<string name="TrackType_video">Video</string> |
|
||||||
<string name="TrackType_audio">Audio</string> |
|
||||||
<string name="TrackType_subtitle">Subtitle</string> |
|
||||||
<string name="TrackType_timedtext">Timed text</string> |
|
||||||
<string name="TrackType_metadata">Meta data</string> |
|
||||||
<string name="TrackType_unknown">Unknown</string> |
|
||||||
|
|
||||||
<string name="VideoView_error_text_invalid_progressive_playback">Invalid progressive playback</string> |
|
||||||
<string name="VideoView_error_text_unknown">Unknown</string> |
|
||||||
<string name="VideoView_error_button">OK</string> |
|
||||||
|
|
||||||
<string name="VideoView_ar_aspect_fit_parent">Aspect / Fit parent</string> |
|
||||||
<string name="VideoView_ar_aspect_fill_parent">Aspect / Fill parent</string> |
|
||||||
<string name="VideoView_ar_aspect_wrap_content">Aspect / Wrap content</string> |
|
||||||
<string name="VideoView_ar_match_parent">Free / Fill parent</string> |
|
||||||
<string name="VideoView_ar_16_9_fit_parent">16:9 / Fit parent</string> |
|
||||||
<string name="VideoView_ar_4_3_fit_parent">4:3 / Fit parent</string> |
|
||||||
|
|
||||||
<string name="VideoView_render_none">Render: None</string> |
|
||||||
<string name="VideoView_render_surface_view">Render: SurfaceView</string> |
|
||||||
<string name="VideoView_render_texture_view">Render: TextureView</string> |
|
||||||
|
|
||||||
<string name="VideoView_player_none">Player: None</string> |
|
||||||
<string name="VideoView_player_AndroidMediaPlayer">Player: AndroidMediaPlayer</string> |
|
||||||
<string name="VideoView_player_IjkMediaPlayer">Player: IjkMediaPlayer</string> |
|
||||||
<string name="VideoView_player_IjkExoMediaPlayer">Player: IjkExoMediaPlayer</string> |
|
||||||
|
|
||||||
</resources> |
|
@ -1,112 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
<!-- --> |
|
||||||
<string name="pref_title_general">General</string> |
|
||||||
|
|
||||||
<string name="pref_key_enable_background_play">pref.enable_background_play</string> |
|
||||||
<string name="pref_title_enable_background_play">Enable background play</string> |
|
||||||
<string name="pref_summary_enable_background_play">need Android 4.0+</string> |
|
||||||
|
|
||||||
<string name="pref_key_using_android_player">pref.using_android_player</string> |
|
||||||
<string name="pref_title_using_android_player">Using system player</string> |
|
||||||
<string name="pref_summary_using_android_player"></string> |
|
||||||
|
|
||||||
<string name="pref_key_player">pref.player</string> |
|
||||||
<string name="pref_title_player">Choose Player</string> |
|
||||||
<string-array name="pref_entries_player"> |
|
||||||
<item>Auto Select</item> |
|
||||||
<item>AndroidMediaPlayer</item> |
|
||||||
<item>IjkMediaPlayer</item> |
|
||||||
<item>IjkExoMediaPlayer</item> |
|
||||||
</string-array> |
|
||||||
<string-array name="pref_entry_values_player"> |
|
||||||
<item>0</item> |
|
||||||
<item>1</item> |
|
||||||
<item>2</item> |
|
||||||
<item>3</item> |
|
||||||
</string-array> |
|
||||||
<string-array name="pref_entry_summaries_player"> |
|
||||||
<item>Auto Select</item> |
|
||||||
<item>AndroidMediaPlayer</item> |
|
||||||
<item>IjkMediaPlayer</item> |
|
||||||
<item>IjkExoMediaPlayer</item> |
|
||||||
</string-array> |
|
||||||
|
|
||||||
<!-- --> |
|
||||||
<string name="pref_title_ijkplayer_video">Video: ijkplayer</string> |
|
||||||
|
|
||||||
<string name="pref_key_using_media_codec">pref.using_media_codec</string> |
|
||||||
<string name="pref_title_using_media_codec">Using MediaCodec</string> |
|
||||||
<string name="pref_summary_using_media_codec"></string> |
|
||||||
|
|
||||||
<string name="pref_key_using_media_codec_auto_rotate">pref.using_media_codec_auto_rotate</string> |
|
||||||
<string name="pref_title_using_media_codec_auto_rotate">Using MediaCodec auto rotate</string> |
|
||||||
<string name="pref_summary_using_media_codec_auto_rotate"></string> |
|
||||||
|
|
||||||
<string name="pref_key_media_codec_handle_resolution_change">pref.media_codec_handle_resolution_change</string> |
|
||||||
<string name="pref_title_media_codec_handle_resolution_change">MediaCodec handle resolution change</string> |
|
||||||
<string name="pref_summary_media_codec_handle_resolution_change"></string> |
|
||||||
|
|
||||||
<string name="pref_key_pixel_format">pref.pixel_format</string> |
|
||||||
<string name="pref_title_pixel_format">Pixel Format</string> |
|
||||||
<string-array name="pref_entries_pixel_format"> |
|
||||||
<item>Auto Select</item> |
|
||||||
<item>RGB 565</item> |
|
||||||
<item>RGB 888</item> |
|
||||||
<item>RGBX 8888</item> |
|
||||||
<item>YV12</item> |
|
||||||
<item>OpenGL ES2</item> |
|
||||||
</string-array> |
|
||||||
<string-array name="pref_entry_values_pixel_format"> |
|
||||||
<item></item> |
|
||||||
<item>fcc-rv16</item> |
|
||||||
<item>fcc-rv24</item> |
|
||||||
<item>fcc-rv32</item> |
|
||||||
<item>fcc-yv12</item> |
|
||||||
<item>fcc-_es2</item> |
|
||||||
</string-array> |
|
||||||
<string-array name="pref_entry_summaries_pixel_format"> |
|
||||||
<item>Auto Select</item> |
|
||||||
<item>RGB 565</item> |
|
||||||
<item>RGB 888</item> |
|
||||||
<item>RGBX 8888</item> |
|
||||||
<item>YV12</item> |
|
||||||
<item>OpenGL ES2</item> |
|
||||||
</string-array> |
|
||||||
|
|
||||||
<!-- --> |
|
||||||
<string name="pref_title_ijkplayer_audio">Audio: ijkplayer</string> |
|
||||||
|
|
||||||
<string name="pref_key_using_opensl_es">pref.using_opensl_es</string> |
|
||||||
<string name="pref_title_using_opensl_es">Using OpenSL ES</string> |
|
||||||
<string name="pref_summary_using_opensl_es"></string> |
|
||||||
|
|
||||||
<!-- --> |
|
||||||
<string name="pref_title_render_view">RenderView</string> |
|
||||||
|
|
||||||
<string name="pref_key_enable_no_view">pref.enable_no_view</string> |
|
||||||
<string name="pref_title_enable_no_view">Enable NoView</string> |
|
||||||
<string name="pref_summary_enable_no_view"></string> |
|
||||||
|
|
||||||
<string name="pref_key_enable_surface_view">pref.enable_surface_view</string> |
|
||||||
<string name="pref_title_enable_surface_view">Enable SurfaceView</string> |
|
||||||
<string name="pref_summary_enable_surface_view"></string> |
|
||||||
|
|
||||||
<string name="pref_key_enable_texture_view">pref.enable_texture_view</string> |
|
||||||
<string name="pref_title_enable_texture_view">Enable TextureView</string> |
|
||||||
<string name="pref_summary_enable_texture_view"></string> |
|
||||||
|
|
||||||
<string name="pref_key_enable_detached_surface_texture">pref.enable_detached_surface_texture</string> |
|
||||||
<string name="pref_title_enable_detached_surface_texture">Enable detached SurfaceTexture</string> |
|
||||||
<string name="pref_summary_enable_detached_surface_texture"></string> |
|
||||||
|
|
||||||
<!-- --> |
|
||||||
<string name="pref_title_misc">Misc</string> |
|
||||||
<string name="pref_key_using_mediadatasource">pref.using_mediadatasource</string> |
|
||||||
<string name="pref_title_using_mediadatasource">Using MediaDataSource</string> |
|
||||||
<string name="pref_summary_using_mediadatasource"></string> |
|
||||||
|
|
||||||
<!-- --> |
|
||||||
<string name="pref_key_last_directory"></string> |
|
||||||
|
|
||||||
</resources> |
|
@ -1,19 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
<style name="Widget.App.ActionButton" parent="@style/Widget.AppCompat.ActionButton"> |
|
||||||
<item name="textAllCaps">false</item> |
|
||||||
</style> |
|
||||||
|
|
||||||
<style name="Theme.App.Light.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar"> |
|
||||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item> |
|
||||||
|
|
||||||
<item name="actionButtonStyle">@style/Widget.App.ActionButton</item> |
|
||||||
</style> |
|
||||||
|
|
||||||
<style name="Theme.App.NoActionBar" parent="Theme.AppCompat.NoActionBar"> |
|
||||||
<item name="android:windowContentOverlay">@null</item> |
|
||||||
<item name="android:windowBackground">@null</item> |
|
||||||
|
|
||||||
<item name="actionButtonStyle">@style/Widget.App.ActionButton</item> |
|
||||||
</style> |
|
||||||
</resources> |
|
@ -1,8 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<resources> |
|
||||||
<style name="AppTheme" parent="Theme.App.Light.NoActionBar"> |
|
||||||
</style> |
|
||||||
|
|
||||||
<style name="FullscreenTheme" parent="Theme.App.NoActionBar"> |
|
||||||
</style> |
|
||||||
</resources> |
|
@ -1,74 +0,0 @@ |
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"> |
|
||||||
<PreferenceCategory android:title="@string/pref_title_general"> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:disableDependentsState="true" |
|
||||||
android:key="@string/pref_key_enable_background_play" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_enable_background_play" |
|
||||||
android:title="@string/pref_title_enable_background_play" /> |
|
||||||
</PreferenceCategory> |
|
||||||
<PreferenceCategory android:title="@string/pref_title_ijkplayer_video"> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_using_media_codec" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_using_media_codec" |
|
||||||
android:title="@string/pref_title_using_media_codec" /> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_using_media_codec_auto_rotate" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_using_media_codec_auto_rotate" |
|
||||||
android:title="@string/pref_title_using_media_codec_auto_rotate" /> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_media_codec_handle_resolution_change" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_media_codec_handle_resolution_change" |
|
||||||
android:title="@string/pref_title_media_codec_handle_resolution_change" /> |
|
||||||
</PreferenceCategory> |
|
||||||
<PreferenceCategory android:title="@string/pref_title_ijkplayer_audio"> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_using_opensl_es" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_using_opensl_es" |
|
||||||
android:title="@string/pref_title_using_opensl_es" /> |
|
||||||
</PreferenceCategory> |
|
||||||
<PreferenceCategory android:title="@string/pref_title_render_view"> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_enable_no_view" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_enable_no_view" |
|
||||||
android:title="@string/pref_title_enable_no_view" /> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_enable_surface_view" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_enable_surface_view" |
|
||||||
android:title="@string/pref_title_enable_surface_view" /> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_enable_texture_view" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_enable_texture_view" |
|
||||||
android:title="@string/pref_title_enable_texture_view" /> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_enable_detached_surface_texture" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_enable_detached_surface_texture" |
|
||||||
android:title="@string/pref_title_enable_detached_surface_texture" /> |
|
||||||
</PreferenceCategory> |
|
||||||
<PreferenceCategory android:title="@string/pref_title_misc"> |
|
||||||
<CheckBoxPreference |
|
||||||
android:defaultValue="false" |
|
||||||
android:key="@string/pref_key_using_mediadatasource" |
|
||||||
android:persistent="true" |
|
||||||
android:summary="@string/pref_summary_using_mediadatasource" |
|
||||||
android:title="@string/pref_title_using_mediadatasource" /> |
|
||||||
</PreferenceCategory> |
|
||||||
</PreferenceScreen> |
|
@ -1 +1,3 @@ |
|||||||
include ':app', ':Live', ':OnLive', ':libmp3' |
include ':app' |
||||||
|
include ':Live' |
||||||
|
include ':libmp3' |
||||||
|