增加RTSP低延时直播

增加RTSP低延时直播
pull/107/head
xufulong 6 years ago
parent d47e6693ac
commit 5abd5cc1a9
  1. 2
      Live/build.gradle
  2. 52
      OnLive/build.gradle
  3. BIN
      OnLive/libs/armeabi-v7a/libijkffmpeg.so
  4. BIN
      OnLive/libs/armeabi-v7a/libijkplayer.so
  5. BIN
      OnLive/libs/armeabi-v7a/libijksdl.so
  6. 17
      OnLive/proguard-rules.pro
  7. 13
      OnLive/src/androidTest/java/tv/frank/living/media/example/ApplicationTest.java
  8. 31
      OnLive/src/main/AndroidManifest.xml
  9. 80
      OnLive/src/main/java/com/frank/living/activity/RtspLiveActivity.java
  10. 108
      OnLive/src/main/java/com/frank/living/config/Settings.java
  11. 14
      OnLive/src/main/java/com/frank/living/listener/IjkPlayerListener.java
  12. 87
      OnLive/src/main/java/com/frank/living/widget/AndroidMediaController.java
  13. 57
      OnLive/src/main/java/com/frank/living/widget/FileMediaDataSource.java
  14. 42
      OnLive/src/main/java/com/frank/living/widget/IMediaController.java
  15. 87
      OnLive/src/main/java/com/frank/living/widget/IRenderView.java
  16. 1204
      OnLive/src/main/java/com/frank/living/widget/IjkVideoView.java
  17. 164
      OnLive/src/main/java/com/frank/living/widget/InfoHudViewHolder.java
  18. 248
      OnLive/src/main/java/com/frank/living/widget/MeasureHelper.java
  19. 76
      OnLive/src/main/java/com/frank/living/widget/MediaPlayerCompat.java
  20. 286
      OnLive/src/main/java/com/frank/living/widget/SurfaceRenderView.java
  21. 131
      OnLive/src/main/java/com/frank/living/widget/TableLayoutBinder.java
  22. 368
      OnLive/src/main/java/com/frank/living/widget/TextureRenderView.java
  23. 20
      OnLive/src/main/project.properties
  24. BIN
      OnLive/src/main/res/drawable-xhdpi/ic_action_dark_aspect_ratio.png
  25. BIN
      OnLive/src/main/res/drawable-xhdpi/ic_action_dark_filter.png
  26. BIN
      OnLive/src/main/res/drawable-xhdpi/ic_action_dark_settings.png
  27. BIN
      OnLive/src/main/res/drawable-xhdpi/ic_theme_description.png
  28. BIN
      OnLive/src/main/res/drawable-xhdpi/ic_theme_folder.png
  29. BIN
      OnLive/src/main/res/drawable-xhdpi/ic_theme_play_arrow.png
  30. BIN
      OnLive/src/main/res/drawable-xxhdpi/ic_action_dark_aspect_ratio.png
  31. BIN
      OnLive/src/main/res/drawable-xxhdpi/ic_action_dark_filter.png
  32. BIN
      OnLive/src/main/res/drawable-xxhdpi/ic_action_dark_settings.png
  33. BIN
      OnLive/src/main/res/drawable-xxhdpi/ic_theme_description.png
  34. BIN
      OnLive/src/main/res/drawable-xxhdpi/ic_theme_folder.png
  35. BIN
      OnLive/src/main/res/drawable-xxhdpi/ic_theme_play_arrow.png
  36. 25
      OnLive/src/main/res/layout/activity_live.xml
  37. 13
      OnLive/src/main/res/layout/table_media_info.xml
  38. 21
      OnLive/src/main/res/layout/table_media_info_row1.xml
  39. 24
      OnLive/src/main/res/layout/table_media_info_row2.xml
  40. 15
      OnLive/src/main/res/layout/table_media_info_section.xml
  41. BIN
      OnLive/src/main/res/mipmap-xhdpi/ic_launcher.png
  42. BIN
      OnLive/src/main/res/mipmap-xxhdpi/ic_launcher.png
  43. 19
      OnLive/src/main/res/values/colors.xml
  44. 8
      OnLive/src/main/res/values/dimens.xml
  45. 4
      OnLive/src/main/res/values/ids.xml
  46. 73
      OnLive/src/main/res/values/strings.xml
  47. 112
      OnLive/src/main/res/values/strings_pref.xml
  48. 19
      OnLive/src/main/res/values/styles.xml
  49. 8
      OnLive/src/main/res/values/themes.xml
  50. 74
      OnLive/src/main/res/xml/settings.xml
  51. 2
      app/build.gradle
  52. 10
      build.gradle
  53. 2
      gradle/wrapper/gradle-wrapper.properties
  54. 2
      settings.gradle

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
android { android {
compileSdkVersion 25 compileSdkVersion 25
buildToolsVersion "25.0.2" buildToolsVersion '26.0.2'
defaultConfig { defaultConfig {
// applicationId "com.frank.live" // applicationId "com.frank.live"
minSdkVersion 15 minSdkVersion 15

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

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>

@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
android { android {
compileSdkVersion 25 compileSdkVersion 25
buildToolsVersion "25.0.2" buildToolsVersion '26.0.2'
defaultConfig { defaultConfig {
applicationId "com.frank.ffmpeg" applicationId "com.frank.ffmpeg"
minSdkVersion 17 minSdkVersion 17

@ -3,9 +3,13 @@
buildscript { buildscript {
repositories { repositories {
jcenter() jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.3.3' classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@ -15,6 +19,10 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
jcenter() jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
} }
} }

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

@ -1 +1 @@
include ':app', ':Live' include ':app', ':Live', ':OnLive'

Loading…
Cancel
Save