@ -0,0 +1,52 @@ |
||||
apply plugin: 'com.android.application' |
||||
|
||||
android { |
||||
compileSdkVersion 25 |
||||
buildToolsVersion '26.0.2' |
||||
|
||||
lintOptions { |
||||
abortOnError false |
||||
} |
||||
defaultConfig { |
||||
applicationId "com.frank.living" |
||||
minSdkVersion 17 |
||||
targetSdkVersion 25 |
||||
versionCode 1001 |
||||
versionName "1.0.1" |
||||
flavorDimensions "800400" |
||||
|
||||
ndk { |
||||
abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a' |
||||
} |
||||
} |
||||
buildTypes { |
||||
release { |
||||
minifyEnabled false |
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
||||
} |
||||
} |
||||
productFlavors { |
||||
all32 { minSdkVersion 17 } |
||||
all64 { minSdkVersion 17 } |
||||
} |
||||
|
||||
sourceSets { |
||||
main { |
||||
jniLibs.srcDirs = ['libs'] |
||||
} |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
compile fileTree(include: ['*.jar'], dir: 'libs') |
||||
compile 'com.android.support:appcompat-v7:25.3.1' |
||||
compile 'com.android.support:preference-v7:25.4.0' |
||||
compile 'com.android.support:support-annotations:26.0.1' |
||||
compile 'com.android.support:recyclerview-v7:25.3.1' |
||||
|
||||
compile 'com.squareup:otto:1.3.8' |
||||
|
||||
compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8' |
||||
compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8' |
||||
|
||||
} |
@ -0,0 +1,17 @@ |
||||
# 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 *; |
||||
#} |
@ -0,0 +1,13 @@ |
||||
package tv.frank.living.media.example; |
||||
|
||||
import android.app.Application; |
||||
import android.test.ApplicationTestCase; |
||||
|
||||
/** |
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a> |
||||
*/ |
||||
public class ApplicationTest extends ApplicationTestCase<Application> { |
||||
public ApplicationTest() { |
||||
super(Application.class); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
<?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" /> |
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
</activity> |
||||
|
||||
</application> |
||||
|
||||
</manifest> |
@ -0,0 +1,80 @@ |
||||
package com.frank.living.activity; |
||||
|
||||
import android.os.Bundle; |
||||
import android.support.v7.app.AppCompatActivity; |
||||
import android.util.Log; |
||||
import android.widget.TableLayout; |
||||
import com.frank.living.R; |
||||
import com.frank.living.listener.IjkPlayerListener; |
||||
import tv.danmaku.ijk.media.player.IjkMediaPlayer; |
||||
import com.frank.living.widget.IjkVideoView; |
||||
|
||||
public class RtspLiveActivity extends AppCompatActivity implements IjkPlayerListener{ |
||||
|
||||
private final static String TAG = RtspLiveActivity.class.getSimpleName(); |
||||
|
||||
private IjkMediaPlayer ijkMediaPlayer; |
||||
private IjkVideoView mVideoView; |
||||
|
||||
private final static String url = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"; |
||||
|
||||
@Override |
||||
protected void onCreate(Bundle savedInstanceState) { |
||||
super.onCreate(savedInstanceState); |
||||
setContentView(R.layout.activity_live); |
||||
|
||||
init(); |
||||
|
||||
} |
||||
|
||||
private void init(){ |
||||
IjkMediaPlayer.loadLibrariesOnce(null); |
||||
IjkMediaPlayer.native_profileBegin("libijkplayer.so"); |
||||
|
||||
TableLayout mHudView = (TableLayout) findViewById(R.id.hud_view); |
||||
mVideoView = (IjkVideoView) findViewById(R.id.video_view); |
||||
mVideoView.setHudView(mHudView); |
||||
mVideoView.setIjkPlayerListener(this); |
||||
mVideoView.setVideoPath(url); |
||||
mVideoView.start(); |
||||
} |
||||
|
||||
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:代表关闭 1:代表开启
|
||||
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 |
||||
protected void onStop() { |
||||
super.onStop(); |
||||
|
||||
mVideoView.stopPlayback(); |
||||
mVideoView.release(true); |
||||
IjkMediaPlayer.native_profileEnd(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,108 @@ |
||||
/* |
||||
* 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, false); |
||||
} |
||||
|
||||
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(); |
||||
} |
||||
} |
@ -0,0 +1,14 @@ |
||||
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); |
||||
|
||||
} |
@ -0,0 +1,87 @@ |
||||
/* |
||||
* 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 android.support.annotation.NonNull; |
||||
import android.support.annotation.Nullable; |
||||
import android.support.v7.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(); |
||||
} |
||||
} |
@ -0,0 +1,57 @@ |
||||
/* |
||||
* 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; |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
/* |
||||
* 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); |
||||
} |
@ -0,0 +1,87 @@ |
||||
/* |
||||
* 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 android.support.annotation.NonNull; |
||||
import android.support.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); |
||||
} |
||||
} |
@ -0,0 +1,164 @@ |
||||
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); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
} |
@ -0,0 +1,248 @@ |
||||
/* |
||||
* 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 android.support.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; |
||||
} |
||||
} |
@ -0,0 +1,76 @@ |
||||
/* |
||||
* 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); |
||||
} |
||||
} |
@ -0,0 +1,286 @@ |
||||
/* |
||||
* 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 android.support.annotation.NonNull; |
||||
import android.support.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()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,131 @@ |
||||
/* |
||||
* 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 android.support.v7.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); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,368 @@ |
||||
/* |
||||
* 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 android.support.annotation.NonNull; |
||||
import android.support.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()); |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
# 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 |
After Width: | Height: | Size: 525 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 942 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,25 @@ |
||||
<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> |
||||
|
||||
<TableLayout |
||||
android:id="@+id/hud_view" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:layout_gravity="right|center_vertical" |
||||
android:background="@color/ijk_transparent_dark" |
||||
android:padding="8dp" |
||||
android:visibility="gone"/> |
||||
|
||||
</RelativeLayout> |
@ -0,0 +1,13 @@ |
||||
<?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> |
@ -0,0 +1,21 @@ |
||||
<?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> |
@ -0,0 +1,24 @@ |
||||
<?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> |
@ -0,0 +1,15 @@ |
||||
<?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> |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 7.5 KiB |
@ -0,0 +1,19 @@ |
||||
<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> |
@ -0,0 +1,8 @@ |
||||
<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> |
@ -0,0 +1,4 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<item name="render_view" type="id"/> |
||||
</resources> |
@ -0,0 +1,73 @@ |
||||
<?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> |
@ -0,0 +1,112 @@ |
||||
<?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> |
@ -0,0 +1,19 @@ |
||||
<?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> |
@ -0,0 +1,8 @@ |
||||
<?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> |
@ -0,0 +1,74 @@ |
||||
<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 @@ |
||||
include ':app', ':Live' |
||||
include ':app', ':Live', ':OnLive' |
||||
|