@ -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' |
||||||
|