Commit 9e10c7aa5fabbf623b092facdf6300f0a2afa800

Authored by 王琳
0 parents

新建推流SDKdemo

Showing 96 changed files with 3203 additions and 0 deletions
.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +*.iml
  2 +.gradle
  3 +/local.properties
  4 +/.idea/workspace.xml
  5 +/.idea/libraries
  6 +.DS_Store
  7 +/build
  8 +/captures
  9 +.externalNativeBuild
... ...
.idea/compiler.xml 0 → 100644
  1 +++ a/.idea/compiler.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="CompilerConfiguration">
  4 + <resourceExtensions />
  5 + <wildcardResourcePatterns>
  6 + <entry name="!?*.java" />
  7 + <entry name="!?*.form" />
  8 + <entry name="!?*.class" />
  9 + <entry name="!?*.groovy" />
  10 + <entry name="!?*.scala" />
  11 + <entry name="!?*.flex" />
  12 + <entry name="!?*.kt" />
  13 + <entry name="!?*.clj" />
  14 + <entry name="!?*.aj" />
  15 + </wildcardResourcePatterns>
  16 + <annotationProcessing>
  17 + <profile default="true" name="Default" enabled="false">
  18 + <processorPath useClasspath="true" />
  19 + </profile>
  20 + </annotationProcessing>
  21 + </component>
  22 +</project>
0 23 \ No newline at end of file
... ...
.idea/copyright/profiles_settings.xml 0 → 100644
  1 +++ a/.idea/copyright/profiles_settings.xml
  1 +<component name="CopyrightManager">
  2 + <settings default="" />
  3 +</component>
0 4 \ No newline at end of file
... ...
.idea/gradle.xml 0 → 100644
  1 +++ a/.idea/gradle.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="GradleSettings">
  4 + <option name="linkedExternalProjectsSettings">
  5 + <GradleProjectSettings>
  6 + <option name="distributionType" value="LOCAL" />
  7 + <option name="externalProjectPath" value="$PROJECT_DIR$" />
  8 + <option name="gradleHome" value="C:\Program Files\Android\Android Studio\gradle\gradle-2.14.1" />
  9 + <option name="modules">
  10 + <set>
  11 + <option value="$PROJECT_DIR$" />
  12 + <option value="$PROJECT_DIR$/app" />
  13 + </set>
  14 + </option>
  15 + <option name="resolveModulePerSourceSet" value="false" />
  16 + </GradleProjectSettings>
  17 + </option>
  18 + </component>
  19 +</project>
0 20 \ No newline at end of file
... ...
.idea/misc.xml 0 → 100644
  1 +++ a/.idea/misc.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="EntryPointsManager">
  4 + <entry_points version="2.0" />
  5 + </component>
  6 + <component name="NullableNotNullManager">
  7 + <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
  8 + <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
  9 + <option name="myNullables">
  10 + <value>
  11 + <list size="4">
  12 + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
  13 + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
  14 + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
  15 + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
  16 + </list>
  17 + </value>
  18 + </option>
  19 + <option name="myNotNulls">
  20 + <value>
  21 + <list size="4">
  22 + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
  23 + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
  24 + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
  25 + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
  26 + </list>
  27 + </value>
  28 + </option>
  29 + </component>
  30 + <component name="ProjectLevelVcsManager" settingsEditedManually="false">
  31 + <OptionsSetting value="true" id="Add" />
  32 + <OptionsSetting value="true" id="Remove" />
  33 + <OptionsSetting value="true" id="Checkout" />
  34 + <OptionsSetting value="true" id="Update" />
  35 + <OptionsSetting value="true" id="Status" />
  36 + <OptionsSetting value="true" id="Edit" />
  37 + <ConfirmationsSetting value="0" id="Add" />
  38 + <ConfirmationsSetting value="0" id="Remove" />
  39 + </component>
  40 + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
  41 + <output url="file://$PROJECT_DIR$/build/classes" />
  42 + </component>
  43 + <component name="ProjectType">
  44 + <option name="id" value="Android" />
  45 + </component>
  46 +</project>
0 47 \ No newline at end of file
... ...
.idea/modules.xml 0 → 100644
  1 +++ a/.idea/modules.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="ProjectModuleManager">
  4 + <modules>
  5 + <module fileurl="file://$PROJECT_DIR$/QNStreamerDemo.iml" filepath="$PROJECT_DIR$/QNStreamerDemo.iml" />
  6 + <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
  7 + </modules>
  8 + </component>
  9 +</project>
0 10 \ No newline at end of file
... ...
.idea/runConfigurations.xml 0 → 100644
  1 +++ a/.idea/runConfigurations.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="RunConfigurationProducerService">
  4 + <option name="ignoredProducers">
  5 + <set>
  6 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
  7 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
  8 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
  9 + </set>
  10 + </option>
  11 + </component>
  12 +</project>
0 13 \ No newline at end of file
... ...
.idea/vcs.xml 0 → 100644
  1 +++ a/.idea/vcs.xml
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="VcsDirectoryMappings">
  4 + <mapping directory="$PROJECT_DIR$" vcs="Git" />
  5 + </component>
  6 +</project>
0 7 \ No newline at end of file
... ...
app/.gitignore 0 → 100644
  1 +++ a/app/.gitignore
  1 +/build
... ...
app/build.gradle 0 → 100644
  1 +++ a/app/build.gradle
  1 +apply plugin: 'com.android.application'
  2 +
  3 +android {
  4 + compileSdkVersion 24
  5 + buildToolsVersion "24.0.3"
  6 + defaultConfig {
  7 + applicationId "com.cnlive.demo.stream"
  8 + minSdkVersion 16
  9 + targetSdkVersion 24
  10 + versionCode 1
  11 + versionName "1.0"
  12 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  13 + }
  14 + buildTypes {
  15 + release {
  16 + minifyEnabled false
  17 + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  18 + }
  19 + }
  20 +}
  21 +
  22 +dependencies {
  23 + compile fileTree(include: ['*.jar'], dir: 'libs')
  24 + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
  25 + exclude group: 'com.android.support', module: 'support-annotations'
  26 + })
  27 + compile 'com.android.support:appcompat-v7:24.2.1'
  28 + testCompile 'junit:junit:4.12'
  29 + compile files('libs/libcnliveutilplus.jar')
  30 +}
... ...
app/libs/libcnlivestream.jar 0 → 100644
No preview for this file type
app/libs/libcnliveutilplus.jar 0 → 100644
No preview for this file type
app/proguard-rules.pro 0 → 100644
  1 +++ a/app/proguard-rules.pro
  1 +# Add project specific ProGuard rules here.
  2 +# By default, the flags in this file are appended to flags specified
  3 +# in D:\android_sdk/tools/proguard/proguard-android.txt
  4 +# You can edit the include path and order by changing the proguardFiles
  5 +# directive in build.gradle.
  6 +#
  7 +# For more details, see
  8 +# http://developer.android.com/guide/developing/tools/proguard.html
  9 +
  10 +# Add any project specific keep options here:
  11 +
  12 +# If your project uses WebView with JS, uncomment the following
  13 +# and specify the fully qualified class name to the JavaScript interface
  14 +# class:
  15 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  16 +# public *;
  17 +#}
... ...
app/src/androidTest/java/com/cnlive/demo/stream/ExampleInstrumentedTest.java 0 → 100644
  1 +++ a/app/src/androidTest/java/com/cnlive/demo/stream/ExampleInstrumentedTest.java
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import android.content.Context;
  4 +import android.support.test.InstrumentationRegistry;
  5 +import android.support.test.runner.AndroidJUnit4;
  6 +
  7 +import org.junit.Test;
  8 +import org.junit.runner.RunWith;
  9 +
  10 +import static org.junit.Assert.*;
  11 +
  12 +/**
  13 + * Instrumentation test, which will execute on an Android device.
  14 + *
  15 + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
  16 + */
  17 +@RunWith(AndroidJUnit4.class)
  18 +public class ExampleInstrumentedTest {
  19 + @Test
  20 + public void useAppContext() throws Exception {
  21 + // Context of the app under test.
  22 + Context appContext = InstrumentationRegistry.getTargetContext();
  23 +
  24 + assertEquals("com.cnlive.demo.stream", appContext.getPackageName());
  25 + }
  26 +}
... ...
app/src/main/AndroidManifest.xml 0 → 100644
  1 +++ a/app/src/main/AndroidManifest.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3 + package="com.cnlive.demo.stream">
  4 +
  5 + <uses-permission android:name="android.permission.INTERNET" />
  6 + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  7 + <uses-permission android:name="android.permission.RECORD_AUDIO" />
  8 + <uses-permission android:name="android.permission.CAMERA" />
  9 + <uses-permission android:name="android.permission.WAKE_LOCK" />
  10 + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  11 + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
  12 +
  13 + <uses-feature android:name="android.hardware.camera.autofocus" />
  14 + <uses-feature android:glEsVersion="0x00020000" android:required="true" />
  15 +
  16 + <application
  17 + android:name=".CNStreamApplication"
  18 + android:allowBackup="true"
  19 + android:icon="@mipmap/ic_launcher"
  20 + android:label="@string/app_name"
  21 + android:supportsRtl="true"
  22 + android:theme="@style/AppTheme">
  23 + <activity android:name=".MainActivity">
  24 + <intent-filter>
  25 + <action android:name="android.intent.action.MAIN" />
  26 +
  27 + <category android:name="android.intent.category.LAUNCHER" />
  28 + </intent-filter>
  29 + </activity>
  30 + <activity android:name=".BaseStreamActivity"
  31 + android:configChanges="orientation|screenSize"></activity>
  32 + </application>
  33 +
  34 +</manifest>
0 35 \ No newline at end of file
... ...
app/src/main/java/com/cnlive/demo/stream/BaseStreamActivity.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/BaseStreamActivity.java
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import android.app.Activity;
  4 +import android.app.AlertDialog;
  5 +import android.content.DialogInterface;
  6 +import android.content.Intent;
  7 +import android.content.pm.ActivityInfo;
  8 +import android.graphics.Bitmap;
  9 +import android.hardware.Camera;
  10 +import android.os.Build;
  11 +import android.os.Bundle;
  12 +import android.os.Environment;
  13 +import android.os.Handler;
  14 +import android.util.Log;
  15 +import android.view.KeyEvent;
  16 +import android.view.MotionEvent;
  17 +import android.view.View;
  18 +import android.view.Window;
  19 +import android.view.WindowManager;
  20 +import android.widget.Button;
  21 +import android.widget.ImageView;
  22 +import android.widget.SeekBar;
  23 +import android.widget.Toast;
  24 +
  25 +import com.cnlive.demo.stream.gles.FBO;
  26 +import com.cnlive.demo.stream.ui.CameraPreviewFrameView;
  27 +import com.cnlive.demo.stream.ui.RotateLayout;
  28 +
  29 +import com.cnlive.libs.lib_cnstream.CNCameraSetting;
  30 +import com.cnlive.libs.lib_cnstream.CNMicrophoneSetting;
  31 +import com.cnlive.libs.lib_cnstream.CNStreamManager;
  32 +import com.cnlive.libs.lib_cnstream.CNStreamProfile;
  33 +import com.cnlive.libs.lib_cnstream.CNWatermarkSetting;
  34 +import com.cnlive.libs.lib_cnstream.base.CNAspectFrameLayout;
  35 +import com.cnlive.libs.lib_cnstream.base.CNPLFourCC;
  36 +import com.cnlive.libs.lib_cnstream.base.EncodeType;
  37 +import com.cnlive.libs.lib_cnstream.base.ICNCameraSetting;
  38 +import com.cnlive.libs.lib_cnstream.base.ICNStreamManager;
  39 +import com.cnlive.libs.lib_cnstream.base.ICNStreamProfile;
  40 +import com.cnlive.libs.lib_cnstream.base.ICNWatermarkSetting;
  41 +import com.cnlive.libs.lib_cnstream.callback.CNAudioSourceCallback;
  42 +import com.cnlive.libs.lib_cnstream.callback.CNFrameCapturedCallback;
  43 +import com.cnlive.libs.lib_cnstream.callback.CNStreamStatusCallback;
  44 +import com.cnlive.libs.lib_cnstream.callback.CNStreamingPreviewCallback;
  45 +import com.cnlive.libs.lib_cnstream.callback.CNStreamingSessionListener;
  46 +import com.cnlive.libs.lib_cnstream.callback.CNStreamingStateChangedListener;
  47 +import com.cnlive.libs.lib_cnstream.callback.CNSurfaceTextureCallback;
  48 +import com.cnlive.libs.lib_cnstream.model.ActivityConfig;
  49 +import com.cnlive.libs.lib_util.dns.CNAndroidDnsServer;
  50 +import com.cnlive.libs.lib_util.dns.CNDnsManager;
  51 +import com.cnlive.libs.lib_util.dns.CNDnspodFree;
  52 +import com.cnlive.libs.lib_util.dns.CNIResolver;
  53 +import com.cnlive.libs.lib_util.dns.CNNetworkInfo;
  54 +import com.cnlive.libs.lib_util.dns.CNResolver;
  55 +import com.qiniu.pili.droid.streaming.StreamingEnv;
  56 +
  57 +import java.io.BufferedOutputStream;
  58 +import java.io.File;
  59 +import java.io.FileOutputStream;
  60 +import java.io.IOException;
  61 +import java.net.InetAddress;
  62 +import java.nio.ByteBuffer;
  63 +import java.util.List;
  64 +
  65 +import static android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK;
  66 +
  67 +/**
  68 + * Created by Administrator on 2016/11/16.
  69 + */
  70 +
  71 +public class BaseStreamActivity extends Activity implements View.OnClickListener,
  72 + View.OnLayoutChangeListener,
  73 + CNStreamStatusCallback,
  74 + CNStreamingPreviewCallback,
  75 + CNSurfaceTextureCallback,
  76 + CNAudioSourceCallback,
  77 + CameraPreviewFrameView.Listener,
  78 + CNStreamingSessionListener,
  79 + CNStreamingStateChangedListener,
  80 + ICNStreamManager.OnInfoListener,
  81 + ICNStreamManager.OnErrorListener {
  82 + private static final String TAG = "StreamingBaseActivity";
  83 +
  84 + private String activityId;
  85 + private String channelId;
  86 +
  87 + private ImageView flashBtn;
  88 + private ImageView flipBtn;
  89 + private ImageView closeBtn;
  90 + private ImageView beautyBtn;
  91 + private SeekBar beautyLevel;
  92 +
  93 + private Button orientationBtn;
  94 + protected Button startStreamBtn;
  95 + private Button muteBtn;
  96 + private Button captureBtn;
  97 + private CameraPreviewFrameView cameraPreviewFrameView;
  98 +
  99 + private Screenshooter screenshooter = new Screenshooter();
  100 + private boolean isFlashOpened = false;
  101 + private boolean mIsNeedFB = false;
  102 + private boolean mIsNeedMute = false;
  103 + protected boolean mIsRecording = false;
  104 +
  105 + private RotateLayout mRotateLayout;
  106 + private boolean isEncOrientationPort = true;
  107 + protected String mLogContent = "\n";
  108 +
  109 + protected CNStreamManager mMediaManager;
  110 + protected CNCameraSetting mCameraSetting;
  111 + protected CNMicrophoneSetting mMicrophoneSetting;
  112 + protected CNStreamProfile mProfile;
  113 + private CNWatermarkSetting watermarksetting;
  114 + private CNAspectFrameLayout afl;
  115 +
  116 + private int orientation = 1;
  117 +
  118 +
  119 + private boolean mOrientationChanged = false;
  120 +
  121 + protected boolean mIsReady = false;
  122 +
  123 + private int mCurrentZoom = 0;
  124 + private int mMaxZoom = 0;
  125 +
  126 + private FBO mFBO = new FBO();
  127 +
  128 + @Override
  129 + protected void onCreate(Bundle savedInstanceState) {
  130 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  131 + requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
  132 + } else {
  133 + requestWindowFeature(Window.FEATURE_NO_TITLE);
  134 + }
  135 +
  136 + super.onCreate(savedInstanceState);
  137 + StreamingEnv.init(getApplicationContext());
  138 +
  139 + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  140 +
  141 + if (Config.SCREEN_ORIENTATION == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
  142 + isEncOrientationPort = true;
  143 + } else if (Config.SCREEN_ORIENTATION == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
  144 + isEncOrientationPort = false;
  145 + }
  146 + setRequestedOrientation(Config.SCREEN_ORIENTATION);
  147 + setContentView(R.layout.activity_stream);
  148 +
  149 + CNStreamProfile.AudioProfile aProfile = new CNStreamProfile.AudioProfile(44100, 96 * 1024);
  150 + CNStreamProfile.VideoProfile vProfile = new CNStreamProfile.VideoProfile(30, 1000 * 1024, 48);
  151 + CNStreamProfile.AVProfile avProfile = new CNStreamProfile.AVProfile(vProfile, aProfile);
  152 +
  153 + initStreamProfile();
  154 +
  155 + initCameraSetting();
  156 + mIsNeedFB = true;
  157 + initWatermark();
  158 +
  159 + initMicrophoneSetting();
  160 + initView();
  161 + initData();
  162 + setUpMediaManager();
  163 + setFocusAreaIndicator();
  164 + }
  165 +
  166 + //推流参数配置
  167 + private void initStreamProfile() {
  168 + mProfile = new CNStreamProfile();
  169 + mProfile.setVideoQuality(ICNStreamProfile.VIDEO_QUALITY_MEDIUM2);
  170 + mProfile.setAudioQuality(ICNStreamProfile.AUDIO_QUALITY_MEDIUM2);
  171 + mProfile.setEncodingSizeLevel(ICNStreamProfile.VIDEO_ENCODING_HEIGHT_480);
  172 + mProfile.setEncoderRCMode(ICNStreamProfile.BITRATE_PRIORITY);
  173 + mProfile.setDnsManager(getMyDnsManager());
  174 + mProfile.setAdaptiveBitrateEnable(true);
  175 + mProfile.setFpsControllerEnable(true);
  176 + mProfile.setStreamStatusConfig(new CNStreamProfile.StreamStatusConfig(3));
  177 + mProfile.setSendingBufferProfile(new CNStreamProfile.SendingBufferProfile(0.2f, 0.8f, 3.0f, 20 * 1000));
  178 + }
  179 +
  180 + //摄像头参数配置
  181 + private void initCameraSetting() {
  182 + mCameraSetting = new CNCameraSetting();
  183 + mCameraSetting.setCameraId(CAMERA_FACING_BACK);
  184 + mCameraSetting.setContinuousFocusModeEnabled(true);
  185 + mCameraSetting.setRecordingHint(false);
  186 + mCameraSetting.setCameraFacingId(ICNCameraSetting.CAMERA_FACING_FRONT);
  187 + mCameraSetting.setBuiltInFaceBeautyEnabled(true);
  188 + mCameraSetting.setResetTouchFocusDelayInMs(3000);
  189 + mCameraSetting.setCameraPrvSizeLevel(ICNCameraSetting.SMALL);
  190 + mCameraSetting.setCameraPrvSizeRatio(ICNCameraSetting.RATIO_16_9);
  191 + mCameraSetting.setFaceBeautySetting(new CNCameraSetting.FaceBeautySetting(1.0f, 1.0f, 0.8f));
  192 + }
  193 +
  194 + //麦克风参数配置
  195 + private void initMicrophoneSetting() {
  196 + mMicrophoneSetting = new CNMicrophoneSetting();
  197 + mMicrophoneSetting.setBluetoothSCOEnabled(false);
  198 + }
  199 +
  200 + //水印参数配置
  201 + private void initWatermark() {
  202 + watermarksetting = new CNWatermarkSetting(this);
  203 + watermarksetting.setResourceId(R.drawable.cnlive_logo);
  204 + watermarksetting.setAlpha(100);
  205 + watermarksetting.setSize(ICNWatermarkSetting.SMALL);
  206 + watermarksetting.setCustomPosition(0.5f, 0.5f);
  207 + }
  208 +
  209 + //初始化推流所需参数
  210 + private void initData() {
  211 + Intent intent = getIntent();
  212 + activityId = intent.getStringExtra("activityId");
  213 + channelId = intent.getStringExtra("channelId");
  214 + }
  215 +
  216 + //配置推流核心类
  217 + private void setUpMediaManager() {
  218 + mMediaManager = new CNStreamManager(this, afl, cameraPreviewFrameView, EncodeType.SW_VIDEO_WITH_SW_AUDIO_CODEC); // sw codec
  219 +
  220 + mMediaManager.prepare(mCameraSetting, mMicrophoneSetting, watermarksetting, mProfile);
  221 +
  222 + mMediaManager.setStreamingStateListener(this);
  223 + mMediaManager.setSurfaceTextureCallback(this);
  224 + mMediaManager.setStreamingSessionListener(this);
  225 + mMediaManager.setStreamStatusCallback(this);
  226 + mMediaManager.setStreamingPreviewCallback(this);
  227 + mMediaManager.setAudioSourceCallback(this);
  228 + mMediaManager.setOnErrorListener(this);
  229 + mMediaManager.setOnInfoListener(this);
  230 + }
  231 +
  232 + @Override
  233 + public void onResume() {
  234 + super.onResume();
  235 + mMediaManager.resume();
  236 + }
  237 +
  238 + public void onPause() {
  239 + super.onPause();
  240 + mIsReady = false;
  241 + mMediaManager.pause();
  242 + }
  243 +
  244 + @Override
  245 + public void onDestroy() {
  246 + super.onDestroy();
  247 + mMediaManager.destroy();
  248 + }
  249 +
  250 + //开始推流
  251 + protected void startStreaming() {
  252 + mMediaManager.startStream(activityConfig);
  253 + startStreamBtn.setText("结束推流");
  254 + startStreamBtn.postInvalidate();
  255 + mIsRecording = true;
  256 + }
  257 +
  258 + //结束推流
  259 + protected void stopStreaming() {
  260 + mMediaManager.stopStream();
  261 + startStreamBtn.setText("开始推流");
  262 + startStreamBtn.postInvalidate();
  263 + mIsRecording = false;
  264 + }
  265 +
  266 + //返回按钮
  267 + protected void onBackoffClick() {
  268 + new AlertDialog.Builder(BaseStreamActivity.this).setCancelable(true)
  269 + .setTitle("结束直播?")
  270 + .setNegativeButton("取消", new DialogInterface.OnClickListener() {
  271 +
  272 + @Override
  273 + public void onClick(DialogInterface arg0, int arg1) {
  274 +
  275 + }
  276 + })
  277 + .setPositiveButton("确定", new DialogInterface.OnClickListener() {
  278 + @Override
  279 + public void onClick(DialogInterface arg0, int arg1) {
  280 + mMediaManager.stopStream();
  281 + mIsRecording = false;
  282 + new Handler().postDelayed(new Runnable() {
  283 + public void run() {
  284 + //execute the task
  285 + BaseStreamActivity.this.finish();
  286 +
  287 + }
  288 + }, 1000);
  289 + }
  290 + }).show();
  291 + }
  292 +
  293 + @Override
  294 + public boolean onKeyDown(int keyCode, KeyEvent event) {
  295 + switch (keyCode) {
  296 + case KeyEvent.KEYCODE_BACK:
  297 + onBackoffClick();
  298 + break;
  299 + default:
  300 + break;
  301 + }
  302 + return true;
  303 + }
  304 +
  305 + @Override
  306 + public boolean onRecordAudioFailedHandled(int err) {
  307 + return false;
  308 + }
  309 +
  310 + @Override
  311 + public boolean onRestartStreamingHandled(int err) {
  312 + Log.i(TAG, "onRestartStreamingHandled");
  313 + return mMediaManager.startStreaming();
  314 + }
  315 +
  316 + @Override
  317 + public Camera.Size onPreviewSizeSelected(List<Camera.Size> list) {
  318 + Camera.Size size = null;
  319 + if (list != null) {
  320 + for (Camera.Size s : list) {
  321 + if (s.height >= 480) {
  322 + size = s;
  323 + break;
  324 + }
  325 + }
  326 + }
  327 + return size;
  328 + }
  329 +
  330 + //对焦
  331 + @Override
  332 + public boolean onSingleTapUp(MotionEvent e) {
  333 + Log.i(TAG, "onSingleTapUp X:" + e.getX() + ",Y:" + e.getY());
  334 +
  335 + if (mIsReady) {
  336 + setFocusAreaIndicator();
  337 + mMediaManager.doSingleTapUp((int) e.getX(), (int) e.getY());
  338 + return true;
  339 + }
  340 + return false;
  341 + }
  342 +
  343 + //变焦
  344 + @Override
  345 + public boolean onZoomValueChanged(float factor) {
  346 + if (mIsReady && mMediaManager.isZoomSupported()) {
  347 + mCurrentZoom = (int) (mMaxZoom * factor);
  348 + mCurrentZoom = Math.min(mCurrentZoom, mMaxZoom);
  349 + mCurrentZoom = Math.max(0, mCurrentZoom);
  350 +
  351 + Log.d(TAG, "zoom ongoing, scale: " + mCurrentZoom + ",factor:" + factor + ",maxZoom:" + mMaxZoom);
  352 + mMediaManager.setZoomValue(mCurrentZoom);
  353 + }
  354 + return false;
  355 + }
  356 +
  357 + @Override
  358 + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
  359 + Log.i(TAG, "view!!!!:" + v);
  360 + }
  361 +
  362 + @Override
  363 + public boolean onPreviewFrame(byte[] bytes, int width, int height, int rotation, int fmt, long tsInNanoTime) {
  364 + Log.i(TAG, "onPreviewFrame " + width + "x" + height + ",fmt:" + (fmt == CNPLFourCC.FOURCC_I420 ? "I420" : "NV21") + ",ts:" + tsInNanoTime + ",rotation:" + rotation);
  365 + return true;
  366 + }
  367 +
  368 + @Override
  369 + public void onSurfaceCreated() {
  370 + Log.i(TAG, "onSurfaceCreated");
  371 + mFBO.initialize(this);
  372 + }
  373 +
  374 + @Override
  375 + public void onSurfaceChanged(int width, int height) {
  376 + Log.i(TAG, "onSurfaceChanged width:" + width + ",height:" + height);
  377 + mFBO.updateSurfaceSize(width, height);
  378 + }
  379 +
  380 + @Override
  381 + public void onSurfaceDestroyed() {
  382 + Log.i(TAG, "onSurfaceDestroyed");
  383 + mFBO.release();
  384 + }
  385 +
  386 + @Override
  387 + public int onDrawFrame(int texId, int texWidth, int texHeight, float[] transformMatrix) {
  388 + int newTexId = mFBO.drawFrame(texId, texWidth, texHeight);
  389 + return newTexId;
  390 + }
  391 +
  392 + @Override
  393 + public void onAudioSourceAvailable(ByteBuffer byteBuffer, int size, long tsInNanoTime, boolean eof) {
  394 + }
  395 +
  396 + @Override
  397 + public void notifyStreamStatusChanged(final CNStreamProfile.StreamStatus streamStatus) {
  398 + }
  399 +
  400 + //推流过程中的状态改变监听
  401 + @Override
  402 + public void onStateChanged(com.cnlive.libs.lib_cnstream.base.StreamingState streamingState, Object extra) {
  403 + Log.i(TAG, "StreamingState streamingState:" + streamingState + ",extra:" + extra);
  404 +
  405 + switch (streamingState) {
  406 + case PREPARING:
  407 + break;
  408 + case READY:
  409 + mIsReady = true;
  410 + mMaxZoom = mMediaManager.getMaxZoom();
  411 + break;
  412 + case CONNECTING:
  413 + break;
  414 + case STREAMING:
  415 + break;
  416 + case SHUTDOWN:
  417 + break;
  418 + case IOERROR:
  419 + break;
  420 + case UNKNOWN:
  421 + break;
  422 + case SENDING_BUFFER_EMPTY:
  423 + break;
  424 + case SENDING_BUFFER_FULL:
  425 + break;
  426 + case AUDIO_RECORDING_FAIL:
  427 + break;
  428 + case OPEN_CAMERA_FAIL:
  429 + Log.e(TAG, "Open Camera Fail. id:" + extra);
  430 + break;
  431 + case DISCONNECTED:
  432 + mLogContent += "DISCONNECTED\n";
  433 + break;
  434 + case INVALID_STREAMING_URL:
  435 + Log.e(TAG, "Invalid streaming url:" + extra);
  436 + break;
  437 + case UNAUTHORIZED_STREAMING_URL:
  438 + Log.e(TAG, "Unauthorized streaming url:" + extra);
  439 + mLogContent += "Unauthorized Url\n";
  440 + break;
  441 + case TORCH_INFO:
  442 + if (extra != null) {
  443 + final boolean isSupportedTorch = (Boolean) extra;
  444 + Log.i(TAG, "isSupportedTorch=" + isSupportedTorch);
  445 + }
  446 + break;
  447 + }
  448 + Log.e(TAG, streamingState.toString());
  449 + }
  450 +
  451 + //初始化view
  452 + private void initView() {
  453 + afl = (CNAspectFrameLayout) findViewById(R.id.cameraPreview_afl);
  454 + afl.setShowMode(CNAspectFrameLayout.SHOW_MODE.FULL);
  455 + cameraPreviewFrameView = (CameraPreviewFrameView) findViewById(R.id.cameraPreview_surfaceView);
  456 + cameraPreviewFrameView.setListener(this);
  457 + flashBtn = (ImageView) findViewById(R.id.flashBtn);
  458 + flashBtn.setOnClickListener(this);
  459 + flipBtn = (ImageView) findViewById(R.id.flipBtn);
  460 + flipBtn.setOnClickListener(this);
  461 + closeBtn = (ImageView) findViewById(R.id.closeBtn);
  462 + closeBtn.setOnClickListener(this);
  463 + beautyBtn = (ImageView) findViewById(R.id.beautyBtn);
  464 + beautyBtn.setOnClickListener(this);
  465 +
  466 + orientationBtn = (Button) findViewById(R.id.orientationBtn);
  467 + orientationBtn.setOnClickListener(this);
  468 + startStreamBtn = (Button) findViewById(R.id.startStreamBtn);
  469 + startStreamBtn.setOnClickListener(this);
  470 + muteBtn = (Button) findViewById(R.id.muteBtn);
  471 + muteBtn.setOnClickListener(this);
  472 + captureBtn = (Button) findViewById(R.id.captureBtn);
  473 + captureBtn.setOnClickListener(this);
  474 +
  475 + beautyLevel = (SeekBar) findViewById(R.id.beautyLevel_seekBar);
  476 + beautyLevel.setOnSeekBarChangeListener(beautyLevelListener);
  477 + }
  478 +
  479 + @Override
  480 + public void onClick(View view) {
  481 + switch (view.getId()) {
  482 + case R.id.flashBtn:
  483 + if (isFlashOpened) {
  484 + mMediaManager.turnLightOff();
  485 + isFlashOpened = false;
  486 + } else {
  487 + mMediaManager.turnLightOn();
  488 + isFlashOpened = true;
  489 + }
  490 + break;
  491 + case R.id.flipBtn:
  492 + mMediaManager.switchCamera();
  493 + break;
  494 + case R.id.beautyBtn:
  495 + mIsNeedFB = !mIsNeedFB;
  496 + mMediaManager.setVideoFilterType(mIsNeedFB ? ICNCameraSetting.VIDEO_FILTER_BEAUTY
  497 + : ICNCameraSetting.VIDEO_FILTER_NONE);
  498 + beautyBtn.setBackgroundResource(mIsNeedFB ? R.drawable.icon_record_meiyan_on : R.drawable.icon_record_meiyan_off);
  499 + break;
  500 + case R.id.closeBtn:
  501 + onBackoffClick();
  502 + break;
  503 + case R.id.orientationBtn:
  504 + mOrientationChanged = !mOrientationChanged;
  505 + isEncOrientationPort = !isEncOrientationPort;
  506 + mProfile.setEncodingOrientation(isEncOrientationPort ? ICNStreamProfile.PORT : ICNStreamProfile.LAND);
  507 + orientation = isEncOrientationPort ? 0 : 1;
  508 + mMediaManager.setStreamingProfile(mProfile);
  509 + setRequestedOrientation(isEncOrientationPort ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
  510 + mMediaManager.notifyActivityOrientationChanged();
  511 + orientationBtn.setText(isEncOrientationPort ? "横屏" : "竖屏");
  512 + break;
  513 + case R.id.startStreamBtn:
  514 + if (mIsRecording) {
  515 + stopStreaming();
  516 + } else {
  517 + startStreaming();
  518 + }
  519 + break;
  520 + case R.id.muteBtn:
  521 + mIsNeedMute = !mIsNeedMute;
  522 + mMediaManager.mute(mIsNeedMute);
  523 + muteBtn.setText(mIsNeedMute ? "恢复" : "静音");
  524 + break;
  525 + case R.id.captureBtn:
  526 + new Handler().postDelayed(screenshooter, 100);
  527 + break;
  528 + }
  529 + }
  530 +
  531 + //美颜seekbar监听
  532 + SeekBar.OnSeekBarChangeListener beautyLevelListener = new SeekBar.OnSeekBarChangeListener() {
  533 + @Override
  534 + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  535 + CNCameraSetting.FaceBeautySetting faceBeautySetting = mCameraSetting.getFaceBeautySetting();
  536 + faceBeautySetting.beautyLevel = progress / 100.0f;
  537 + faceBeautySetting.whiten = progress / 100.0f;
  538 + faceBeautySetting.redden = progress / 100.0f;
  539 +
  540 + mMediaManager.updateFaceBeautySetting(faceBeautySetting);
  541 + }
  542 +
  543 + @Override
  544 + public void onStartTrackingTouch(SeekBar seekBar) {
  545 + }
  546 +
  547 + @Override
  548 + public void onStopTrackingTouch(SeekBar seekBar) {
  549 + }
  550 + };
  551 +
  552 + protected void setFocusAreaIndicator() {
  553 + if (mRotateLayout == null) {
  554 + mRotateLayout = (RotateLayout) findViewById(R.id.focus_indicator_rotate_layout);
  555 + mMediaManager.setFocusAreaIndicator(mRotateLayout, mRotateLayout.findViewById(R.id.focus_indicator));
  556 + }
  557 + }
  558 +
  559 + private static CNDnsManager getMyDnsManager() {
  560 + CNIResolver r0 = new CNDnspodFree();
  561 + CNIResolver r1 = CNAndroidDnsServer.defaultResolver();
  562 + CNIResolver r2 = null;
  563 + try {
  564 + r2 = new CNResolver(InetAddress.getByName("119.29.29.29"));
  565 + } catch (IOException ex) {
  566 + ex.printStackTrace();
  567 + }
  568 + return new CNDnsManager(CNNetworkInfo.normal, new CNIResolver[]{r0, r1, r2});
  569 + }
  570 +
  571 + //传入推流所需参数
  572 + ActivityConfig activityConfig = new ActivityConfig() {
  573 + @Override
  574 + public String getActivityID() {
  575 + return activityId;
  576 + }
  577 +
  578 + @Override
  579 + public String getActivityName() {
  580 + return " ";
  581 + }
  582 +
  583 + @Override
  584 + public int getActivityStatus() {
  585 + return 0;
  586 + }
  587 +
  588 + @Override
  589 + public int getIsHorizontalScreen() {
  590 + return orientation;
  591 + }
  592 +
  593 + @Override
  594 + public String getChannelID() {
  595 + return channelId;
  596 + }
  597 +
  598 + @Override
  599 + public String getStartTime() {
  600 + return " ";
  601 + }
  602 +
  603 + @Override
  604 + public String getEndTime() {
  605 + return " ";
  606 + }
  607 +
  608 + @Override
  609 + public String getCoverImgUrl() {
  610 + return " ";
  611 + }
  612 +
  613 + @Override
  614 + public String getActivityCategory() {
  615 + return " ";
  616 + }
  617 +
  618 + @Override
  619 + public String getExtensions() {
  620 + return " ";
  621 + }
  622 +
  623 + @Override
  624 + public String getUuid() {
  625 + return " ";
  626 + }
  627 +
  628 + @Override
  629 + public String getCallbackAcitvityStatushttpPostUrl() {
  630 + return " ";
  631 + }
  632 + };
  633 +
  634 + //推流过程中的错误监听
  635 + @Override
  636 + public void onError(int what, String extra) {
  637 + stopStreaming();
  638 + Log.e(TAG, what + " " + extra);
  639 + }
  640 +
  641 + //推流过程中返回的信息
  642 + @Override
  643 + public void onInfo(int what, String extra) {
  644 + Log.e(TAG, what + " " + extra);
  645 + }
  646 +
  647 + private class Screenshooter implements Runnable {
  648 + @Override
  649 + public void run() {
  650 + final String fileName = "PLStreaming_" + System.currentTimeMillis() + ".jpg";
  651 + mMediaManager.captureFrame(100, 100, new CNFrameCapturedCallback() {
  652 + private Bitmap bitmap;
  653 +
  654 + @Override
  655 + public void onFrameCaptured(Bitmap bmp) {
  656 + if (bmp == null) {
  657 + return;
  658 + }
  659 + bitmap = bmp;
  660 + new Thread(new Runnable() {
  661 + @Override
  662 + public void run() {
  663 + try {
  664 + saveToSDCard(fileName, bitmap);
  665 + } catch (IOException e) {
  666 + e.printStackTrace();
  667 + } finally {
  668 + if (bitmap != null) {
  669 + bitmap.recycle();
  670 + bitmap = null;
  671 + }
  672 + }
  673 + }
  674 + }).start();
  675 + }
  676 + });
  677 + }
  678 + }
  679 +
  680 + private void saveToSDCard(String filename, Bitmap bmp) throws IOException {
  681 + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
  682 + File file = new File(Environment.getExternalStorageDirectory(), filename);
  683 + BufferedOutputStream bos = null;
  684 + try {
  685 + bos = new BufferedOutputStream(new FileOutputStream(file));
  686 + bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
  687 + bmp.recycle();
  688 + bmp = null;
  689 + } finally {
  690 + if (bos != null) bos.close();
  691 + }
  692 +
  693 + final String info = "Save frame to:" + Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + filename;
  694 + runOnUiThread(new Runnable() {
  695 + @Override
  696 + public void run() {
  697 + Toast.makeText(BaseStreamActivity.this, info, Toast.LENGTH_LONG).show();
  698 + }
  699 + });
  700 + }
  701 + }
  702 +}
... ...
app/src/main/java/com/cnlive/demo/stream/CNStreamApplication.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/CNStreamApplication.java
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import android.app.Application;
  4 +
  5 +import com.cnlive.libs.util.Config;
  6 +
  7 +/**
  8 + * Created by Administrator on 2016/11/16.
  9 + */
  10 +
  11 +public class CNStreamApplication extends Application {
  12 + @Override
  13 + public void onCreate() {
  14 + super.onCreate();
  15 + Config.init(this, "60_irn9gh9d49", "f7b6db55616d207fd5c07e2b580b5650c690aff58fb36d");
  16 + }
  17 +}
... ...
app/src/main/java/com/cnlive/demo/stream/Config.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/Config.java
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import android.content.pm.ActivityInfo;
  4 +
  5 +/**
  6 + * Created by jerikc on 15/12/8.
  7 + */
  8 +public class Config {
  9 + public static final boolean FILTER_ENABLED = false;
  10 + public static final int SCREEN_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  11 +}
... ...
app/src/main/java/com/cnlive/demo/stream/MainActivity.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/MainActivity.java
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import android.app.Activity;
  4 +import android.content.Intent;
  5 +import android.os.Bundle;
  6 +import android.view.View;
  7 +import android.widget.Button;
  8 +import android.widget.EditText;
  9 +import android.widget.Toast;
  10 +
  11 +public class MainActivity extends Activity implements View.OnClickListener {
  12 + public static final String TAG = "MainActivity";
  13 + private Button connectBtn;
  14 + private EditText activityId;
  15 + private EditText channelId;
  16 +
  17 + @Override
  18 + protected void onCreate(Bundle savedInstanceState) {
  19 + super.onCreate(savedInstanceState);
  20 + setContentView(R.layout.activity_main);
  21 + initView();
  22 + }
  23 +
  24 + private void initView() {
  25 + connectBtn = (Button) findViewById(R.id.connectBtn);
  26 + connectBtn.setOnClickListener(this);
  27 + activityId = (EditText) findViewById(R.id.activityId);
  28 + channelId = (EditText) findViewById(R.id.channelId);
  29 + }
  30 +
  31 + @Override
  32 + public void onClick(View v) {
  33 + switch (v.getId()) {
  34 + case R.id.connectBtn:
  35 + Intent intent = new Intent(this, BaseStreamActivity.class);
  36 + intent.putExtra("activityId", activityId.getText().toString());
  37 + intent.putExtra("channelId", channelId.getText().toString());
  38 + if (activityId.getText().toString().isEmpty() || channelId.getText().toString().isEmpty()) {
  39 + Toast.makeText(this, "activityID或channelId不能为空哦", Toast.LENGTH_SHORT).show();
  40 + } else {
  41 + startActivity(intent);
  42 + }
  43 + break;
  44 + }
  45 + }
  46 +}
... ...
app/src/main/java/com/cnlive/demo/stream/filter/AbstractFilter.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/filter/AbstractFilter.java
  1 +package com.cnlive.demo.stream.filter;
  2 +
  3 +import android.content.Context;
  4 +
  5 +import java.nio.FloatBuffer;
  6 +
  7 +/**
  8 + * Created by jerikc on 16/2/23.
  9 + */
  10 +public abstract class AbstractFilter {
  11 + protected abstract int createProgram(Context context);
  12 +
  13 + protected abstract void getGLSLValues();
  14 +
  15 + protected abstract void useProgram();
  16 +
  17 + protected abstract void bindTexture(int textureId);
  18 +
  19 + protected abstract void bindGLSLValues(float[] mvpMatrix, FloatBuffer vertexBuffer,
  20 + int coordsPerVertex, int vertexStride, FloatBuffer texBuffer,
  21 + int texStride);
  22 +
  23 + protected abstract void drawArrays(int firstVertex, int vertexCount);
  24 +
  25 + protected abstract void unbindGLSLValues();
  26 +
  27 + protected abstract void unbindTexture();
  28 +
  29 + protected abstract void disuseProgram();
  30 +}
... ...
app/src/main/java/com/cnlive/demo/stream/filter/CameraFilter.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/filter/CameraFilter.java
  1 +package com.cnlive.demo.stream.filter;
  2 +
  3 +import android.content.Context;
  4 +import android.opengl.GLES11Ext;
  5 +import android.opengl.GLES20;
  6 +
  7 +import com.cnlive.demo.stream.R;
  8 +import com.cnlive.demo.stream.gles.GlUtil;
  9 +
  10 +import java.nio.FloatBuffer;
  11 +
  12 +
  13 +/**
  14 + * Created by jerikc on 16/2/23.
  15 + */
  16 +public class CameraFilter extends AbstractFilter implements IFilter {
  17 + protected int mProgramHandle;
  18 + private int maPositionLoc;
  19 + private int muMVPMatrixLoc;
  20 + private int maTextureCoordLoc;
  21 + private int mTextureLoc;
  22 +
  23 + protected int mIncomingWidth, mIncomingHeight;
  24 +
  25 + public CameraFilter(Context applicationContext) {
  26 + mProgramHandle = createProgram(applicationContext);
  27 + if (mProgramHandle == 0) {
  28 + throw new RuntimeException("Unable to create program");
  29 + }
  30 + getGLSLValues();
  31 + }
  32 +
  33 + @Override
  34 + public int getTextureTarget() {
  35 + return GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
  36 + }
  37 +
  38 + @Override
  39 + public void setTextureSize(int width, int height) {
  40 + if (width == 0 || height == 0) {
  41 + return;
  42 + }
  43 + if (width == mIncomingWidth && height == mIncomingHeight) {
  44 + return;
  45 + }
  46 + mIncomingWidth = width;
  47 + mIncomingHeight = height;
  48 + }
  49 +
  50 + @Override
  51 + protected int createProgram(Context applicationContext) {
  52 + return GlUtil.createProgram(applicationContext, R.raw.vertex_shader,
  53 + R.raw.fragment_shader_no_filter);
  54 + }
  55 +
  56 + @Override
  57 + protected void getGLSLValues() {
  58 + mTextureLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexture");
  59 + maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
  60 + muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
  61 + maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord");
  62 + }
  63 +
  64 + @Override
  65 + public void onDraw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex,
  66 + int vertexCount, int coordsPerVertex, int vertexStride,
  67 + FloatBuffer texBuffer, int textureId, int texStride) {
  68 +
  69 + GlUtil.checkGlError("draw start");
  70 +
  71 + useProgram();
  72 +
  73 + bindTexture(textureId);
  74 +
  75 + bindGLSLValues(mvpMatrix, vertexBuffer, coordsPerVertex, vertexStride, texBuffer,
  76 + texStride);
  77 +
  78 + drawArrays(firstVertex, vertexCount);
  79 +
  80 + unbindGLSLValues();
  81 +
  82 + unbindTexture();
  83 +
  84 + disuseProgram();
  85 + }
  86 +
  87 + @Override
  88 + protected void useProgram() {
  89 + GLES20.glUseProgram(mProgramHandle);
  90 + //GlUtil.checkGlError("glUseProgram");
  91 + }
  92 +
  93 + @Override
  94 + protected void bindTexture(int textureId) {
  95 + GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  96 + GLES20.glBindTexture(getTextureTarget(), textureId);
  97 + GLES20.glUniform1i(mTextureLoc, 0);
  98 + }
  99 +
  100 + @Override
  101 + protected void bindGLSLValues(float[] mvpMatrix, FloatBuffer vertexBuffer, int coordsPerVertex,
  102 + int vertexStride, FloatBuffer texBuffer, int texStride) {
  103 +
  104 + GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0);
  105 + GLES20.glEnableVertexAttribArray(maPositionLoc);
  106 + GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex, GLES20.GL_FLOAT, false,
  107 + vertexStride, vertexBuffer);
  108 + GLES20.glEnableVertexAttribArray(maTextureCoordLoc);
  109 + GLES20.glVertexAttribPointer(maTextureCoordLoc, 2, GLES20.GL_FLOAT, false, texStride,
  110 + texBuffer);
  111 + }
  112 +
  113 + @Override
  114 + protected void drawArrays(int firstVertex, int vertexCount) {
  115 + GLES20.glClearColor(0f, 0f, 0f, 1f);
  116 + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
  117 + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount);
  118 + }
  119 +
  120 + @Override
  121 + protected void unbindGLSLValues() {
  122 + GLES20.glDisableVertexAttribArray(maPositionLoc);
  123 + GLES20.glDisableVertexAttribArray(maTextureCoordLoc);
  124 + }
  125 +
  126 + @Override
  127 + protected void unbindTexture() {
  128 + GLES20.glBindTexture(getTextureTarget(), 0);
  129 + }
  130 +
  131 + @Override
  132 + protected void disuseProgram() {
  133 + GLES20.glUseProgram(0);
  134 + }
  135 +
  136 + @Override
  137 + public void releaseProgram() {
  138 + GLES20.glDeleteProgram(mProgramHandle);
  139 + mProgramHandle = -1;
  140 + }
  141 +}
... ...
app/src/main/java/com/cnlive/demo/stream/filter/CameraFilterBeauty.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/filter/CameraFilterBeauty.java
  1 +package com.cnlive.demo.stream.filter;
  2 +
  3 +import android.content.Context;
  4 +import android.opengl.GLES20;
  5 +
  6 +import com.cnlive.demo.stream.R;
  7 +import com.cnlive.demo.stream.gles.GlUtil;
  8 +
  9 +import java.nio.FloatBuffer;
  10 +
  11 +/**
  12 + * Created by shengwenhui on 16/3/3.
  13 + */
  14 +public class CameraFilterBeauty extends CameraFilter {
  15 + private int singleStepOffset;
  16 +
  17 + private static final float offset_array[] = {
  18 + 2, 2,
  19 + };
  20 +
  21 + public CameraFilterBeauty(Context context) {
  22 + super(context);
  23 + offset_array[0] = offset_array[0] / 90;
  24 + offset_array[1] = offset_array[1] / 160;
  25 + }
  26 +
  27 + @Override
  28 + protected int createProgram(Context applicationContext) {
  29 + return GlUtil.createProgram(applicationContext, R.raw.vertex_shader,
  30 + R.raw.fragment_shader_beauty);
  31 + }
  32 +
  33 + @Override
  34 + protected void getGLSLValues() {
  35 + super.getGLSLValues();
  36 +
  37 + singleStepOffset = GLES20.glGetUniformLocation(mProgramHandle, "singleStepOffset");
  38 + }
  39 +
  40 + @Override
  41 + protected void bindGLSLValues(float[] mvpMatrix, FloatBuffer vertexBuffer, int coordsPerVertex,
  42 + int vertexStride, FloatBuffer texBuffer, int texStride) {
  43 + super.bindGLSLValues(mvpMatrix, vertexBuffer, coordsPerVertex, vertexStride,
  44 + texBuffer, texStride);
  45 +
  46 + GLES20.glUniform2fv(singleStepOffset, 1, offset_array, 0);
  47 + }
  48 +}
  49 +
... ...
app/src/main/java/com/cnlive/demo/stream/filter/CameraFilterMosaic.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/filter/CameraFilterMosaic.java
  1 +package com.cnlive.demo.stream.filter;
  2 +
  3 +import android.content.Context;
  4 +import android.opengl.GLES20;
  5 +
  6 +import com.cnlive.demo.stream.R;
  7 +import com.cnlive.demo.stream.gles.GlUtil;
  8 +
  9 +import java.nio.FloatBuffer;
  10 +
  11 +/**
  12 + * Created by shengwenhui on 16/3/3.
  13 + */
  14 +public class CameraFilterMosaic extends CameraFilter {
  15 + private int muTexsizeLoc;
  16 +
  17 + private static final float Texsize_array[] = {
  18 + 255, 255,
  19 + };
  20 +
  21 + public CameraFilterMosaic(Context context) {
  22 + super(context);
  23 + }
  24 +
  25 + @Override
  26 + protected int createProgram(Context applicationContext) {
  27 + return GlUtil.createProgram(applicationContext, R.raw.vertex_shader,
  28 + R.raw.fragment_shader_mosaic);
  29 + }
  30 +
  31 + @Override
  32 + protected void getGLSLValues() {
  33 + super.getGLSLValues();
  34 +
  35 + muTexsizeLoc = GLES20.glGetUniformLocation(mProgramHandle, "TexSize");
  36 + }
  37 +
  38 + @Override
  39 + protected void bindGLSLValues(float[] mvpMatrix, FloatBuffer vertexBuffer, int coordsPerVertex,
  40 + int vertexStride, FloatBuffer texBuffer, int texStride) {
  41 + super.bindGLSLValues(mvpMatrix, vertexBuffer, coordsPerVertex, vertexStride,
  42 + texBuffer, texStride);
  43 +
  44 + GLES20.glUniform2fv(muTexsizeLoc, 1, Texsize_array, 0);
  45 + }
  46 +}
0 47 \ No newline at end of file
... ...
app/src/main/java/com/cnlive/demo/stream/filter/CameraFilterToneCurve.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/filter/CameraFilterToneCurve.java
  1 +package com.cnlive.demo.stream.filter;
  2 +
  3 +import android.content.Context;
  4 +import android.graphics.Point;
  5 +import android.graphics.PointF;
  6 +import android.opengl.GLES20;
  7 +
  8 +import com.cnlive.demo.stream.R;
  9 +import com.cnlive.demo.stream.gles.GlUtil;
  10 +
  11 +import java.io.IOException;
  12 +import java.io.InputStream;
  13 +import java.nio.ByteBuffer;
  14 +import java.nio.FloatBuffer;
  15 +import java.util.ArrayList;
  16 +import java.util.Arrays;
  17 +import java.util.Comparator;
  18 +
  19 +/**
  20 + * Created by jerikc on 16/2/23.
  21 + */
  22 +public class CameraFilterToneCurve extends CameraFilter {
  23 +
  24 + private final int mToneCurveTextureId;
  25 + protected int muToneCurveTextureLoc;
  26 +
  27 + private PointF[] mRgbCompositeControlPoints;
  28 + private PointF[] mRedControlPoints;
  29 + private PointF[] mGreenControlPoints;
  30 + private PointF[] mBlueControlPoints;
  31 +
  32 + private ArrayList<Float> mRgbCompositeCurve;
  33 + private ArrayList<Float> mRedCurve;
  34 + private ArrayList<Float> mGreenCurve;
  35 + private ArrayList<Float> mBlueCurve;
  36 +
  37 + public CameraFilterToneCurve(Context context, InputStream inputStream) {
  38 + super(context);
  39 + setFromCurveFileInputStream(inputStream);
  40 + setRgbCompositeControlPoints(mRgbCompositeControlPoints);
  41 + setRedControlPoints(mRedControlPoints);
  42 + setGreenControlPoints(mGreenControlPoints);
  43 + setBlueControlPoints(mBlueControlPoints);
  44 +
  45 + mToneCurveTextureId = GlUtil.createTexture(GLES20.GL_TEXTURE_2D);
  46 + }
  47 +
  48 + @Override
  49 + protected int createProgram(Context applicationContext) {
  50 + return GlUtil.createProgram(applicationContext, R.raw.vertex_shader,
  51 + R.raw.fragment_shader_ext_tone_curve);
  52 + }
  53 +
  54 + @Override
  55 + protected void getGLSLValues() {
  56 + super.getGLSLValues();
  57 +
  58 + muToneCurveTextureLoc = GLES20.glGetUniformLocation(mProgramHandle, "toneCurveTexture");
  59 + }
  60 +
  61 + @Override
  62 + protected void bindTexture(int textureId) {
  63 +
  64 + super.bindTexture(textureId);
  65 +
  66 + GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
  67 + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mToneCurveTextureId);
  68 + GLES20.glUniform1i(muToneCurveTextureLoc, 1);
  69 +
  70 + if ((mRedCurve.size() >= 256)
  71 + && (mGreenCurve.size() >= 256)
  72 + && (mBlueCurve.size() >= 256)
  73 + && (mRgbCompositeCurve.size() >= 256)) {
  74 + byte[] toneCurveByteArray = new byte[256 * 4];
  75 + for (int currentCurveIndex = 0; currentCurveIndex < 256; currentCurveIndex++) {
  76 + // BGRA for upload to texture
  77 + toneCurveByteArray[currentCurveIndex * 4 + 2] = (byte) ((int) Math.min(Math.max(
  78 + currentCurveIndex
  79 + + mBlueCurve.get(currentCurveIndex)
  80 + + mRgbCompositeCurve.get(currentCurveIndex), 0), 255) & 0xff);
  81 + toneCurveByteArray[currentCurveIndex * 4 + 1] = (byte) ((int) Math.min(Math.max(
  82 + currentCurveIndex
  83 + + mGreenCurve.get(currentCurveIndex)
  84 + + mRgbCompositeCurve.get(currentCurveIndex), 0), 255) & 0xff);
  85 + toneCurveByteArray[currentCurveIndex * 4] = (byte) ((int) Math.min(Math.max(
  86 + currentCurveIndex
  87 + + mRedCurve.get(currentCurveIndex)
  88 + + mRgbCompositeCurve.get(currentCurveIndex), 0), 255) & 0xff);
  89 + toneCurveByteArray[currentCurveIndex * 4 + 3] = (byte) (255 & 0xff);
  90 + }
  91 +
  92 + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256 /*width*/,
  93 + 1 /*height*/, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
  94 + ByteBuffer.wrap(toneCurveByteArray));
  95 + }
  96 + }
  97 +
  98 + @Override
  99 + protected void bindGLSLValues(float[] mvpMatrix, FloatBuffer vertexBuffer, int coordsPerVertex,
  100 + int vertexStride, FloatBuffer texBuffer, int texStride) {
  101 + super.bindGLSLValues(mvpMatrix, vertexBuffer, coordsPerVertex, vertexStride,
  102 + texBuffer, texStride);
  103 + //GLES20.glEnableVertexAttribArray(maExtraTextureCoordLoc);
  104 + //GLES20.glVertexAttribPointer(maExtraTextureCoordLoc, 2, GLES20.GL_FLOAT, false, texStride,
  105 + // texBuffer);
  106 + }
  107 +
  108 + @Override
  109 + protected void unbindGLSLValues() {
  110 + super.unbindGLSLValues();
  111 + //GLES20.glDisableVertexAttribArray(maExtraTextureCoordLoc);
  112 + }
  113 +
  114 + @Override
  115 + protected void unbindTexture() {
  116 + super.unbindTexture();
  117 + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
  118 + }
  119 +
  120 + ///////////////////////
  121 +
  122 + private short readShort(InputStream input) throws IOException {
  123 + return (short) (input.read() << 8 | input.read());
  124 + }
  125 +
  126 + private void setFromCurveFileInputStream(InputStream input) {
  127 + try {
  128 + int version = readShort(input);
  129 + int totalCurves = readShort(input);
  130 +
  131 + ArrayList<PointF[]> curves = new ArrayList<PointF[]>(totalCurves);
  132 + float pointRate = 1.0f / 255;
  133 +
  134 + for (int i = 0; i < totalCurves; i++) {
  135 + // 2 bytes, Count of points in the curve (short integer from 2...19)
  136 + short pointCount = readShort(input);
  137 + PointF[] points = new PointF[pointCount];
  138 + // point count * 4
  139 + // Curve points. Each curve point is a pair of short integers where
  140 + // the first number is the output value (vertical coordinate on the
  141 + // Curves dialog graph) and the second is the input value. All coordinates have range 0 to 255.
  142 + for (int j = 0; j < pointCount; j++) {
  143 + short y = readShort(input);
  144 + short x = readShort(input);
  145 +
  146 + points[j] = new PointF(x * pointRate, y * pointRate);
  147 + }
  148 +
  149 + curves.add(points);
  150 + }
  151 + input.close();
  152 +
  153 + mRgbCompositeControlPoints = curves.get(0);
  154 + mRedControlPoints = curves.get(1);
  155 + mGreenControlPoints = curves.get(2);
  156 + mBlueControlPoints = curves.get(3);
  157 + } catch (IOException e) {
  158 + e.printStackTrace();
  159 + }
  160 + }
  161 +
  162 + public void setRgbCompositeControlPoints(PointF[] points) {
  163 + mRgbCompositeControlPoints = points;
  164 + mRgbCompositeCurve = createSplineCurve(mRgbCompositeControlPoints);
  165 + //updateToneCurveTexture();
  166 + }
  167 +
  168 + public void setRedControlPoints(PointF[] points) {
  169 + mRedControlPoints = points;
  170 + mRedCurve = createSplineCurve(mRedControlPoints);
  171 + //updateToneCurveTexture();
  172 + }
  173 +
  174 + public void setGreenControlPoints(PointF[] points) {
  175 + mGreenControlPoints = points;
  176 + mGreenCurve = createSplineCurve(mGreenControlPoints);
  177 + //updateToneCurveTexture();
  178 + }
  179 +
  180 + public void setBlueControlPoints(PointF[] points) {
  181 + mBlueControlPoints = points;
  182 + mBlueCurve = createSplineCurve(mBlueControlPoints);
  183 + //updateToneCurveTexture();
  184 + }
  185 +
  186 + private ArrayList<Float> createSplineCurve(PointF[] points) {
  187 + if (points == null || points.length <= 0) {
  188 + return null;
  189 + }
  190 +
  191 + // Sort the array
  192 + PointF[] pointsSorted = points.clone();
  193 + Arrays.sort(pointsSorted, new Comparator<PointF>() {
  194 + @Override
  195 + public int compare(PointF point1, PointF point2) {
  196 + if (point1.x < point2.x) {
  197 + return -1;
  198 + } else if (point1.x > point2.x) {
  199 + return 1;
  200 + } else {
  201 + return 0;
  202 + }
  203 + }
  204 + });
  205 +
  206 + // Convert from (0, 1) to (0, 255).
  207 + Point[] convertedPoints = new Point[pointsSorted.length];
  208 + for (int i = 0; i < points.length; i++) {
  209 + PointF point = pointsSorted[i];
  210 + convertedPoints[i] = new Point((int) (point.x * 255), (int) (point.y * 255));
  211 + }
  212 +
  213 + ArrayList<Point> splinePoints = createSplineCurve2(convertedPoints);
  214 +
  215 + // If we have a first point like (0.3, 0) we'll be missing some points at the beginning
  216 + // that should be 0.
  217 + Point firstSplinePoint = splinePoints.get(0);
  218 + if (firstSplinePoint.x > 0) {
  219 + for (int i = firstSplinePoint.x; i >= 0; i--) {
  220 + splinePoints.add(0, new Point(i, 0));
  221 + }
  222 + }
  223 +
  224 + // Insert points similarly at the end, if necessary.
  225 + Point lastSplinePoint = splinePoints.get(splinePoints.size() - 1);
  226 + if (lastSplinePoint.x < 255) {
  227 + for (int i = lastSplinePoint.x + 1; i <= 255; i++) {
  228 + splinePoints.add(new Point(i, 255));
  229 + }
  230 + }
  231 +
  232 + // Prepare the spline points.
  233 + ArrayList<Float> preparedSplinePoints = new ArrayList<Float>(splinePoints.size());
  234 + for (Point newPoint : splinePoints) {
  235 + Point origPoint = new Point(newPoint.x, newPoint.x);
  236 +
  237 + float distance = (float) Math.sqrt(
  238 + Math.pow((origPoint.x - newPoint.x), 2.0) + Math.pow((origPoint.y - newPoint.y),
  239 + 2.0));
  240 +
  241 + if (origPoint.y > newPoint.y) {
  242 + distance = -distance;
  243 + }
  244 +
  245 + preparedSplinePoints.add(distance);
  246 + }
  247 +
  248 + return preparedSplinePoints;
  249 + }
  250 +
  251 + private ArrayList<Point> createSplineCurve2(Point[] points) {
  252 + ArrayList<Double> sdA = createSecondDerivative(points);
  253 +
  254 + // Is [points count] equal to [sdA count]?
  255 + // int n = [points count];
  256 + int n = sdA.size();
  257 + if (n < 1) {
  258 + return null;
  259 + }
  260 + double sd[] = new double[n];
  261 +
  262 + // From NSMutableArray to sd[n];
  263 + for (int i = 0; i < n; i++) {
  264 + sd[i] = sdA.get(i);
  265 + }
  266 +
  267 + ArrayList<Point> output = new ArrayList<Point>(n + 1);
  268 +
  269 + for (int i = 0; i < n - 1; i++) {
  270 + Point cur = points[i];
  271 + Point next = points[i + 1];
  272 +
  273 + for (int x = cur.x; x < next.x; x++) {
  274 + double t = (double) (x - cur.x) / (next.x - cur.x);
  275 +
  276 + double a = 1 - t;
  277 + double b = t;
  278 + double h = next.x - cur.x;
  279 +
  280 + double y = a * cur.y + b * next.y + (h * h / 6) * ((a * a * a - a) * sd[i]
  281 + + (b * b * b - b) * sd[i + 1]);
  282 +
  283 + if (y > 255.0) {
  284 + y = 255.0;
  285 + } else if (y < 0.0) {
  286 + y = 0.0;
  287 + }
  288 +
  289 + output.add(new Point(x, (int) Math.round(y)));
  290 + }
  291 + }
  292 +
  293 + // If the last point is (255, 255) it doesn't get added.
  294 + if (output.size() == 255) {
  295 + output.add(points[points.length - 1]);
  296 + }
  297 + return output;
  298 + }
  299 +
  300 + private ArrayList<Double> createSecondDerivative(Point[] points) {
  301 + int n = points.length;
  302 + if (n <= 1) {
  303 + return null;
  304 + }
  305 +
  306 + double matrix[][] = new double[n][3];
  307 + double result[] = new double[n];
  308 + matrix[0][1] = 1;
  309 + // What about matrix[0][1] and matrix[0][0]? Assuming 0 for now (Brad L.)
  310 + matrix[0][0] = 0;
  311 + matrix[0][2] = 0;
  312 +
  313 + for (int i = 1; i < n - 1; i++) {
  314 + Point P1 = points[i - 1];
  315 + Point P2 = points[i];
  316 + Point P3 = points[i + 1];
  317 +
  318 + matrix[i][0] = (double) (P2.x - P1.x) / 6;
  319 + matrix[i][1] = (double) (P3.x - P1.x) / 3;
  320 + matrix[i][2] = (double) (P3.x - P2.x) / 6;
  321 + result[i] =
  322 + (double) (P3.y - P2.y) / (P3.x - P2.x) - (double) (P2.y - P1.y) / (P2.x - P1.x);
  323 + }
  324 +
  325 + // What about result[0] and result[n-1]? Assuming 0 for now (Brad L.)
  326 + result[0] = 0;
  327 + result[n - 1] = 0;
  328 +
  329 + matrix[n - 1][1] = 1;
  330 + // What about matrix[n-1][0] and matrix[n-1][2]? For now, assuming they are 0 (Brad L.)
  331 + matrix[n - 1][0] = 0;
  332 + matrix[n - 1][2] = 0;
  333 +
  334 + // solving pass1 (up->down)
  335 + for (int i = 1; i < n; i++) {
  336 + double k = matrix[i][0] / matrix[i - 1][1];
  337 + matrix[i][1] -= k * matrix[i - 1][2];
  338 + matrix[i][0] = 0;
  339 + result[i] -= k * result[i - 1];
  340 + }
  341 + // solving pass2 (down->up)
  342 + for (int i = n - 2; i >= 0; i--) {
  343 + double k = matrix[i][2] / matrix[i + 1][1];
  344 + matrix[i][1] -= k * matrix[i + 1][0];
  345 + matrix[i][2] = 0;
  346 + result[i] -= k * result[i + 1];
  347 + }
  348 +
  349 + ArrayList<Double> output = new ArrayList<Double>(n);
  350 + for (int i = 0; i < n; i++) output.add(result[i] / matrix[i][1]);
  351 +
  352 + return output;
  353 + }
  354 +}
0 355 \ No newline at end of file
... ...
app/src/main/java/com/cnlive/demo/stream/filter/IFilter.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/filter/IFilter.java
  1 +package com.cnlive.demo.stream.filter;
  2 +
  3 +import java.nio.FloatBuffer;
  4 +
  5 +/**
  6 + * Created by jerikc on 16/2/23.
  7 + */
  8 +public interface IFilter {
  9 + int getTextureTarget();
  10 +
  11 + void setTextureSize(int width, int height);
  12 +
  13 + void onDraw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex, int vertexCount,
  14 + int coordsPerVertex, int vertexStride, FloatBuffer texBuffer,
  15 + int textureId, int texStride);
  16 +
  17 + void releaseProgram();
  18 +}
... ...
app/src/main/java/com/cnlive/demo/stream/gles/Drawable2d.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/gles/Drawable2d.java
  1 +package com.cnlive.demo.stream.gles;
  2 +
  3 +import java.nio.FloatBuffer;
  4 +
  5 +/**
  6 + * Created by jerikc on 16/2/23.
  7 + */
  8 +public class Drawable2d {
  9 + private static final int SIZEOF_FLOAT = 4;
  10 +
  11 + private static final float FULL_RECTANGLE_COORDS[] = {
  12 + -1.0f, -1.0f, // 0 bottom left
  13 + 1.0f, -1.0f, // 1 bottom right
  14 + -1.0f, 1.0f, // 2 top left
  15 + 1.0f, 1.0f, // 3 top right
  16 + };
  17 +
  18 + private static final float FULL_RECTANGLE_TEX_COORDS[] = {
  19 + 0.0f, 0.0f, // 0 bottom left
  20 + 1.0f, 0.0f, // 1 bottom right
  21 + 0.0f, 1.0f, // 2 top left
  22 + 1.0f, 1.0f // 3 top right
  23 + };
  24 +
  25 + private static final FloatBuffer FULL_RECTANGLE_BUF =
  26 + GlUtil.createFloatBuffer(FULL_RECTANGLE_COORDS);
  27 + private static final FloatBuffer FULL_RECTANGLE_TEX_BUF =
  28 + GlUtil.createFloatBuffer(FULL_RECTANGLE_TEX_COORDS);
  29 +
  30 + private FloatBuffer mVertexArray;
  31 + private FloatBuffer mTexCoordArray;
  32 +
  33 + private int mVertexCount;
  34 + private int mCoordsPerVertex;
  35 + private int mVertexStride;
  36 + private int mTexCoordStride;
  37 +
  38 + public Drawable2d() {
  39 + mVertexArray = FULL_RECTANGLE_BUF;
  40 + mTexCoordArray = FULL_RECTANGLE_TEX_BUF;
  41 + mCoordsPerVertex = 2;
  42 + mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
  43 + mVertexCount = FULL_RECTANGLE_COORDS.length / mCoordsPerVertex;
  44 + mTexCoordStride = 2 * SIZEOF_FLOAT;
  45 + }
  46 +
  47 + /**
  48 + * Returns the array of vertices.
  49 + * <p>
  50 + * To avoid allocations, this returns internal state. The caller must not modify it.
  51 + */
  52 + public FloatBuffer getVertexArray() {
  53 + return mVertexArray;
  54 + }
  55 +
  56 + /**
  57 + * Returns the array of texture coordinates.
  58 + * <p>
  59 + * To avoid allocations, this returns internal state. The caller must not modify it.
  60 + */
  61 + public FloatBuffer getTexCoordArray() {
  62 + return mTexCoordArray;
  63 + }
  64 +
  65 + /**
  66 + * Returns the number of vertices stored in the vertex array.
  67 + */
  68 + public int getVertexCount() {
  69 + return mVertexCount;
  70 + }
  71 +
  72 + /**
  73 + * Returns the width, in bytes, of the data for each vertex.
  74 + */
  75 + public int getVertexStride() {
  76 + return mVertexStride;
  77 + }
  78 +
  79 + /**
  80 + * Returns the width, in bytes, of the data for each texture coordinate.
  81 + */
  82 + public int getTexCoordStride() {
  83 + return mTexCoordStride;
  84 + }
  85 +
  86 + /**
  87 + * Returns the number of position coordinates per vertex. This will be 2 or 3.
  88 + */
  89 + public int getCoordsPerVertex() {
  90 + return mCoordsPerVertex;
  91 + }
  92 +}
... ...
app/src/main/java/com/cnlive/demo/stream/gles/FBO.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/gles/FBO.java
  1 +package com.cnlive.demo.stream.gles;
  2 +
  3 +import android.content.Context;
  4 +import android.opengl.GLES20;
  5 +import android.util.Log;
  6 +
  7 +import com.cnlive.demo.stream.Config;
  8 +import com.cnlive.demo.stream.R;
  9 +import com.cnlive.demo.stream.filter.CameraFilterBeauty;
  10 +
  11 +
  12 +/**
  13 + * Created by jerikc on 16/2/23.
  14 + */
  15 +public class FBO {
  16 + private static final String TAG = "FBO";
  17 + private boolean mEnable = Config.FILTER_ENABLED;
  18 +
  19 + private int mSurfaceWidth;
  20 + private int mSurfaceHeight;
  21 +
  22 + private static int[] mCurveArrays = new int[] {
  23 + R.raw.cross_1, R.raw.cross_2, R.raw.cross_3, R.raw.cross_4, R.raw.cross_5,
  24 + R.raw.cross_6, R.raw.cross_7, R.raw.cross_8, R.raw.cross_9, R.raw.cross_10,
  25 + R.raw.cross_11,
  26 + };
  27 + private int mCurveIndex = 10;
  28 +
  29 + // Used for off-screen rendering.
  30 + private int mOffscreenTexture;
  31 + private int mFramebuffer;
  32 + private FullFrameRect mFullScreen;
  33 +
  34 + public void updateSurfaceSize(int width, int height) {
  35 + if (!mEnable) {
  36 + return;
  37 + }
  38 +
  39 + mSurfaceWidth = width;
  40 + mSurfaceHeight = height;
  41 + }
  42 +
  43 + public void initialize(Context context) {
  44 + if (!mEnable) {
  45 + return;
  46 + }
  47 +
  48 + if (mFullScreen != null) {
  49 + mFullScreen.release(false);
  50 + }
  51 +
  52 + /**
  53 + * Create a new full frame renderer with beauty filter.
  54 + * There are two another filter, you can have a try.
  55 + */
  56 +// mFullScreen = new FullFrameRect(new CameraFilterToneCurve(context,
  57 +// context.getResources().openRawResource(mCurveArrays[mCurveIndex])));
  58 +// mFullScreen = new FullFrameRect(new CameraFilterMosaic(context));
  59 + mFullScreen = new FullFrameRect(new CameraFilterBeauty(context));
  60 +
  61 + mOffscreenTexture = 0;
  62 + }
  63 +
  64 + public void release() {
  65 + if (!mEnable) {
  66 + return;
  67 + }
  68 + mFullScreen.release(true);
  69 + }
  70 +
  71 + /**
  72 + * Prepares the off-screen framebuffer.
  73 + */
  74 + private void prepareFramebuffer(int width, int height) {
  75 + GlUtil.checkGlError("start");
  76 + int[] values = new int[1];
  77 +
  78 + // Create a texture object and bind it. This will be the color buffer.
  79 + GLES20.glGenTextures(1, values, 0);
  80 + GlUtil.checkGlError("glGenTextures");
  81 + mOffscreenTexture = values[0]; // expected > 0
  82 + Log.i(TAG, "prepareFramebuffer mOffscreenTexture:" + mOffscreenTexture);
  83 +
  84 + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mOffscreenTexture);
  85 + GlUtil.checkGlError("glBindTexture");
  86 +
  87 + // Create texture storage.
  88 + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
  89 + GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
  90 +
  91 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
  92 + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
  93 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
  94 + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
  95 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
  96 + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
  97 + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
  98 + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
  99 +
  100 + GlUtil.checkGlError("glTexParameter");
  101 +
  102 + // Create framebuffer object and bind it.
  103 + GLES20.glGenFramebuffers(1, values, 0);
  104 + GlUtil.checkGlError("glGenFramebuffers");
  105 + mFramebuffer = values[0]; // expected > 0
  106 +
  107 + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFramebuffer);
  108 + GlUtil.checkGlError("glBindFramebuffer " + mFramebuffer);
  109 +
  110 + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
  111 + GLES20.GL_TEXTURE_2D, mOffscreenTexture, 0);
  112 +
  113 + // See if GLES is happy with all this.
  114 + int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
  115 + if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
  116 + throw new RuntimeException("Framebuffer not complete, status=" + status);
  117 + }
  118 +
  119 + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
  120 + // Switch back to the default framebuffer.
  121 + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
  122 + GlUtil.checkGlError("glBindFramebuffer");
  123 + }
  124 +
  125 + public int drawFrame(int texId, int texWidth, int texHeight) {
  126 + if (!mEnable) {
  127 + return texId;
  128 + }
  129 + GLES20.glViewport(0, 0, texWidth, texHeight);
  130 + if (mOffscreenTexture == 0) {
  131 + prepareFramebuffer(texWidth, texHeight);
  132 + }
  133 + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFramebuffer);
  134 +
  135 + mFullScreen.getFilter().setTextureSize(texWidth, texHeight);
  136 + mFullScreen.drawFrame(texId);
  137 +
  138 + // Blit to display.
  139 + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
  140 + GLES20.glViewport(0, 0, mSurfaceWidth, mSurfaceHeight);
  141 + return mOffscreenTexture;
  142 + }
  143 +}
... ...
app/src/main/java/com/cnlive/demo/stream/gles/FullFrameRect.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/gles/FullFrameRect.java
  1 +package com.cnlive.demo.stream.gles;
  2 +
  3 +/**
  4 + * Created by jerikc on 16/2/23.
  5 + */
  6 +import android.opengl.Matrix;
  7 +
  8 +import com.cnlive.demo.stream.filter.IFilter;
  9 +
  10 +/**
  11 + * This class essentially represents a viewport-sized sprite that will be rendered with
  12 + * a texture, usually from an external source like the camera.
  13 + */
  14 +public class FullFrameRect {
  15 + private final Drawable2d mRectDrawable = new Drawable2d();
  16 + private IFilter mFilter;
  17 + public final float[] IDENTITY_MATRIX = new float[16];
  18 +
  19 + /**
  20 + * Prepares the object.
  21 + *
  22 + * @param program The program to use. FullFrameRect takes ownership, and will release
  23 + * the program when no longer needed.
  24 + */
  25 + public FullFrameRect(IFilter program) {
  26 + mFilter = program;
  27 + Matrix.setIdentityM(IDENTITY_MATRIX, 0);
  28 + }
  29 +
  30 + /**
  31 + * Releases resources.
  32 + * <p>
  33 + * This must be called with the appropriate EGL context current (i.e. the one that was
  34 + * current when the constructor was called). If we're about to destroy the EGL context,
  35 + * there's no value in having the caller make it current just to do this cleanup, so you
  36 + * can pass a flag that will tell this function to skip any EGL-context-specific cleanup.
  37 + */
  38 + public void release(boolean doEglCleanup) {
  39 + if (mFilter != null) {
  40 + if (doEglCleanup) {
  41 + mFilter.releaseProgram();
  42 + }
  43 + mFilter = null;
  44 + }
  45 + }
  46 +
  47 + /**
  48 + * Returns the program currently in use.
  49 + */
  50 + public IFilter getFilter() {
  51 + return mFilter;
  52 + }
  53 +
  54 + /**
  55 + * Changes the program. The previous program will be released.
  56 + * <p>
  57 + * The appropriate EGL context must be current.
  58 + */
  59 + public void changeProgram(IFilter newFilter) {
  60 + mFilter.releaseProgram();
  61 + mFilter = newFilter;
  62 + }
  63 +
  64 + /**
  65 + * Draws a viewport-filling rect, texturing it with the specified texture object.
  66 + */
  67 + public void drawFrame(int textureId) {
  68 + // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport.
  69 + mFilter.onDraw(IDENTITY_MATRIX, mRectDrawable.getVertexArray(), 0,
  70 + mRectDrawable.getVertexCount(), mRectDrawable.getCoordsPerVertex(),
  71 + mRectDrawable.getVertexStride(), mRectDrawable.getTexCoordArray(),
  72 + textureId, mRectDrawable.getTexCoordStride());
  73 + }
  74 +}
... ...
app/src/main/java/com/cnlive/demo/stream/gles/GlUtil.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/gles/GlUtil.java
  1 +package com.cnlive.demo.stream.gles;
  2 +
  3 +import android.content.Context;
  4 +import android.graphics.Bitmap;
  5 +import android.opengl.GLES20;
  6 +import android.opengl.GLUtils;
  7 +import android.support.annotation.Nullable;
  8 +import android.support.annotation.RawRes;
  9 +import android.util.Log;
  10 +
  11 +import java.io.BufferedReader;
  12 +import java.io.IOException;
  13 +import java.io.InputStream;
  14 +import java.io.InputStreamReader;
  15 +import java.nio.ByteBuffer;
  16 +import java.nio.ByteOrder;
  17 +import java.nio.FloatBuffer;
  18 +
  19 +/**
  20 + * Created by jerikc on 16/2/23.
  21 + */
  22 +public class GlUtil {
  23 + private static final String TAG = "GlDemoUtil";
  24 + /** Identity matrix for general use. Don't modify or life will get weird. */
  25 +
  26 + public static final int NO_TEXTURE = -1;
  27 +
  28 + private static final int SIZEOF_FLOAT = 4;
  29 +
  30 + private GlUtil() { // do not instantiate
  31 + }
  32 +
  33 + public static int createProgram(Context applicationContext, @RawRes int vertexSourceRawId,
  34 + @RawRes int fragmentSourceRawId) {
  35 +
  36 + String vertexSource = readTextFromRawResource(applicationContext, vertexSourceRawId);
  37 + String fragmentSource = readTextFromRawResource(applicationContext, fragmentSourceRawId);
  38 +
  39 + return createProgram(vertexSource, fragmentSource);
  40 + }
  41 +
  42 + public static int createProgram(String vertexSource, String fragmentSource) {
  43 + int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
  44 + if (vertexShader == 0) {
  45 + return 0;
  46 + }
  47 + int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
  48 + if (pixelShader == 0) {
  49 + return 0;
  50 + }
  51 + int program = GLES20.glCreateProgram();
  52 + checkGlError("glCreateProgram");
  53 + if (program == 0) {
  54 + Log.e(TAG, "Could not create program");
  55 + }
  56 + GLES20.glAttachShader(program, vertexShader);
  57 + checkGlError("glAttachShader");
  58 + GLES20.glAttachShader(program, pixelShader);
  59 + checkGlError("glAttachShader");
  60 + GLES20.glLinkProgram(program);
  61 + int[] linkStatus = new int[1];
  62 + GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
  63 + if (linkStatus[0] != GLES20.GL_TRUE) {
  64 + Log.e(TAG, "Could not link program: ");
  65 + Log.e(TAG, GLES20.glGetProgramInfoLog(program));
  66 + GLES20.glDeleteProgram(program);
  67 + program = 0;
  68 + }
  69 + Log.i(TAG, "linkStatus:" + linkStatus[0]);
  70 + return program;
  71 + }
  72 +
  73 + public static int loadShader(int shaderType, String source) {
  74 + int shader = GLES20.glCreateShader(shaderType);
  75 + checkGlError("glCreateShader type=" + shaderType);
  76 + GLES20.glShaderSource(shader, source);
  77 + GLES20.glCompileShader(shader);
  78 + int[] compiled = new int[1];
  79 + GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
  80 + if (compiled[0] == 0) {
  81 + Log.e(TAG, "Could not compile shader " + shaderType + ":");
  82 + Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader));
  83 + GLES20.glDeleteShader(shader);
  84 + shader = 0;
  85 + }
  86 + return shader;
  87 + }
  88 +
  89 + public static int createTexture(int textureTarget, @Nullable Bitmap bitmap, int minFilter,
  90 + int magFilter, int wrapS, int wrapT) {
  91 + int[] textureHandle = new int[1];
  92 +
  93 + GLES20.glGenTextures(1, textureHandle, 0);
  94 + GlUtil.checkGlError("glGenTextures");
  95 + GLES20.glBindTexture(textureTarget, textureHandle[0]);
  96 + GlUtil.checkGlError("glBindTexture " + textureHandle[0]);
  97 + GLES20.glTexParameterf(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, minFilter);
  98 + GLES20.glTexParameterf(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, magFilter); //线性插值
  99 + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, wrapS);
  100 + GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, wrapT);
  101 +
  102 + if (bitmap != null) {
  103 + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
  104 + }
  105 +
  106 + GlUtil.checkGlError("glTexParameter");
  107 + return textureHandle[0];
  108 + }
  109 +
  110 + public static int createTexture(int textureTarget) {
  111 + return createTexture(textureTarget, null, GLES20.GL_LINEAR, GLES20.GL_LINEAR,
  112 + GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE);
  113 + }
  114 +
  115 + public static int createTexture(int textureTarget, Bitmap bitmap) {
  116 + return createTexture(textureTarget, bitmap, GLES20.GL_LINEAR, GLES20.GL_LINEAR,
  117 + GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE);
  118 + }
  119 +
  120 + /**
  121 + * Checks to see if a GLES error has been raised.
  122 + */
  123 + public static void checkGlError(String op) {
  124 + int error = GLES20.glGetError();
  125 + if (error != GLES20.GL_NO_ERROR) {
  126 + String msg = op + ": glError 0x" + Integer.toHexString(error);
  127 + Log.e(TAG, msg);
  128 + throw new RuntimeException(msg);
  129 + }
  130 + }
  131 +
  132 + /**
  133 + * Allocates a direct float buffer, and populates it with the float array data.
  134 + */
  135 + public static FloatBuffer createFloatBuffer(float[] coords) {
  136 + // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it.
  137 + ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT);
  138 + bb.order(ByteOrder.nativeOrder());
  139 + FloatBuffer fb = bb.asFloatBuffer();
  140 + fb.put(coords);
  141 + fb.position(0);
  142 + return fb;
  143 + }
  144 +
  145 + public static String readTextFromRawResource(final Context applicationContext,
  146 + @RawRes final int resourceId) {
  147 + final InputStream inputStream =
  148 + applicationContext.getResources().openRawResource(resourceId);
  149 + final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
  150 + final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
  151 + String nextLine;
  152 + final StringBuilder body = new StringBuilder();
  153 + try {
  154 + while ((nextLine = bufferedReader.readLine()) != null) {
  155 + body.append(nextLine);
  156 + body.append('\n');
  157 + }
  158 + } catch (IOException e) {
  159 + return null;
  160 + }
  161 +
  162 + return body.toString();
  163 + }
  164 +}
... ...
app/src/main/java/com/cnlive/demo/stream/ui/CameraPreviewFrameView.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/ui/CameraPreviewFrameView.java
  1 +package com.cnlive.demo.stream.ui;
  2 +
  3 +import android.content.Context;
  4 +import android.opengl.GLSurfaceView;
  5 +import android.util.AttributeSet;
  6 +import android.util.Log;
  7 +import android.view.GestureDetector;
  8 +import android.view.MotionEvent;
  9 +import android.view.ScaleGestureDetector;
  10 +
  11 +/**
  12 + * Created by jerikc on 16/2/5.
  13 + */
  14 +public class CameraPreviewFrameView extends GLSurfaceView {
  15 + private static final String TAG = "CameraPreviewFrameView";
  16 +
  17 + public interface Listener {
  18 + boolean onSingleTapUp(MotionEvent e);
  19 + boolean onZoomValueChanged(float factor);
  20 + }
  21 +
  22 + private Listener mListener;
  23 + private ScaleGestureDetector mScaleDetector;
  24 + private GestureDetector mGestureDetector;
  25 +
  26 + public CameraPreviewFrameView(Context context) {
  27 + super(context);
  28 + initialize(context);
  29 + }
  30 +
  31 + public CameraPreviewFrameView(Context context, AttributeSet attrs) {
  32 + super(context, attrs);
  33 + initialize(context);
  34 + }
  35 +
  36 + public void setListener(Listener listener) {
  37 + mListener = listener;
  38 + }
  39 +
  40 + @Override
  41 + public boolean onTouchEvent(MotionEvent event) {
  42 + if (!mGestureDetector.onTouchEvent(event)) {
  43 + return mScaleDetector.onTouchEvent(event);
  44 + }
  45 + return false;
  46 + }
  47 +
  48 + private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
  49 + @Override
  50 + public boolean onSingleTapUp(MotionEvent e) {
  51 + if (mListener != null) {
  52 + mListener.onSingleTapUp(e);
  53 + }
  54 + return false;
  55 + }
  56 + };
  57 +
  58 + private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
  59 +
  60 + private float mScaleFactor = 1.0f;
  61 +
  62 + @Override
  63 + public boolean onScaleBegin(ScaleGestureDetector detector) {
  64 + return true;
  65 + }
  66 +
  67 + @Override
  68 + public boolean onScale(ScaleGestureDetector detector) {
  69 + // factor > 1, zoom
  70 + // factor < 1, pinch
  71 + mScaleFactor *= detector.getScaleFactor();
  72 +
  73 + // Don't let the object get too small or too large.
  74 + mScaleFactor = Math.max(0.01f, Math.min(mScaleFactor, 1.0f));
  75 +
  76 + return mListener != null && mListener.onZoomValueChanged(mScaleFactor);
  77 + }
  78 + };
  79 +
  80 + private void initialize(Context context) {
  81 + Log.i(TAG, "initialize");
  82 + mScaleDetector = new ScaleGestureDetector(context, mScaleListener);
  83 + mGestureDetector = new GestureDetector(context, mGestureListener);
  84 + }
  85 +}
... ...
app/src/main/java/com/cnlive/demo/stream/ui/FocusIndicatorRotateLayout.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/ui/FocusIndicatorRotateLayout.java
  1 +package com.cnlive.demo.stream.ui;
  2 +
  3 +import android.annotation.TargetApi;
  4 +import android.content.Context;
  5 +import android.os.Build;
  6 +import android.util.AttributeSet;
  7 +import android.util.Log;
  8 +
  9 +import com.cnlive.demo.stream.R;
  10 +
  11 +
  12 +// A view that indicates the focus area or the metering area.
  13 +public class FocusIndicatorRotateLayout extends RotateLayout {
  14 + private static final String TAG = "FocusIndicatorLayout";
  15 +
  16 + // Sometimes continuous autofucus starts and stops several times quickly.
  17 + // These states are used to make sure the animation is run for at least some
  18 + // time.
  19 + private int mState;
  20 + private static final int STATE_IDLE = 0;
  21 + private static final int STATE_FOCUSING = 1;
  22 + private static final int STATE_FINISHING = 2;
  23 +
  24 + private Runnable mDisappear = new Disappear();
  25 + private Runnable mEndAction = new EndAction();
  26 +
  27 + private static final int SCALING_UP_TIME = 1000;
  28 + private static final int SCALING_DOWN_TIME = 200;
  29 + private static final int DISAPPEAR_TIMEOUT = 200;
  30 +
  31 + public FocusIndicatorRotateLayout(Context context, AttributeSet attrs) {
  32 + super(context, attrs);
  33 + }
  34 +
  35 + private void setDrawable(int resid) {
  36 + mChild.setBackgroundDrawable(getResources().getDrawable(resid));
  37 + }
  38 +
  39 + @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  40 + public void showStart() {
  41 + Log.i(TAG, "showStart");
  42 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
  43 + return;
  44 + }
  45 + if (mState == STATE_IDLE) {
  46 + setDrawable(R.drawable.ic_focus_focusing);
  47 + animate().withLayer().setDuration(SCALING_UP_TIME)
  48 + .scaleX(1.5f).scaleY(1.5f);
  49 + mState = STATE_FOCUSING;
  50 + }
  51 + }
  52 +
  53 + @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  54 + public void showSuccess(boolean timeout) {
  55 + Log.i(TAG, "showSuccess");
  56 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
  57 + return;
  58 + }
  59 + if (mState == STATE_FOCUSING) {
  60 + setDrawable(R.drawable.ic_focus_focused);
  61 + animate().withLayer().setDuration(SCALING_DOWN_TIME).scaleX(1f)
  62 + .scaleY(1f).withEndAction(timeout ? mEndAction : null);
  63 + mState = STATE_FINISHING;
  64 + }
  65 + }
  66 +
  67 + @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  68 + public void showFail(boolean timeout) {
  69 + Log.i(TAG, "showFail");
  70 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
  71 + return;
  72 + }
  73 + if (mState == STATE_FOCUSING) {
  74 + setDrawable(R.drawable.ic_focus_failed);
  75 + animate().withLayer().setDuration(SCALING_DOWN_TIME).scaleX(1f)
  76 + .scaleY(1f).withEndAction(timeout ? mEndAction : null);
  77 + mState = STATE_FINISHING;
  78 + }
  79 + }
  80 +
  81 + @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
  82 + public void clear() {
  83 + Log.i(TAG, "clear");
  84 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
  85 + return;
  86 + }
  87 + animate().cancel();
  88 + removeCallbacks(mDisappear);
  89 + mDisappear.run();
  90 + setScaleX(1f);
  91 + setScaleY(1f);
  92 + }
  93 +
  94 + private class EndAction implements Runnable {
  95 + @Override
  96 + public void run() {
  97 + // Keep the focus indicator for some time.
  98 + postDelayed(mDisappear, DISAPPEAR_TIMEOUT);
  99 + }
  100 + }
  101 +
  102 + private class Disappear implements Runnable {
  103 + @Override
  104 + public void run() {
  105 + mChild.setBackgroundDrawable(null);
  106 + mState = STATE_IDLE;
  107 + }
  108 + }
  109 +}
0 110 \ No newline at end of file
... ...
app/src/main/java/com/cnlive/demo/stream/ui/Rotatable.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/ui/Rotatable.java
  1 +package com.cnlive.demo.stream.ui;
  2 +
  3 +/**
  4 + * Created by jerikc on 16/2/5.
  5 + */
  6 +public interface Rotatable {
  7 + // Set parameter 'animation' to true to have animation when rotation.
  8 + void setOrientation(int orientation, boolean animation);
  9 +}
... ...
app/src/main/java/com/cnlive/demo/stream/ui/RotateLayout.java 0 → 100644
  1 +++ a/app/src/main/java/com/cnlive/demo/stream/ui/RotateLayout.java
  1 +package com.cnlive.demo.stream.ui;
  2 +
  3 +import android.annotation.TargetApi;
  4 +import android.content.Context;
  5 +import android.os.Build;
  6 +import android.util.AttributeSet;
  7 +import android.view.View;
  8 +import android.view.ViewGroup;
  9 +
  10 +// A RotateLayout is designed to display a single item and provides the
  11 +// capabilities to rotate the item.
  12 +public class RotateLayout extends ViewGroup implements Rotatable {
  13 + @SuppressWarnings("unused")
  14 + private static final String TAG = "RotateLayout";
  15 + private int mOrientation;
  16 + protected View mChild;
  17 + public RotateLayout(Context context, AttributeSet attrs) {
  18 + super(context, attrs);
  19 + // The transparent background here is a workaround of the render issue
  20 + // happened when the view is rotated as the device's orientation
  21 + // changed. The view looks fine in landscape. After rotation, the view
  22 + // is invisible.
  23 + setBackgroundResource(android.R.color.transparent);
  24 + }
  25 +
  26 + @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  27 + @Override
  28 + protected void onFinishInflate() {
  29 + super.onFinishInflate();
  30 + mChild = getChildAt(0);
  31 + mChild.setPivotX(0);
  32 + mChild.setPivotY(0);
  33 + }
  34 +
  35 + @Override
  36 + protected void onLayout(
  37 + boolean change, int left, int top, int right, int bottom) {
  38 + int width = right - left;
  39 + int height = bottom - top;
  40 + switch (mOrientation) {
  41 + case 0:
  42 + case 180:
  43 + mChild.layout(0, 0, width, height);
  44 + break;
  45 + case 90:
  46 + case 270:
  47 + mChild.layout(0, 0, height, width);
  48 + break;
  49 + }
  50 + }
  51 +
  52 + @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  53 + @Override
  54 + protected void onMeasure(int widthSpec, int heightSpec) {
  55 + int w = 0, h = 0;
  56 + switch(mOrientation) {
  57 + case 0:
  58 + case 180:
  59 + measureChild(mChild, widthSpec, heightSpec);
  60 + w = mChild.getMeasuredWidth();
  61 + h = mChild.getMeasuredHeight();
  62 + break;
  63 + case 90:
  64 + case 270:
  65 + measureChild(mChild, heightSpec, widthSpec);
  66 + w = mChild.getMeasuredHeight();
  67 + h = mChild.getMeasuredWidth();
  68 + break;
  69 + }
  70 + setMeasuredDimension(w, h);
  71 + switch (mOrientation) {
  72 + case 0:
  73 + mChild.setTranslationX(0);
  74 + mChild.setTranslationY(0);
  75 + break;
  76 + case 90:
  77 + mChild.setTranslationX(0);
  78 + mChild.setTranslationY(h);
  79 + break;
  80 + case 180:
  81 + mChild.setTranslationX(w);
  82 + mChild.setTranslationY(h);
  83 + break;
  84 + case 270:
  85 + mChild.setTranslationX(w);
  86 + mChild.setTranslationY(0);
  87 + break;
  88 + }
  89 + mChild.setRotation(-mOrientation);
  90 + }
  91 +
  92 + @Override
  93 + public boolean shouldDelayChildPressedState() {
  94 + return false;
  95 + }
  96 +
  97 + // Rotate the view counter-clockwise
  98 + @Override
  99 + public void setOrientation(int orientation, boolean animation) {
  100 + orientation = orientation % 360;
  101 + if (mOrientation == orientation) return;
  102 + mOrientation = orientation;
  103 + requestLayout();
  104 + }
  105 +}
0 106 \ No newline at end of file
... ...
app/src/main/jniLibs/arm64-v8a/libpldroid_mmprocessing.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/arm64-v8a/libpldroid_streaming_aac_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/arm64-v8a/libpldroid_streaming_core.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/arm64-v8a/libpldroid_streaming_h264_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libpldroid_mmprocessing.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_aac_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_core.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi-v7a/libpldroid_streaming_h264_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libpldroid_mmprocessing.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libpldroid_streaming_aac_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libpldroid_streaming_core.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/armeabi/libpldroid_streaming_h264_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libpldroid_mmprocessing.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libpldroid_streaming_aac_encoder.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libpldroid_streaming_core.so 0 → 100644
No preview for this file type
app/src/main/jniLibs/x86/libpldroid_streaming_h264_encoder.so 0 → 100644
No preview for this file type
app/src/main/res/drawable-hdpi/cnlive_logo.png 0 → 100644

6.02 KB

app/src/main/res/drawable-hdpi/ic_focus_failed.9.png 0 → 100644

809 Bytes

app/src/main/res/drawable-hdpi/ic_focus_focused.9.png 0 → 100644

927 Bytes

app/src/main/res/drawable-hdpi/ic_focus_focusing.9.png 0 → 100644

812 Bytes

app/src/main/res/drawable-xxhdpi/icon_record_close_off.png 0 → 100644

5.67 KB

app/src/main/res/drawable-xxhdpi/icon_record_flash_off.png 0 → 100644

3.77 KB

app/src/main/res/drawable-xxhdpi/icon_record_flip_off.png 0 → 100644

4.03 KB

app/src/main/res/drawable-xxhdpi/icon_record_meiyan_off.png 0 → 100644

4.48 KB

app/src/main/res/drawable-xxhdpi/icon_record_meiyan_on.png 0 → 100644

6.14 KB

app/src/main/res/layout/activity_main.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/activity_main.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:tools="http://schemas.android.com/tools"
  4 + android:id="@+id/activity_main"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + android:paddingBottom="@dimen/activity_vertical_margin"
  8 + android:paddingLeft="@dimen/activity_horizontal_margin"
  9 + android:paddingRight="@dimen/activity_horizontal_margin"
  10 + android:paddingTop="@dimen/activity_vertical_margin"
  11 + tools:context=".MainActivity">
  12 +
  13 + <LinearLayout
  14 + android:layout_width="match_parent"
  15 + android:layout_height="wrap_content"
  16 + android:orientation="vertical">
  17 +
  18 + <LinearLayout
  19 + android:layout_width="match_parent"
  20 + android:layout_height="wrap_content"
  21 + android:orientation="horizontal">
  22 +
  23 + <TextView
  24 + android:layout_width="wrap_content"
  25 + android:layout_height="wrap_content"
  26 + android:text="ActivityID"
  27 + android:textColor="#000000"
  28 + android:textSize="18sp" />
  29 +
  30 + <EditText
  31 + android:id="@+id/activityId"
  32 + android:layout_width="match_parent"
  33 + android:layout_height="wrap_content"
  34 + android:hint="请输入activityId" />
  35 + </LinearLayout>
  36 +
  37 + <LinearLayout
  38 + android:layout_width="match_parent"
  39 + android:layout_height="wrap_content"
  40 + android:orientation="horizontal">
  41 +
  42 + <TextView
  43 + android:layout_width="wrap_content"
  44 + android:layout_height="wrap_content"
  45 + android:text="ChannelID"
  46 + android:textColor="#000000"
  47 + android:textSize="18sp" />
  48 +
  49 + <EditText
  50 + android:id="@+id/channelId"
  51 + android:layout_width="match_parent"
  52 + android:layout_height="wrap_content"
  53 + android:hint="请输入ChannelId" />
  54 + </LinearLayout>
  55 + </LinearLayout>
  56 +
  57 + <Button
  58 + android:id="@+id/connectBtn"
  59 + android:layout_width="match_parent"
  60 + android:layout_height="wrap_content"
  61 + android:layout_alignParentBottom="true"
  62 + android:text="准备推流"
  63 + android:textSize="18sp" />
  64 +</RelativeLayout>
... ...
app/src/main/res/layout/activity_stream.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/activity_stream.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:tools="http://schemas.android.com/tools"
  4 + android:id="@+id/activity_stream"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + tools:context=".BaseStreamActivity">
  8 +
  9 +
  10 + <com.cnlive.libs.lib_cnstream.base.CNAspectFrameLayout
  11 + android:id="@+id/cameraPreview_afl"
  12 + android:layout_width="match_parent"
  13 + android:layout_height="match_parent"
  14 + android:layout_alignParentTop="true"
  15 + android:layout_centerHorizontal="true">
  16 +
  17 + <com.cnlive.demo.stream.ui.CameraPreviewFrameView
  18 + android:id="@+id/cameraPreview_surfaceView"
  19 + android:layout_width="match_parent"
  20 + android:layout_height="match_parent"
  21 + android:layout_gravity="center" />
  22 +
  23 + <include layout="@layout/focus_indicator" />
  24 +
  25 + </com.cnlive.libs.lib_cnstream.base.CNAspectFrameLayout>
  26 +
  27 + <RelativeLayout
  28 + android:layout_width="wrap_content"
  29 + android:layout_height="wrap_content"
  30 + android:layout_alignParentRight="true"
  31 + android:layout_marginTop="8dp">
  32 +
  33 + <ImageView
  34 + android:id="@+id/flashBtn"
  35 + android:layout_width="wrap_content"
  36 + android:layout_height="wrap_content"
  37 + android:layout_marginRight="8dp"
  38 + android:background="@drawable/icon_record_flash_off" />
  39 +
  40 + <ImageView
  41 + android:id="@+id/flipBtn"
  42 + android:layout_width="wrap_content"
  43 + android:layout_height="wrap_content"
  44 + android:layout_marginRight="8dp"
  45 + android:layout_toRightOf="@id/flashBtn"
  46 + android:background="@drawable/icon_record_flip_off" />
  47 +
  48 + <ImageView
  49 + android:id="@+id/beautyBtn"
  50 + android:layout_width="wrap_content"
  51 + android:layout_height="wrap_content"
  52 + android:layout_marginRight="8dp"
  53 + android:layout_toRightOf="@id/flipBtn"
  54 + android:background="@drawable/icon_record_meiyan_on" />
  55 +
  56 + <ImageView
  57 + android:id="@+id/closeBtn"
  58 + android:layout_width="wrap_content"
  59 + android:layout_height="wrap_content"
  60 + android:layout_marginRight="8dp"
  61 + android:layout_toRightOf="@id/beautyBtn"
  62 + android:background="@drawable/icon_record_close_off" />
  63 + </RelativeLayout>
  64 +
  65 + <SeekBar
  66 + android:id="@+id/beautyLevel_seekBar"
  67 + android:layout_width="match_parent"
  68 + android:layout_height="wrap_content"
  69 + android:layout_alignParentBottom="true"
  70 + android:layout_marginBottom="100dp"
  71 + android:max="100"
  72 + android:progress="100" />
  73 +
  74 + <LinearLayout
  75 + android:layout_width="match_parent"
  76 + android:layout_height="wrap_content"
  77 + android:layout_alignParentBottom="true"
  78 + android:layout_alignParentLeft="true"
  79 + android:layout_alignParentStart="true"
  80 + android:orientation="horizontal">
  81 +
  82 + <Button
  83 + android:id="@+id/orientationBtn"
  84 + android:layout_width="0dp"
  85 + android:layout_height="wrap_content"
  86 + android:layout_weight="1"
  87 + android:text="横屏"
  88 + android:textSize="14sp" />
  89 +
  90 + <Button
  91 + android:id="@+id/startStreamBtn"
  92 + android:layout_width="0dp"
  93 + android:layout_height="wrap_content"
  94 + android:layout_marginLeft="5dp"
  95 + android:layout_weight="1"
  96 + android:text="开始推流"
  97 + android:textSize="14sp" />
  98 +
  99 + <Button
  100 + android:id="@+id/muteBtn"
  101 + android:layout_width="0dp"
  102 + android:layout_height="wrap_content"
  103 + android:layout_marginLeft="5dp"
  104 + android:layout_weight="1"
  105 + android:text="静音"
  106 + android:textSize="14sp" />
  107 +
  108 + <Button
  109 + android:id="@+id/captureBtn"
  110 + android:layout_width="0dp"
  111 + android:layout_height="wrap_content"
  112 + android:layout_marginLeft="5dp"
  113 + android:layout_weight="1"
  114 + android:text="截帧"
  115 + android:textSize="14sp" />
  116 + </LinearLayout>
  117 +</RelativeLayout>
... ...
app/src/main/res/layout/focus_indicator.xml 0 → 100644
  1 +++ a/app/src/main/res/layout/focus_indicator.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<com.cnlive.demo.stream.ui.FocusIndicatorRotateLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:id="@+id/focus_indicator_rotate_layout"
  4 + android:layout_width="wrap_content"
  5 + android:layout_height="wrap_content"
  6 + android:layout_centerInParent="true">
  7 +
  8 + <View
  9 + android:id="@+id/focus_indicator"
  10 + android:layout_width="120dp"
  11 + android:layout_height="120dp" />
  12 +</com.cnlive.demo.stream.ui.FocusIndicatorRotateLayout>
0 13 \ No newline at end of file
... ...
app/src/main/res/mipmap-hdpi/ic_launcher.png 0 → 100644

3.34 KB

app/src/main/res/mipmap-mdpi/ic_launcher.png 0 → 100644

2.15 KB

app/src/main/res/mipmap-xhdpi/ic_launcher.png 0 → 100644

4.73 KB

app/src/main/res/mipmap-xxhdpi/ic_launcher.png 0 → 100644

7.54 KB

app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 0 → 100644

10.2 KB

app/src/main/res/raw/cross_1.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_10.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_11.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_2.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_3.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_4.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_5.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_6.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_7.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_8.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/cross_9.acv 0 → 100644
No preview for this file type
app/src/main/res/raw/fragment_shader_beauty.glsl 0 → 100644
  1 +++ a/app/src/main/res/raw/fragment_shader_beauty.glsl
  1 +#extension GL_OES_EGL_image_external : require
  2 +
  3 +precision highp float;
  4 +
  5 + uniform samplerExternalOES uTexture;
  6 + uniform vec2 singleStepOffset;
  7 +
  8 + vec4 params = vec4(0.33, 0.63, 0.4, 0.35);
  9 +
  10 + varying vec2 vTextureCoord;
  11 +
  12 + const highp vec3 W = vec3(0.299,0.587,0.114);
  13 +
  14 + const mat3 saturateMatrix = mat3(
  15 + 1.1102,-0.0598,-0.061,
  16 + -0.0774,1.0826,-0.1186,
  17 + -0.0228,-0.0228,1.1772);
  18 +
  19 + vec2 blurCoordinates[24];
  20 +
  21 + float hardLight(float color)
  22 + {
  23 + if(color <= 0.5)
  24 + {
  25 + color = color * color * 2.0;
  26 + }
  27 + else
  28 + {
  29 + color = 1.0 - ((1.0 - color)*(1.0 - color) * 2.0);
  30 + }
  31 + return color;
  32 + }
  33 +
  34 + void main(){
  35 + vec3 centralColor = texture2D(uTexture, vTextureCoord).rgb;
  36 +
  37 + blurCoordinates[0] = vTextureCoord.xy + singleStepOffset * vec2(0.0, -10.0);
  38 + blurCoordinates[1] = vTextureCoord.xy + singleStepOffset * vec2(0.0, 10.0);
  39 + blurCoordinates[2] = vTextureCoord.xy + singleStepOffset * vec2(-10.0, 0.0);
  40 + blurCoordinates[3] = vTextureCoord.xy + singleStepOffset * vec2(10.0, 0.0);
  41 + blurCoordinates[4] = vTextureCoord.xy + singleStepOffset * vec2(5.0, -8.0);
  42 + blurCoordinates[5] = vTextureCoord.xy + singleStepOffset * vec2(5.0, 8.0);
  43 + blurCoordinates[6] = vTextureCoord.xy + singleStepOffset * vec2(-5.0, 8.0);
  44 + blurCoordinates[7] = vTextureCoord.xy + singleStepOffset * vec2(-5.0, -8.0);
  45 + blurCoordinates[8] = vTextureCoord.xy + singleStepOffset * vec2(8.0, -5.0);
  46 + blurCoordinates[9] = vTextureCoord.xy + singleStepOffset * vec2(8.0, 5.0);
  47 + blurCoordinates[10] = vTextureCoord.xy + singleStepOffset * vec2(-8.0, 5.0);
  48 + blurCoordinates[11] = vTextureCoord.xy + singleStepOffset * vec2(-8.0, -5.0);
  49 + blurCoordinates[12] = vTextureCoord.xy + singleStepOffset * vec2(0.0, -6.0);
  50 + blurCoordinates[13] = vTextureCoord.xy + singleStepOffset * vec2(0.0, 6.0);
  51 + blurCoordinates[14] = vTextureCoord.xy + singleStepOffset * vec2(6.0, 0.0);
  52 + blurCoordinates[15] = vTextureCoord.xy + singleStepOffset * vec2(-6.0, 0.0);
  53 + blurCoordinates[16] = vTextureCoord.xy + singleStepOffset * vec2(-4.0, -4.0);
  54 + blurCoordinates[17] = vTextureCoord.xy + singleStepOffset * vec2(-4.0, 4.0);
  55 + blurCoordinates[18] = vTextureCoord.xy + singleStepOffset * vec2(4.0, -4.0);
  56 + blurCoordinates[19] = vTextureCoord.xy + singleStepOffset * vec2(4.0, 4.0);
  57 + blurCoordinates[20] = vTextureCoord.xy + singleStepOffset * vec2(-2.0, -2.0);
  58 + blurCoordinates[21] = vTextureCoord.xy + singleStepOffset * vec2(-2.0, 2.0);
  59 + blurCoordinates[22] = vTextureCoord.xy + singleStepOffset * vec2(2.0, -2.0);
  60 + blurCoordinates[23] = vTextureCoord.xy + singleStepOffset * vec2(2.0, 2.0);
  61 +
  62 + float sampleColor = centralColor.g * 22.0;
  63 + sampleColor += texture2D(uTexture, blurCoordinates[0]).g;
  64 + sampleColor += texture2D(uTexture, blurCoordinates[1]).g;
  65 + sampleColor += texture2D(uTexture, blurCoordinates[2]).g;
  66 + sampleColor += texture2D(uTexture, blurCoordinates[3]).g;
  67 + sampleColor += texture2D(uTexture, blurCoordinates[4]).g;
  68 + sampleColor += texture2D(uTexture, blurCoordinates[5]).g;
  69 + sampleColor += texture2D(uTexture, blurCoordinates[6]).g;
  70 + sampleColor += texture2D(uTexture, blurCoordinates[7]).g;
  71 + sampleColor += texture2D(uTexture, blurCoordinates[8]).g;
  72 + sampleColor += texture2D(uTexture, blurCoordinates[9]).g;
  73 + sampleColor += texture2D(uTexture, blurCoordinates[10]).g;
  74 + sampleColor += texture2D(uTexture, blurCoordinates[11]).g;
  75 + sampleColor += texture2D(uTexture, blurCoordinates[12]).g * 2.0;
  76 + sampleColor += texture2D(uTexture, blurCoordinates[13]).g * 2.0;
  77 + sampleColor += texture2D(uTexture, blurCoordinates[14]).g * 2.0;
  78 + sampleColor += texture2D(uTexture, blurCoordinates[15]).g * 2.0;
  79 + sampleColor += texture2D(uTexture, blurCoordinates[16]).g * 2.0;
  80 + sampleColor += texture2D(uTexture, blurCoordinates[17]).g * 2.0;
  81 + sampleColor += texture2D(uTexture, blurCoordinates[18]).g * 2.0;
  82 + sampleColor += texture2D(uTexture, blurCoordinates[19]).g * 2.0;
  83 + sampleColor += texture2D(uTexture, blurCoordinates[20]).g * 3.0;
  84 + sampleColor += texture2D(uTexture, blurCoordinates[21]).g * 3.0;
  85 + sampleColor += texture2D(uTexture, blurCoordinates[22]).g * 3.0;
  86 + sampleColor += texture2D(uTexture, blurCoordinates[23]).g * 3.0;
  87 + sampleColor = sampleColor / 62.0;
  88 +
  89 + float highPass = centralColor.g - sampleColor + 0.5;
  90 +
  91 + for(int i = 0; i < 5;i++)
  92 + {
  93 + highPass = hardLight(highPass);
  94 + }
  95 + float luminance = dot(centralColor, W);
  96 + float alpha = pow(luminance, params.r);
  97 +
  98 + vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*alpha*0.1;
  99 +
  100 + smoothColor.r = clamp(pow(smoothColor.r, params.g),0.0,1.0);
  101 + smoothColor.g = clamp(pow(smoothColor.g, params.g),0.0,1.0);
  102 + smoothColor.b = clamp(pow(smoothColor.b, params.g),0.0,1.0);
  103 +
  104 + vec3 screen = vec3(1.0) - (vec3(1.0)-smoothColor) * (vec3(1.0)-centralColor);
  105 + vec3 lighten = max(smoothColor, centralColor);
  106 + vec3 softLight = 2.0 * centralColor*smoothColor + centralColor*centralColor
  107 + - 2.0 * centralColor*centralColor * smoothColor;
  108 +
  109 + gl_FragColor = vec4(mix(centralColor, screen, alpha), 1.0);
  110 + gl_FragColor.rgb = mix(gl_FragColor.rgb, lighten, alpha);
  111 + gl_FragColor.rgb = mix(gl_FragColor.rgb, softLight, params.b);
  112 +
  113 + vec3 satColor = gl_FragColor.rgb * saturateMatrix;
  114 + gl_FragColor.rgb = mix(gl_FragColor.rgb, satColor, params.a);
  115 + }
0 116 \ No newline at end of file
... ...
app/src/main/res/raw/fragment_shader_ext_tone_curve.glsl 0 → 100644
  1 +++ a/app/src/main/res/raw/fragment_shader_ext_tone_curve.glsl
  1 +#extension GL_OES_EGL_image_external : require
  2 +precision mediump float;
  3 +
  4 +varying vec2 vTextureCoord;
  5 +uniform samplerExternalOES uTexture;
  6 +uniform sampler2D toneCurveTexture;
  7 +
  8 +void main() {
  9 + vec4 textureColor = texture2D(uTexture, vTextureCoord);
  10 +
  11 + float redCurveValue = texture2D(toneCurveTexture, vec2(textureColor.r, 0.0)).r;
  12 + float greenCurveValue = texture2D(toneCurveTexture, vec2(textureColor.g, 0.0)).g;
  13 + float blueCurveValue = texture2D(toneCurveTexture, vec2(textureColor.b, 0.0)).b;
  14 +
  15 + gl_FragColor = vec4(redCurveValue, greenCurveValue, blueCurveValue, textureColor.a);
  16 +}
0 17 \ No newline at end of file
... ...
app/src/main/res/raw/fragment_shader_mosaic.glsl 0 → 100644
  1 +++ a/app/src/main/res/raw/fragment_shader_mosaic.glsl
  1 +#extension GL_OES_EGL_image_external : require
  2 +
  3 +precision mediump float;
  4 +
  5 +varying vec2 vTextureCoord;
  6 +uniform samplerExternalOES uTexture;
  7 +uniform vec2 TexSize;
  8 +vec2 mosaicSize = vec2(2,2);
  9 +
  10 + void main()
  11 + {
  12 + vec2 intXY = vec2(vTextureCoord.x*TexSize.x, vTextureCoord.y*TexSize.y);
  13 + vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x,floor(intXY.y/mosaicSize.y)*mosaicSize.y);
  14 + vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x,XYMosaic.y/TexSize.y);
  15 + vec4 baseMap = texture2D(uTexture,UVMosaic);
  16 + gl_FragColor = baseMap;
  17 + }
0 18 \ No newline at end of file
... ...
app/src/main/res/raw/fragment_shader_no_filter.glsl 0 → 100644
  1 +++ a/app/src/main/res/raw/fragment_shader_no_filter.glsl
  1 +#extension GL_OES_EGL_image_external : require
  2 +
  3 +precision mediump float;
  4 +
  5 +varying vec2 vTextureCoord;
  6 +uniform samplerExternalOES uTexture;
  7 +
  8 +void main() {
  9 + gl_FragColor = texture2D(uTexture, vTextureCoord);
  10 +}
0 11 \ No newline at end of file
... ...
app/src/main/res/raw/vertex_shader.glsl 0 → 100644
  1 +++ a/app/src/main/res/raw/vertex_shader.glsl
  1 +uniform mat4 uMVPMatrix;
  2 +
  3 +attribute vec4 aPosition;
  4 +attribute vec4 aTextureCoord;
  5 +
  6 +varying vec2 vTextureCoord;
  7 +
  8 +void main() {
  9 + gl_Position = uMVPMatrix * aPosition;
  10 + vTextureCoord = aTextureCoord.xy;
  11 +}
0 12 \ No newline at end of file
... ...
app/src/main/res/raw/vertex_shader_two_input.glsl 0 → 100644
  1 +++ a/app/src/main/res/raw/vertex_shader_two_input.glsl
  1 +uniform mat4 uMVPMatrix;
  2 +
  3 +attribute vec4 aPosition;
  4 +
  5 +attribute vec4 aTextureCoord;
  6 +attribute vec4 aExtraTextureCoord;
  7 +
  8 +varying vec2 vTextureCoord;
  9 +varying vec2 vExtraTextureCoord;
  10 +
  11 +void main() {
  12 + gl_Position = uMVPMatrix * aPosition;
  13 + vExtraTextureCoord = vec2(aExtraTextureCoord.x, 1.0 - aExtraTextureCoord.y);
  14 + vTextureCoord = aTextureCoord.xy;
  15 +}
0 16 \ No newline at end of file
... ...
app/src/main/res/values-w820dp/dimens.xml 0 → 100644
  1 +++ a/app/src/main/res/values-w820dp/dimens.xml
  1 +<resources>
  2 + <!-- Example customization of dimensions originally defined in res/values/dimens.xml
  3 + (such as screen margins) for screens with more than 820dp of available width. This
  4 + would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
  5 + <dimen name="activity_horizontal_margin">64dp</dimen>
  6 +</resources>
... ...
app/src/main/res/values/colors.xml 0 → 100644
  1 +++ a/app/src/main/res/values/colors.xml
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <color name="colorPrimary">#3F51B5</color>
  4 + <color name="colorPrimaryDark">#303F9F</color>
  5 + <color name="colorAccent">#FF4081</color>
  6 +</resources>
... ...
app/src/main/res/values/dimens.xml 0 → 100644
  1 +++ a/app/src/main/res/values/dimens.xml
  1 +<resources>
  2 + <!-- Default screen margins, per the Android Design guidelines. -->
  3 + <dimen name="activity_horizontal_margin">16dp</dimen>
  4 + <dimen name="activity_vertical_margin">16dp</dimen>
  5 +</resources>
... ...
app/src/main/res/values/strings.xml 0 → 100644
  1 +++ a/app/src/main/res/values/strings.xml
  1 +<resources>
  2 + <string name="app_name">QNStreamDemo</string>
  3 +</resources>
... ...
app/src/main/res/values/styles.xml 0 → 100644
  1 +++ a/app/src/main/res/values/styles.xml
  1 +<resources>
  2 +
  3 + <!-- Base application theme. -->
  4 + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  5 + <!-- Customize your theme here. -->
  6 + <item name="colorPrimary">@color/colorPrimary</item>
  7 + <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  8 + <item name="colorAccent">@color/colorAccent</item>
  9 + </style>
  10 +
  11 +</resources>
... ...
app/src/test/java/com/cnlive/demo/stream/ExampleUnitTest.java 0 → 100644
  1 +++ a/app/src/test/java/com/cnlive/demo/stream/ExampleUnitTest.java
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import org.junit.Test;
  4 +
  5 +import static org.junit.Assert.*;
  6 +
  7 +/**
  8 + * Example local unit test, which will execute on the development machine (host).
  9 + *
  10 + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
  11 + */
  12 +public class ExampleUnitTest {
  13 + @Test
  14 + public void addition_isCorrect() throws Exception {
  15 + assertEquals(4, 2 + 2);
  16 + }
  17 +}
0 18 \ No newline at end of file
... ...
build.gradle 0 → 100644
  1 +++ a/build.gradle
  1 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
  2 +
  3 +buildscript {
  4 + repositories {
  5 + jcenter()
  6 + }
  7 + dependencies {
  8 + classpath 'com.android.tools.build:gradle:2.2.2'
  9 +
  10 + // NOTE: Do not place your application dependencies here; they belong
  11 + // in the individual module build.gradle files
  12 + }
  13 +}
  14 +
  15 +allprojects {
  16 + repositories {
  17 + jcenter()
  18 + }
  19 +}
  20 +
  21 +task clean(type: Delete) {
  22 + delete rootProject.buildDir
  23 +}
... ...
gradle.properties 0 → 100644
  1 +++ a/gradle.properties
  1 +# Project-wide Gradle settings.
  2 +
  3 +# IDE (e.g. Android Studio) users:
  4 +# Gradle settings configured through the IDE *will override*
  5 +# any settings specified in this file.
  6 +
  7 +# For more details on how to configure your build environment visit
  8 +# http://www.gradle.org/docs/current/userguide/build_environment.html
  9 +
  10 +# Specifies the JVM arguments used for the daemon process.
  11 +# The setting is particularly useful for tweaking memory settings.
  12 +org.gradle.jvmargs=-Xmx1536m
  13 +
  14 +# When configured, Gradle will run in incubating parallel mode.
  15 +# This option should only be used with decoupled projects. More details, visit
  16 +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
  17 +# org.gradle.parallel=true
... ...
gradle/wrapper/gradle-wrapper.jar 0 → 100644
No preview for this file type
gradle/wrapper/gradle-wrapper.properties 0 → 100644
  1 +++ a/gradle/wrapper/gradle-wrapper.properties
  1 +#Mon Dec 28 10:00:20 PST 2015
  2 +distributionBase=GRADLE_USER_HOME
  3 +distributionPath=wrapper/dists
  4 +zipStoreBase=GRADLE_USER_HOME
  5 +zipStorePath=wrapper/dists
  6 +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
... ...
gradlew 0 → 100644
  1 +++ a/gradlew
  1 +#!/usr/bin/env bash
  2 +
  3 +##############################################################################
  4 +##
  5 +## Gradle start up script for UN*X
  6 +##
  7 +##############################################################################
  8 +
  9 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  10 +DEFAULT_JVM_OPTS=""
  11 +
  12 +APP_NAME="Gradle"
  13 +APP_BASE_NAME=`basename "$0"`
  14 +
  15 +# Use the maximum available, or set MAX_FD != -1 to use that value.
  16 +MAX_FD="maximum"
  17 +
  18 +warn ( ) {
  19 + echo "$*"
  20 +}
  21 +
  22 +die ( ) {
  23 + echo
  24 + echo "$*"
  25 + echo
  26 + exit 1
  27 +}
  28 +
  29 +# OS specific support (must be 'true' or 'false').
  30 +cygwin=false
  31 +msys=false
  32 +darwin=false
  33 +case "`uname`" in
  34 + CYGWIN* )
  35 + cygwin=true
  36 + ;;
  37 + Darwin* )
  38 + darwin=true
  39 + ;;
  40 + MINGW* )
  41 + msys=true
  42 + ;;
  43 +esac
  44 +
  45 +# Attempt to set APP_HOME
  46 +# Resolve links: $0 may be a link
  47 +PRG="$0"
  48 +# Need this for relative symlinks.
  49 +while [ -h "$PRG" ] ; do
  50 + ls=`ls -ld "$PRG"`
  51 + link=`expr "$ls" : '.*-> \(.*\)$'`
  52 + if expr "$link" : '/.*' > /dev/null; then
  53 + PRG="$link"
  54 + else
  55 + PRG=`dirname "$PRG"`"/$link"
  56 + fi
  57 +done
  58 +SAVED="`pwd`"
  59 +cd "`dirname \"$PRG\"`/" >/dev/null
  60 +APP_HOME="`pwd -P`"
  61 +cd "$SAVED" >/dev/null
  62 +
  63 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
  64 +
  65 +# Determine the Java command to use to start the JVM.
  66 +if [ -n "$JAVA_HOME" ] ; then
  67 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
  68 + # IBM's JDK on AIX uses strange locations for the executables
  69 + JAVACMD="$JAVA_HOME/jre/sh/java"
  70 + else
  71 + JAVACMD="$JAVA_HOME/bin/java"
  72 + fi
  73 + if [ ! -x "$JAVACMD" ] ; then
  74 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
  75 +
  76 +Please set the JAVA_HOME variable in your environment to match the
  77 +location of your Java installation."
  78 + fi
  79 +else
  80 + JAVACMD="java"
  81 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  82 +
  83 +Please set the JAVA_HOME variable in your environment to match the
  84 +location of your Java installation."
  85 +fi
  86 +
  87 +# Increase the maximum file descriptors if we can.
  88 +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
  89 + MAX_FD_LIMIT=`ulimit -H -n`
  90 + if [ $? -eq 0 ] ; then
  91 + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
  92 + MAX_FD="$MAX_FD_LIMIT"
  93 + fi
  94 + ulimit -n $MAX_FD
  95 + if [ $? -ne 0 ] ; then
  96 + warn "Could not set maximum file descriptor limit: $MAX_FD"
  97 + fi
  98 + else
  99 + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
  100 + fi
  101 +fi
  102 +
  103 +# For Darwin, add options to specify how the application appears in the dock
  104 +if $darwin; then
  105 + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
  106 +fi
  107 +
  108 +# For Cygwin, switch paths to Windows format before running java
  109 +if $cygwin ; then
  110 + APP_HOME=`cygpath --path --mixed "$APP_HOME"`
  111 + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
  112 + JAVACMD=`cygpath --unix "$JAVACMD"`
  113 +
  114 + # We build the pattern for arguments to be converted via cygpath
  115 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
  116 + SEP=""
  117 + for dir in $ROOTDIRSRAW ; do
  118 + ROOTDIRS="$ROOTDIRS$SEP$dir"
  119 + SEP="|"
  120 + done
  121 + OURCYGPATTERN="(^($ROOTDIRS))"
  122 + # Add a user-defined pattern to the cygpath arguments
  123 + if [ "$GRADLE_CYGPATTERN" != "" ] ; then
  124 + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
  125 + fi
  126 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
  127 + i=0
  128 + for arg in "$@" ; do
  129 + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
  130 + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
  131 +
  132 + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
  133 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
  134 + else
  135 + eval `echo args$i`="\"$arg\""
  136 + fi
  137 + i=$((i+1))
  138 + done
  139 + case $i in
  140 + (0) set -- ;;
  141 + (1) set -- "$args0" ;;
  142 + (2) set -- "$args0" "$args1" ;;
  143 + (3) set -- "$args0" "$args1" "$args2" ;;
  144 + (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
  145 + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
  146 + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
  147 + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
  148 + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
  149 + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
  150 + esac
  151 +fi
  152 +
  153 +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
  154 +function splitJvmOpts() {
  155 + JVM_OPTS=("$@")
  156 +}
  157 +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
  158 +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
  159 +
  160 +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
... ...
gradlew.bat 0 → 100644
  1 +++ a/gradlew.bat
  1 +@if "%DEBUG%" == "" @echo off
  2 +@rem ##########################################################################
  3 +@rem
  4 +@rem Gradle startup script for Windows
  5 +@rem
  6 +@rem ##########################################################################
  7 +
  8 +@rem Set local scope for the variables with windows NT shell
  9 +if "%OS%"=="Windows_NT" setlocal
  10 +
  11 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  12 +set DEFAULT_JVM_OPTS=
  13 +
  14 +set DIRNAME=%~dp0
  15 +if "%DIRNAME%" == "" set DIRNAME=.
  16 +set APP_BASE_NAME=%~n0
  17 +set APP_HOME=%DIRNAME%
  18 +
  19 +@rem Find java.exe
  20 +if defined JAVA_HOME goto findJavaFromJavaHome
  21 +
  22 +set JAVA_EXE=java.exe
  23 +%JAVA_EXE% -version >NUL 2>&1
  24 +if "%ERRORLEVEL%" == "0" goto init
  25 +
  26 +echo.
  27 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  28 +echo.
  29 +echo Please set the JAVA_HOME variable in your environment to match the
  30 +echo location of your Java installation.
  31 +
  32 +goto fail
  33 +
  34 +:findJavaFromJavaHome
  35 +set JAVA_HOME=%JAVA_HOME:"=%
  36 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
  37 +
  38 +if exist "%JAVA_EXE%" goto init
  39 +
  40 +echo.
  41 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
  42 +echo.
  43 +echo Please set the JAVA_HOME variable in your environment to match the
  44 +echo location of your Java installation.
  45 +
  46 +goto fail
  47 +
  48 +:init
  49 +@rem Get command-line arguments, handling Windowz variants
  50 +
  51 +if not "%OS%" == "Windows_NT" goto win9xME_args
  52 +if "%@eval[2+2]" == "4" goto 4NT_args
  53 +
  54 +:win9xME_args
  55 +@rem Slurp the command line arguments.
  56 +set CMD_LINE_ARGS=
  57 +set _SKIP=2
  58 +
  59 +:win9xME_args_slurp
  60 +if "x%~1" == "x" goto execute
  61 +
  62 +set CMD_LINE_ARGS=%*
  63 +goto execute
  64 +
  65 +:4NT_args
  66 +@rem Get arguments from the 4NT Shell from JP Software
  67 +set CMD_LINE_ARGS=%$
  68 +
  69 +:execute
  70 +@rem Setup the command line
  71 +
  72 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
  73 +
  74 +@rem Execute Gradle
  75 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
  76 +
  77 +:end
  78 +@rem End local scope for the variables with windows NT shell
  79 +if "%ERRORLEVEL%"=="0" goto mainEnd
  80 +
  81 +:fail
  82 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
  83 +rem the _cmd.exe /c_ return code!
  84 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
  85 +exit /b 1
  86 +
  87 +:mainEnd
  88 +if "%OS%"=="Windows_NT" endlocal
  89 +
  90 +:omega
... ...
settings.gradle 0 → 100644
  1 +++ a/settings.gradle
  1 +include ':app'
... ...