Commit f97881e6a78a1a8f75f097a3f2836732a4a6b713
1 parent
c39ca37a
更新推流SDK, 添加悬浮窗预览、截屏、背景图推流、音频滤镜、滤镜等功能
Showing
104 changed files
with
1609 additions
and
124 deletions
app/build.gradle
| @@ -43,7 +43,11 @@ repositories { | @@ -43,7 +43,11 @@ repositories { | ||
| 43 | 43 | ||
| 44 | dependencies { | 44 | dependencies { |
| 45 | compile fileTree(include: ['*.jar'], dir: 'libs') | 45 | compile fileTree(include: ['*.jar'], dir: 'libs') |
| 46 | + compile 'com.cnlive:lib_cnlive:2.1.0' | ||
| 46 | compile 'com.android.support:appcompat-v7:25.2.0' | 47 | compile 'com.android.support:appcompat-v7:25.2.0' |
| 47 | compile 'com.android.support.constraint:constraint-layout:1.0.1' | 48 | compile 'com.android.support.constraint:constraint-layout:1.0.1' |
| 48 | - compile 'com.cnlive:lib_cnlive:2.0.0' | 49 | + |
| 50 | + compile 'com.facebook.fresco:fresco:1.4.0' | ||
| 51 | + compile 'com.facebook.fresco:animated-gif:1.4.0' | ||
| 52 | + compile 'com.facebook.fresco:animated-webp:1.4.0' | ||
| 49 | } | 53 | } |
app/src/main/AndroidManifest.xml
| @@ -35,7 +35,8 @@ | @@ -35,7 +35,8 @@ | ||
| 35 | android:name=".StreamActivity" | 35 | android:name=".StreamActivity" |
| 36 | android:configChanges="orientation|keyboardHidden|screenSize" | 36 | android:configChanges="orientation|keyboardHidden|screenSize" |
| 37 | android:launchMode="singleTop" | 37 | android:launchMode="singleTop" |
| 38 | - android:screenOrientation="portrait" /> | 38 | + android:screenOrientation="portrait" |
| 39 | + android:theme="@style/Theme.RiseAnimation" /> | ||
| 39 | <activity | 40 | <activity |
| 40 | android:name=".CNStreamActivity" | 41 | android:name=".CNStreamActivity" |
| 41 | android:configChanges="orientation|keyboardHidden|screenSize" | 42 | android:configChanges="orientation|keyboardHidden|screenSize" |
| @@ -46,6 +47,11 @@ | @@ -46,6 +47,11 @@ | ||
| 46 | android:configChanges="orientation|keyboardHidden|screenSize" | 47 | android:configChanges="orientation|keyboardHidden|screenSize" |
| 47 | android:launchMode="singleTop" | 48 | android:launchMode="singleTop" |
| 48 | android:screenOrientation="portrait" /> | 49 | android:screenOrientation="portrait" /> |
| 50 | + <activity | ||
| 51 | + android:name=".FloatActivity" | ||
| 52 | + android:configChanges="orientation|keyboardHidden|screenSize" | ||
| 53 | + android:launchMode="singleTop" | ||
| 54 | + android:theme="@style/Theme.RiseAnimation"></activity> | ||
| 49 | </application> | 55 | </application> |
| 50 | 56 | ||
| 51 | </manifest> | 57 | </manifest> |
| 52 | \ No newline at end of file | 58 | \ No newline at end of file |
app/src/main/assets/KSYResource/0_pink.png
0 → 100644
158 KB
app/src/main/assets/KSYResource/0_ruddy2.png
0 → 100644
131 KB
app/src/main/assets/KSYResource/10_sunshine_night.png
0 → 100644
197 KB
app/src/main/assets/KSYResource/11_ruddy.png
0 → 100644
132 KB
app/src/main/assets/KSYResource/12_sunshine.png
0 → 100644
204 KB
app/src/main/assets/KSYResource/13_nature.png
0 → 100644
771 KB
app/src/main/assets/KSYResource/14_amatorka.png
0 → 100644
35.5 KB
app/src/main/assets/KSYResource/15_elegance_1.png
0 → 100644
126 KB
app/src/main/assets/KSYResource/1_freshy.png
0 → 100644
196 KB
app/src/main/assets/KSYResource/2_beauty.png
0 → 100644
64.4 KB
app/src/main/assets/KSYResource/3_sweety.png
0 → 100644
50.3 KB
app/src/main/assets/KSYResource/4_sepia.png
0 → 100644
35.5 KB
app/src/main/assets/KSYResource/5_blue.png
0 → 100644
198 KB
app/src/main/assets/KSYResource/6_nostalgia.png
0 → 100644
136 KB
app/src/main/assets/KSYResource/7_sakura.png
0 → 100644
241 KB
app/src/main/assets/KSYResource/8_sakura_night.png
0 → 100644
226 KB
app/src/main/assets/KSYResource/9_ruddy_night.png
0 → 100644
125 KB
app/src/main/assets/KSYResource/amaro_map.png
0 → 100644
2.95 KB
app/src/main/assets/KSYResource/blackboard_1024.png
0 → 100644
103 KB
app/src/main/assets/KSYResource/blowout_1977.png
0 → 100644
6.62 KB
app/src/main/assets/KSYResource/brannan_blowout.png
0 → 100644
174 Bytes
app/src/main/assets/KSYResource/brannan_contrast.png
0 → 100644
199 Bytes
app/src/main/assets/KSYResource/brannan_luma.png
0 → 100644
229 Bytes
app/src/main/assets/KSYResource/brannan_process.png
0 → 100644
285 Bytes
app/src/main/assets/KSYResource/brannan_screen.png
0 → 100644
217 Bytes
app/src/main/assets/KSYResource/early_bird_curves.png
0 → 100644
156 Bytes
app/src/main/assets/KSYResource/earlybird_blowout.png
0 → 100644
159 Bytes
app/src/main/assets/KSYResource/earlybird_map.png
0 → 100644
208 Bytes
app/src/main/assets/KSYResource/earlybird_overlay_map.png
0 → 100644
187 Bytes
app/src/main/assets/KSYResource/edge_burn.png
0 → 100644
30.5 KB
app/src/main/assets/KSYResource/hefe_gradient_map.png
0 → 100644
2.85 KB
app/src/main/assets/KSYResource/hefe_map.png
0 → 100644
2.93 KB
app/src/main/assets/KSYResource/hefe_metal.png
0 → 100644
109 KB
app/src/main/assets/KSYResource/hefe_soft_light.png
0 → 100644
5.63 KB
app/src/main/assets/KSYResource/hudson_background.png
0 → 100644
85.1 KB
app/src/main/assets/KSYResource/hudson_map.png
0 → 100644
2.82 KB
app/src/main/assets/KSYResource/inkwell_map.png
0 → 100644
2.8 KB
app/src/main/assets/KSYResource/kelvin_map.png
0 → 100644
2.94 KB
app/src/main/assets/KSYResource/lomo_map.png
0 → 100644
2.83 KB
app/src/main/assets/KSYResource/map_1977.png
0 → 100644
279 Bytes
app/src/main/assets/KSYResource/nashville_map.png
0 → 100644
2.95 KB
app/src/main/assets/KSYResource/nashwi.png
0 → 100644
2.95 KB
app/src/main/assets/KSYResource/overlay_map.png
0 → 100644
6.3 KB
app/src/main/assets/KSYResource/rise_map.png
0 → 100644
2.98 KB
app/src/main/assets/KSYResource/sierra_map.png
0 → 100644
237 Bytes
app/src/main/assets/KSYResource/sierra_vignette.png
0 → 100644
69.9 KB
app/src/main/assets/KSYResource/soft_light.png
0 → 100644
5.69 KB
app/src/main/assets/KSYResource/sutro_curves.png
0 → 100644
2.84 KB
app/src/main/assets/KSYResource/sutro_edge_burn.png
0 → 100644
91.8 KB
app/src/main/assets/KSYResource/sutro_metal.png
0 → 100644
89.4 KB
app/src/main/assets/KSYResource/toaster_color_shift.png
0 → 100644
322 Bytes
app/src/main/assets/KSYResource/toaster_curves.png
0 → 100644
297 Bytes
app/src/main/assets/KSYResource/toaster_metal.png
0 → 100644
104 KB
app/src/main/assets/KSYResource/toaster_overlay_map_warm.png
0 → 100644
22.5 KB
app/src/main/assets/KSYResource/toaster_soft_light.png
0 → 100644
4.24 KB
app/src/main/assets/KSYResource/valencia_gradient_map.png
0 → 100644
12.4 KB
app/src/main/assets/KSYResource/valencia_map.png
0 → 100644
2.91 KB
app/src/main/assets/KSYResource/vignette_map.png
0 → 100644
7.51 KB
app/src/main/assets/KSYResource/walden_map.png
0 → 100644
3 KB
app/src/main/assets/KSYResource/xpro_map.png
0 → 100644
236 Bytes
app/src/main/assets/bg.jpg
0 → 100644
126 KB
app/src/main/assets/cnlive_logo.gif
0 → 100644
111 KB
app/src/main/java/com/cnlive/demo/stream/AnimatedImageCapture.java
0 → 100644
| 1 | +package com.cnlive.demo.stream; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.graphics.Bitmap; | ||
| 5 | +import android.graphics.Canvas; | ||
| 6 | +import android.graphics.Color; | ||
| 7 | +import android.graphics.Rect; | ||
| 8 | +import android.net.Uri; | ||
| 9 | +import android.util.Log; | ||
| 10 | + | ||
| 11 | +import com.cnlive.libs.stream.Streamer; | ||
| 12 | +import com.cnlive.libs.stream.capture.CNImgTexSrcPin; | ||
| 13 | +import com.facebook.common.executors.CallerThreadExecutor; | ||
| 14 | +import com.facebook.common.references.CloseableReference; | ||
| 15 | +import com.facebook.datasource.BaseDataSubscriber; | ||
| 16 | +import com.facebook.datasource.DataSource; | ||
| 17 | +import com.facebook.datasource.DataSubscriber; | ||
| 18 | +import com.facebook.drawee.backends.pipeline.Fresco; | ||
| 19 | +import com.facebook.imagepipeline.animated.base.AnimatedDrawableFrameInfo; | ||
| 20 | +import com.facebook.imagepipeline.animated.base.AnimatedImage; | ||
| 21 | +import com.facebook.imagepipeline.animated.base.AnimatedImageFrame; | ||
| 22 | +import com.facebook.imagepipeline.core.ImagePipeline; | ||
| 23 | +import com.facebook.imagepipeline.image.CloseableAnimatedImage; | ||
| 24 | +import com.facebook.imagepipeline.image.CloseableBitmap; | ||
| 25 | +import com.facebook.imagepipeline.image.CloseableImage; | ||
| 26 | +import com.facebook.imagepipeline.request.ImageRequest; | ||
| 27 | +import com.facebook.imagepipeline.request.ImageRequestBuilder; | ||
| 28 | + | ||
| 29 | +import java.util.Date; | ||
| 30 | +import java.util.Timer; | ||
| 31 | +import java.util.TimerTask; | ||
| 32 | +import java.util.stream.Stream; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * Created by Lynn on 2017/9/19. | ||
| 36 | + */ | ||
| 37 | + | ||
| 38 | +public class AnimatedImageCapture { | ||
| 39 | + private static final String TAG = "AnimatedImageCapture"; | ||
| 40 | + private static final boolean VERBOSE = false; | ||
| 41 | + | ||
| 42 | + private int mIndex; | ||
| 43 | + private Date mLastDate; | ||
| 44 | + private int mRepeatCount; // TODO: handle gif/webp repeat count | ||
| 45 | + private Bitmap mBitmap; | ||
| 46 | + private Canvas mCanvas; | ||
| 47 | + private Bitmap mTempBitmap; | ||
| 48 | + private Timer mTimer; | ||
| 49 | + private CloseableReference<? extends CloseableImage> mCloseableReference; | ||
| 50 | + | ||
| 51 | + private Streamer mStreamer; | ||
| 52 | + | ||
| 53 | + private CNImgTexSrcPin cnImgTexSrcPin; | ||
| 54 | + private Context context; | ||
| 55 | + | ||
| 56 | + public AnimatedImageCapture(Context context, Streamer streamer) { | ||
| 57 | + this.context = context; | ||
| 58 | + this.mStreamer = streamer; | ||
| 59 | + cnImgTexSrcPin = new CNImgTexSrcPin(streamer.getGLRender()); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + public void showAnimatedWaterMard(String url, float x, float y, float w, float h, float a) { | ||
| 63 | + cnImgTexSrcPin.connect(mStreamer.getImgTexPreviewMixer().getSinkPin(3)); | ||
| 64 | + cnImgTexSrcPin.connect(mStreamer.getImgTexMixer().getSinkPin(3)); | ||
| 65 | + mStreamer.getImgTexPreviewMixer().setRenderRect(3, x, y, w, h, a); | ||
| 66 | + mStreamer.getImgTexMixer().setRenderRect(3, x, y, w, h, a); | ||
| 67 | + start(context, url); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + public void hideAnimatedWaterMark() { | ||
| 71 | + stop(); | ||
| 72 | + cnImgTexSrcPin.disconnect(false); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + public void start(Context context, String url) { | ||
| 76 | + if (url == null) { | ||
| 77 | + return; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + Uri uri = Uri.parse(assets2Asset(url)); | ||
| 81 | + ImagePipeline imagePipeline = Fresco.getImagePipeline(); | ||
| 82 | + ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).build(); | ||
| 83 | + DataSource<CloseableReference<CloseableImage>> dataSource = | ||
| 84 | + imagePipeline.fetchDecodedImage(request, context); | ||
| 85 | + dataSource.subscribe(mDataSubscriber, CallerThreadExecutor.getInstance()); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + private String assets2Asset(String url) { | ||
| 89 | + if (url.startsWith("assets://")) { | ||
| 90 | + url = url.replaceFirst("assets://", "asset:///"); | ||
| 91 | + } | ||
| 92 | + return url; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + public void stop() { | ||
| 96 | + if (mTimer != null) { | ||
| 97 | + mTimer.cancel(); | ||
| 98 | + mTimer = null; | ||
| 99 | + } | ||
| 100 | + if (mCloseableReference != null) { | ||
| 101 | + CloseableReference.closeSafely(mCloseableReference); | ||
| 102 | + mCloseableReference = null; | ||
| 103 | + } | ||
| 104 | + cnImgTexSrcPin.updateFrame(null, false); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + private void updateFrame() { | ||
| 108 | + if (mCloseableReference == null) { | ||
| 109 | + return; | ||
| 110 | + } | ||
| 111 | + try { | ||
| 112 | + CloseableImage closeableImage = mCloseableReference.get(); | ||
| 113 | + if (closeableImage instanceof CloseableBitmap) { | ||
| 114 | + Bitmap bitmap = ((CloseableBitmap) closeableImage).getUnderlyingBitmap(); | ||
| 115 | + if (bitmap != null && !bitmap.isRecycled()) { | ||
| 116 | + mBitmap = Bitmap.createBitmap(bitmap); | ||
| 117 | + cnImgTexSrcPin.updateFrame(mBitmap, false); | ||
| 118 | + } | ||
| 119 | + return; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + if (!(closeableImage instanceof CloseableAnimatedImage)) { | ||
| 123 | + return; | ||
| 124 | + } | ||
| 125 | + AnimatedImage animatedImage = ((CloseableAnimatedImage) closeableImage).getImage(); | ||
| 126 | + int w = animatedImage.getWidth(); | ||
| 127 | + int h = animatedImage.getHeight(); | ||
| 128 | + if (mBitmap == null || mBitmap.isRecycled()) { | ||
| 129 | + Log.d(TAG, "Got animated image " + w + "x" + h); | ||
| 130 | + mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); | ||
| 131 | + mTempBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); | ||
| 132 | + mCanvas = new Canvas(mBitmap); | ||
| 133 | + } | ||
| 134 | + if (mIndex >= animatedImage.getFrameCount()) { | ||
| 135 | + mRepeatCount++; | ||
| 136 | + mIndex = 0; | ||
| 137 | + int loopCount = animatedImage.getLoopCount(); | ||
| 138 | + if (loopCount > 0 && mRepeatCount >= loopCount) { | ||
| 139 | + Log.d(TAG, "repeat ended, repeat times: " + mRepeatCount); | ||
| 140 | + return; | ||
| 141 | + } | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + AnimatedImageFrame imageFrame = animatedImage.getFrame(mIndex); | ||
| 145 | + int duration = imageFrame.getDurationMs(); | ||
| 146 | + AnimatedDrawableFrameInfo frameInfo = animatedImage.getFrameInfo(mIndex); | ||
| 147 | + imageFrame.renderFrame(frameInfo.width, frameInfo.height, mTempBitmap); | ||
| 148 | + if (VERBOSE) { | ||
| 149 | + Log.d(TAG, "blend: " + frameInfo.blendOperation.name() + | ||
| 150 | + " dispose: " + frameInfo.disposalMethod.name() + | ||
| 151 | + " x=" + frameInfo.xOffset + " y=" + frameInfo.yOffset + | ||
| 152 | + " " + frameInfo.width + "x" + frameInfo.height); | ||
| 153 | + } | ||
| 154 | + if (frameInfo.blendOperation == AnimatedDrawableFrameInfo.BlendOperation.NO_BLEND) { | ||
| 155 | + mBitmap.eraseColor(Color.TRANSPARENT); | ||
| 156 | + } | ||
| 157 | + Rect srcRect = new Rect(0, 0, frameInfo.width, frameInfo.height); | ||
| 158 | + Rect dstRect = new Rect(frameInfo.xOffset, frameInfo.yOffset, | ||
| 159 | + frameInfo.xOffset + frameInfo.width, frameInfo.yOffset + frameInfo.height); | ||
| 160 | + mCanvas.drawBitmap(mTempBitmap, srcRect, dstRect, null); | ||
| 161 | + if (VERBOSE) { | ||
| 162 | + Log.d(TAG, "frame " + mIndex + " got, duration=" + duration); | ||
| 163 | + } | ||
| 164 | + cnImgTexSrcPin.updateFrame(mBitmap, false); | ||
| 165 | + imageFrame.dispose(); | ||
| 166 | + | ||
| 167 | + mIndex++; | ||
| 168 | + if (mLastDate == null) { | ||
| 169 | + mLastDate = new Date(); | ||
| 170 | + } | ||
| 171 | + mLastDate = new Date(mLastDate.getTime() + duration); | ||
| 172 | + mTimer.schedule(new TimerTask() { | ||
| 173 | + @Override | ||
| 174 | + public void run() { | ||
| 175 | + updateFrame(); | ||
| 176 | + } | ||
| 177 | + }, mLastDate); | ||
| 178 | + } catch (Exception e) { | ||
| 179 | + e.printStackTrace(); | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + private DataSubscriber mDataSubscriber = | ||
| 184 | + new BaseDataSubscriber<CloseableReference<? extends CloseableImage>>() { | ||
| 185 | + | ||
| 186 | + @Override | ||
| 187 | + public void onNewResultImpl( | ||
| 188 | + DataSource<CloseableReference<? extends CloseableImage>> dataSource) { | ||
| 189 | + if (!dataSource.isFinished()) { | ||
| 190 | + return; | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + // get ref | ||
| 194 | + mCloseableReference = dataSource.getResult(); | ||
| 195 | + | ||
| 196 | + // reset | ||
| 197 | + mIndex = 0; | ||
| 198 | + mRepeatCount = 0; | ||
| 199 | + mLastDate = null; | ||
| 200 | + mBitmap = null; | ||
| 201 | + mTempBitmap = null; | ||
| 202 | + mCanvas = null; | ||
| 203 | + | ||
| 204 | + // create timer | ||
| 205 | + mTimer = new Timer("AnimatedImage"); | ||
| 206 | + mTimer.schedule(new TimerTask() { | ||
| 207 | + @Override | ||
| 208 | + public void run() { | ||
| 209 | + updateFrame(); | ||
| 210 | + } | ||
| 211 | + }, 0); | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + @Override | ||
| 215 | + public void onFailureImpl(DataSource dataSource) { | ||
| 216 | + Throwable throwable = dataSource.getFailureCause(); | ||
| 217 | + // handle failure | ||
| 218 | + if (throwable != null) { | ||
| 219 | + throwable.printStackTrace(); | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + }; | ||
| 223 | +} |
app/src/main/java/com/cnlive/demo/stream/AudioActivity.java
| @@ -15,9 +15,15 @@ import android.widget.Toast; | @@ -15,9 +15,15 @@ import android.widget.Toast; | ||
| 15 | 15 | ||
| 16 | import com.cnlive.libs.stream.Streamer; | 16 | import com.cnlive.libs.stream.Streamer; |
| 17 | import com.cnlive.libs.stream.base.IStreamer; | 17 | import com.cnlive.libs.stream.base.IStreamer; |
| 18 | +import com.cnlive.libs.stream.filter.CNAudioFilterBase; | ||
| 19 | +import com.cnlive.libs.stream.filter.CNAudioReverbFilter; | ||
| 18 | import com.cnlive.libs.stream.model.ActivityConfig; | 20 | import com.cnlive.libs.stream.model.ActivityConfig; |
| 19 | 21 | ||
| 20 | 22 | ||
| 23 | +import java.util.LinkedList; | ||
| 24 | +import java.util.List; | ||
| 25 | + | ||
| 26 | +import static com.cnlive.libs.stream.base.IStreamer.AUDIO_REVERB_LEVEL_1; | ||
| 21 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_HARDWARE; | 27 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_HARDWARE; |
| 22 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT; | 28 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT; |
| 23 | import static com.cnlive.libs.stream.base.IStreamer.VIDEO_RESOLUTION_360P; | 29 | import static com.cnlive.libs.stream.base.IStreamer.VIDEO_RESOLUTION_360P; |
| @@ -434,18 +440,30 @@ public class AudioActivity extends AppCompatActivity implements View.OnClickList | @@ -434,18 +440,30 @@ public class AudioActivity extends AppCompatActivity implements View.OnClickList | ||
| 434 | }).setPositiveButton("确定", new DialogInterface.OnClickListener() { | 440 | }).setPositiveButton("确定", new DialogInterface.OnClickListener() { |
| 435 | @Override | 441 | @Override |
| 436 | public void onClick(DialogInterface dialogInterface, int which) { | 442 | public void onClick(DialogInterface dialogInterface, int which) { |
| 443 | + List<CNAudioFilterBase> filterBases = new LinkedList<CNAudioFilterBase>(); | ||
| 437 | switch (which) { | 444 | switch (which) { |
| 438 | case 0: | 445 | case 0: |
| 439 | - mStreamer.setAudioFilter(IStreamer.AUDIO_REVERB_LEVEL_1); | 446 | + CNAudioReverbFilter reverbFilter = new CNAudioReverbFilter(); |
| 447 | + reverbFilter.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_1); | ||
| 448 | + filterBases.add(reverbFilter); | ||
| 440 | break; | 449 | break; |
| 441 | case 1: | 450 | case 1: |
| 442 | - mStreamer.setAudioFilter(IStreamer.AUDIO_REVERB_LEVEL_2); | 451 | + CNAudioReverbFilter reverbFilter1 = new CNAudioReverbFilter(); |
| 452 | + reverbFilter1.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_2); | ||
| 453 | + filterBases.add(reverbFilter1); | ||
| 443 | break; | 454 | break; |
| 444 | case 2: | 455 | case 2: |
| 445 | - mStreamer.setAudioFilter(IStreamer.AUDIO_REVERB_LEVEL_3); | 456 | + CNAudioReverbFilter reverbFilter2 = new CNAudioReverbFilter(); |
| 457 | + reverbFilter2.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_3); | ||
| 458 | + filterBases.add(reverbFilter2); | ||
| 446 | break; | 459 | break; |
| 447 | case 3: | 460 | case 3: |
| 448 | - mStreamer.setAudioFilter(IStreamer.AUDIO_REVERB_LEVEL_4); | 461 | + CNAudioReverbFilter reverbFilter3 = new CNAudioReverbFilter(); |
| 462 | + reverbFilter3.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_4); | ||
| 463 | + filterBases.add(reverbFilter3); | ||
| 464 | + break; | ||
| 465 | + default: | ||
| 466 | + mStreamer.getAudioFilterMgt().setFilter(filterBases); | ||
| 449 | break; | 467 | break; |
| 450 | } | 468 | } |
| 451 | } | 469 | } |
app/src/main/java/com/cnlive/demo/stream/CNGlobalStreamer.java
0 → 100644
| 1 | +package com.cnlive.demo.stream; | ||
| 2 | + | ||
| 3 | +import android.annotation.SuppressLint; | ||
| 4 | + | ||
| 5 | +import com.cnlive.libs.stream.Streamer; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Created by Lynn on 2017/9/19. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +public class CNGlobalStreamer { | ||
| 12 | + | ||
| 13 | + @SuppressLint("StaticFieldLeak") | ||
| 14 | + private static Streamer sStreamer; | ||
| 15 | + | ||
| 16 | + private CNGlobalStreamer() { | ||
| 17 | + | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + public static void setInstance(Streamer streamer) { | ||
| 21 | + sStreamer = streamer; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + public static Streamer getInstance() { | ||
| 25 | + return sStreamer; | ||
| 26 | + } | ||
| 27 | +} |
app/src/main/java/com/cnlive/demo/stream/CNStreamApplication.java
| @@ -3,6 +3,7 @@ package com.cnlive.demo.stream; | @@ -3,6 +3,7 @@ package com.cnlive.demo.stream; | ||
| 3 | import android.app.Application; | 3 | import android.app.Application; |
| 4 | 4 | ||
| 5 | import com.cnlive.libs.util.Config; | 5 | import com.cnlive.libs.util.Config; |
| 6 | +import com.facebook.drawee.backends.pipeline.Fresco; | ||
| 6 | 7 | ||
| 7 | /** | 8 | /** |
| 8 | * Created by xiansong on 2016/7/22. | 9 | * Created by xiansong on 2016/7/22. |
| @@ -14,5 +15,8 @@ public class CNStreamApplication extends Application { | @@ -14,5 +15,8 @@ public class CNStreamApplication extends Application { | ||
| 14 | super.onCreate(); | 15 | super.onCreate(); |
| 15 | 16 | ||
| 16 | Config.init(this, "60_irn9gh9d49", "f7b6db55616d207fd5c07e2b580b5650c690aff58fb36d",true); | 17 | Config.init(this, "60_irn9gh9d49", "f7b6db55616d207fd5c07e2b580b5650c690aff58fb36d",true); |
| 18 | + | ||
| 19 | + // 初始化fresco库,用来支持动态水印功能 | ||
| 20 | + Fresco.initialize(this); | ||
| 17 | } | 21 | } |
| 18 | } | 22 | } |
app/src/main/java/com/cnlive/demo/stream/FloatActivity.java
0 → 100644
| 1 | +package com.cnlive.demo.stream; | ||
| 2 | + | ||
| 3 | +import android.app.Activity; | ||
| 4 | +import android.app.Application; | ||
| 5 | +import android.content.Intent; | ||
| 6 | +import android.graphics.Color; | ||
| 7 | +import android.graphics.PixelFormat; | ||
| 8 | +import android.hardware.SensorManager; | ||
| 9 | +import android.net.Uri; | ||
| 10 | +import android.opengl.GLSurfaceView; | ||
| 11 | +import android.os.Build; | ||
| 12 | +import android.provider.Settings; | ||
| 13 | +import android.support.annotation.Nullable; | ||
| 14 | +import android.support.v7.app.AppCompatActivity; | ||
| 15 | +import android.os.Bundle; | ||
| 16 | +import android.util.Log; | ||
| 17 | +import android.view.Gravity; | ||
| 18 | +import android.view.KeyEvent; | ||
| 19 | +import android.view.LayoutInflater; | ||
| 20 | +import android.view.OrientationEventListener; | ||
| 21 | +import android.view.Surface; | ||
| 22 | +import android.view.View; | ||
| 23 | +import android.view.Window; | ||
| 24 | +import android.view.WindowManager; | ||
| 25 | +import android.widget.ImageView; | ||
| 26 | +import android.widget.RelativeLayout; | ||
| 27 | +import android.widget.Toast; | ||
| 28 | + | ||
| 29 | +import com.cnlive.libs.stream.Streamer; | ||
| 30 | + | ||
| 31 | +public class FloatActivity extends Activity { | ||
| 32 | + | ||
| 33 | + private static final String TAG = "FloatViewActivity"; | ||
| 34 | + | ||
| 35 | + private final static int OVERLAY_PERMISSION_RESULT_CODE = 10; | ||
| 36 | + | ||
| 37 | + private GLSurfaceView mCameraView; | ||
| 38 | + private int mLastRotation; | ||
| 39 | + private OrientationEventListener mOrientationEventListener; | ||
| 40 | + | ||
| 41 | + //preview window just demo | ||
| 42 | + private FloatView mFloatLayout; //悬浮窗口的layout, 可以是xml,也可以在代码中创建 | ||
| 43 | + private ImageView mFloatClose; //关闭悬浮窗口 | ||
| 44 | + private ImageView mFloatBack; //跳转到上一个页面,不停止推流 | ||
| 45 | + private WindowManager.LayoutParams mWmParams; //layout的布局 | ||
| 46 | + private WindowManager mWindowManager; | ||
| 47 | + private volatile boolean mPreviewWindowShow = false; //悬浮摄像头预览window是否正在显示 | ||
| 48 | + | ||
| 49 | + //ButtonListener | ||
| 50 | + private ButtonObserver mObserverButton; //所有按键响应 | ||
| 51 | + | ||
| 52 | + // logo path | ||
| 53 | + private String mLogoPath = "file:///sdcard/test.png"; | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + protected void onCreate(@Nullable Bundle savedInstanceState) { | ||
| 57 | + Log.d(TAG, "onCreate"); | ||
| 58 | + super.onCreate(savedInstanceState); | ||
| 59 | + requestWindowFeature(Window.FEATURE_NO_TITLE); | ||
| 60 | + setContentView(R.layout.activity_float); | ||
| 61 | + mObserverButton = new ButtonObserver(); | ||
| 62 | + mLastRotation = getDisplayRotation(); | ||
| 63 | + mFloatBack = (ImageView) findViewById(R.id.float_back); | ||
| 64 | + mFloatBack.setOnClickListener(mObserverButton); | ||
| 65 | + | ||
| 66 | + mOrientationEventListener = new OrientationEventListener(this, | ||
| 67 | + SensorManager.SENSOR_DELAY_NORMAL) { | ||
| 68 | + @Override | ||
| 69 | + public void onOrientationChanged(int orientation) { | ||
| 70 | + int rotation = getDisplayRotation(); | ||
| 71 | + if (rotation != mLastRotation) { | ||
| 72 | + Log.d(TAG, "Rotation changed " + mLastRotation + "->" + rotation); | ||
| 73 | + onSwitchRotate(); | ||
| 74 | + mLastRotation = rotation; | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + }; | ||
| 78 | + if (mOrientationEventListener.canDetectOrientation()) { | ||
| 79 | + mOrientationEventListener.enable(); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + addFloatViewWithPermCheck(); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @Override | ||
| 86 | + protected void onResume() { | ||
| 87 | + Log.d(TAG, "onResume"); | ||
| 88 | + super.onResume(); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + @Override | ||
| 92 | + protected void onPause() { | ||
| 93 | + Log.d(TAG, "onPause"); | ||
| 94 | + super.onPause(); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + @Override | ||
| 98 | + protected void onDestroy() { | ||
| 99 | + Log.d(TAG, "onDestroy"); | ||
| 100 | + super.onDestroy(); | ||
| 101 | + if (mOrientationEventListener != null) { | ||
| 102 | + mOrientationEventListener.disable(); | ||
| 103 | + } | ||
| 104 | + removeSurfaceWindow(); | ||
| 105 | + CNGlobalStreamer.setInstance(null); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Override | ||
| 109 | + public boolean onKeyDown(int keyCode, KeyEvent event) { | ||
| 110 | + switch (keyCode) { | ||
| 111 | + case KeyEvent.KEYCODE_BACK: | ||
| 112 | + onBackoffClick(); | ||
| 113 | + return true; | ||
| 114 | + default: | ||
| 115 | + break; | ||
| 116 | + } | ||
| 117 | + return super.onKeyDown(keyCode, event); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + private void onBackoffClick() { | ||
| 121 | + CNGlobalStreamer.getInstance().pause(); | ||
| 122 | + CNGlobalStreamer.getInstance().setDisplayPreview((GLSurfaceView) null); | ||
| 123 | + FloatActivity.this.finish(); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + /** | ||
| 127 | + * 初始化悬浮窗口示例 | ||
| 128 | + */ | ||
| 129 | + private void initSurfaceWindow() { | ||
| 130 | + if (mWindowManager == null) { | ||
| 131 | + mWmParams = new WindowManager.LayoutParams(); | ||
| 132 | + mWindowManager = (WindowManager) getApplication(). | ||
| 133 | + getSystemService(Application.WINDOW_SERVICE); | ||
| 134 | + | ||
| 135 | + //设置window type | ||
| 136 | + mWmParams.type = WindowManager.LayoutParams.TYPE_TOAST; | ||
| 137 | + | ||
| 138 | + //设置图片格式,效果为背景透明 | ||
| 139 | + mWmParams.format = PixelFormat.RGBA_8888; | ||
| 140 | + | ||
| 141 | + //设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作) | ||
| 142 | + mWmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; | ||
| 143 | + //开启硬件加速,以支持TextureView | ||
| 144 | + mWmParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; | ||
| 145 | + //接收touch事件 | ||
| 146 | + mWmParams.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; | ||
| 147 | + //排版不受限制 | ||
| 148 | + mWmParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; | ||
| 149 | + //调整悬浮窗显示的停靠位置为右侧底部 | ||
| 150 | + mWmParams.gravity = Gravity.RIGHT | Gravity.BOTTOM; | ||
| 151 | + | ||
| 152 | + // 以屏幕左上角为原点,设置x、y初始值,相对于gravity | ||
| 153 | + mWmParams.x = 0; | ||
| 154 | + mWmParams.y = 0; | ||
| 155 | + | ||
| 156 | + //设置悬浮窗口长宽数据(这里取屏幕长宽的等比率缩小值作为悬浮窗口的长宽) | ||
| 157 | + int screenWidth = mWindowManager.getDefaultDisplay().getWidth(); | ||
| 158 | + int screenHeight = mWindowManager.getDefaultDisplay().getHeight(); | ||
| 159 | + int width; | ||
| 160 | + int height; | ||
| 161 | + | ||
| 162 | + boolean isLandscape = (getDisplayRotation() % 180) != 0; | ||
| 163 | + if ((isLandscape && screenWidth < screenHeight) || | ||
| 164 | + (!isLandscape) && screenWidth > screenHeight) { | ||
| 165 | + screenWidth = mWindowManager.getDefaultDisplay().getHeight(); | ||
| 166 | + screenHeight = mWindowManager.getDefaultDisplay().getWidth(); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + if (screenWidth < screenHeight) { | ||
| 170 | + width = align(screenWidth / 3, 8); | ||
| 171 | + height = align(screenHeight / 3, 8); | ||
| 172 | + } else { | ||
| 173 | + width = align(screenWidth / 3, 8); | ||
| 174 | + height = align(screenHeight / 3, 8); | ||
| 175 | + } | ||
| 176 | + mWmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; | ||
| 177 | + mWmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; | ||
| 178 | + | ||
| 179 | + LayoutInflater inflater = LayoutInflater.from(getApplication()); | ||
| 180 | + | ||
| 181 | + //获取浮动窗口视图所在布局 | ||
| 182 | + mFloatLayout = (FloatView) inflater.inflate(R.layout.float_preview, null); | ||
| 183 | + | ||
| 184 | + mFloatClose = new ImageView(this); | ||
| 185 | + mFloatClose.setImageResource(R.drawable.float_close); | ||
| 186 | + mFloatClose.setOnClickListener(new View.OnClickListener() { | ||
| 187 | + @Override | ||
| 188 | + public void onClick(View v) { | ||
| 189 | + onBackoffClick(); | ||
| 190 | + } | ||
| 191 | + }); | ||
| 192 | + | ||
| 193 | + mCameraView = new GLSurfaceView(this); | ||
| 194 | + CNGlobalStreamer.getInstance().setDisplayPreview(mCameraView); | ||
| 195 | + RelativeLayout.LayoutParams previewLayoutParams = | ||
| 196 | + new RelativeLayout.LayoutParams(width, height); | ||
| 197 | + mFloatLayout.addView(mCameraView, previewLayoutParams); | ||
| 198 | + RelativeLayout.LayoutParams closeParams = | ||
| 199 | + new RelativeLayout.LayoutParams(50, 50); | ||
| 200 | + closeParams.setMargins(10,10,0,0); | ||
| 201 | + mFloatLayout.addView(mFloatClose, closeParams); | ||
| 202 | + | ||
| 203 | + mFloatLayout.setWmParams(mWmParams); | ||
| 204 | + } | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + private class ButtonObserver implements View.OnClickListener { | ||
| 208 | + @Override | ||
| 209 | + public void onClick(View view) { | ||
| 210 | + switch (view.getId()) { | ||
| 211 | + case R.id.float_back: | ||
| 212 | + onBackoffClick(); | ||
| 213 | + break; | ||
| 214 | + default: | ||
| 215 | + break; | ||
| 216 | + } | ||
| 217 | + } | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + private void addSurfaceWindow() { | ||
| 221 | + if (mWindowManager != null) { | ||
| 222 | + //添加mFloatLayout | ||
| 223 | + mWindowManager.addView(mFloatLayout, mWmParams); | ||
| 224 | + } | ||
| 225 | + mPreviewWindowShow = true; | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + private void removeSurfaceWindow() { | ||
| 229 | + if (mWindowManager != null) { | ||
| 230 | + mWindowManager.removeViewImmediate(mFloatLayout); | ||
| 231 | + } | ||
| 232 | + mPreviewWindowShow = false; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + private void updateViewPosition() { | ||
| 236 | + if (mWmParams != null && mWindowManager != null) { | ||
| 237 | + mWmParams.gravity = Gravity.RIGHT | Gravity.BOTTOM; | ||
| 238 | + | ||
| 239 | + // 以屏幕左上角为原点,设置x、y初始值,相对于gravity | ||
| 240 | + mWmParams.x = 0; | ||
| 241 | + mWmParams.y = 0; | ||
| 242 | + mWindowManager.updateViewLayout(mFloatLayout, mWmParams); //刷新显示 | ||
| 243 | + } | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + private int align(int val, int align) { | ||
| 247 | + return (val + align - 1) / align * align; | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + private int getDisplayRotation() { | ||
| 251 | + int rotation = getWindowManager().getDefaultDisplay().getRotation(); | ||
| 252 | + switch (rotation) { | ||
| 253 | + case Surface.ROTATION_0: | ||
| 254 | + return 0; | ||
| 255 | + case Surface.ROTATION_90: | ||
| 256 | + return 90; | ||
| 257 | + case Surface.ROTATION_180: | ||
| 258 | + return 180; | ||
| 259 | + case Surface.ROTATION_270: | ||
| 260 | + return 270; | ||
| 261 | + } | ||
| 262 | + return 0; | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + private void addFloatViewWithPermCheck() { | ||
| 266 | + // 6.0 需要检查overlay权限 | ||
| 267 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { | ||
| 268 | + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, | ||
| 269 | + Uri.parse("package:" + getPackageName())); | ||
| 270 | + startActivityForResult(intent, OVERLAY_PERMISSION_RESULT_CODE); | ||
| 271 | + } else { | ||
| 272 | + initSurfaceWindow(); | ||
| 273 | + addSurfaceWindow(); | ||
| 274 | + } | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + /** | ||
| 278 | + * 申请overlay权限窗口返回 | ||
| 279 | + * | ||
| 280 | + * @param requestCode | ||
| 281 | + * @param resultCode | ||
| 282 | + * @param data | ||
| 283 | + */ | ||
| 284 | + @Override | ||
| 285 | + protected void onActivityResult(int requestCode, int resultCode, Intent data) { | ||
| 286 | + if (requestCode == OVERLAY_PERMISSION_RESULT_CODE) { | ||
| 287 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
| 288 | + if (!Settings.canDrawOverlays(this)) { | ||
| 289 | + // SYSTEM_ALERT_WINDOW permission not granted... | ||
| 290 | + Toast.makeText(FloatActivity.this, "SYSTEM_ALERT_WINDOW not granted", | ||
| 291 | + Toast.LENGTH_SHORT).show(); | ||
| 292 | + } else { | ||
| 293 | + initSurfaceWindow(); | ||
| 294 | + addSurfaceWindow(); | ||
| 295 | + } | ||
| 296 | + } | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + /** | ||
| 301 | + * 旋转摄像头预览示例代码 | ||
| 302 | + */ | ||
| 303 | + private void onSwitchRotate() { | ||
| 304 | + if (mPreviewWindowShow) { | ||
| 305 | + int rotation = getDisplayRotation(); | ||
| 306 | + Log.i(TAG, "onSwitchRotate: " + mLastRotation + "->" + rotation); | ||
| 307 | + CNGlobalStreamer.getInstance().setRotateDegrees(rotation); | ||
| 308 | + | ||
| 309 | + boolean isLastLandscape = (mLastRotation % 180) != 0; | ||
| 310 | + boolean isLandscape = (rotation % 180) != 0; | ||
| 311 | + if (isLastLandscape != isLandscape) { | ||
| 312 | + int width = mCameraView.getHeight(); | ||
| 313 | + int height = mCameraView.getWidth(); | ||
| 314 | + RelativeLayout.LayoutParams previewLayoutParams = | ||
| 315 | + (RelativeLayout.LayoutParams) mCameraView.getLayoutParams(); | ||
| 316 | + previewLayoutParams.width = width; | ||
| 317 | + previewLayoutParams.height = height; | ||
| 318 | + //更新CameraPreview布局 | ||
| 319 | + mFloatLayout.updateViewLayout(mCameraView, previewLayoutParams); | ||
| 320 | + | ||
| 321 | + updateViewPosition(); | ||
| 322 | + updateWaterMark(isLandscape); | ||
| 323 | + } | ||
| 324 | + } | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + // update water mark position while screen orientation changed | ||
| 328 | + private void updateWaterMark(boolean isLandscape) { | ||
| 329 | + Streamer streamer = CNGlobalStreamer.getInstance(); | ||
| 330 | + streamer.hideWaterMarkLogo(); | ||
| 331 | + streamer.hideWaterMarkTime(); | ||
| 332 | + if (!isLandscape) { | ||
| 333 | + streamer.showWaterMarkLogo(mLogoPath, 0.08f, 0.04f, 0.20f, 0, 0.8f); | ||
| 334 | + streamer.showWaterMarkTime(0.03f, 0.01f, 0.35f, Color.WHITE, 1.0f); | ||
| 335 | + } else { | ||
| 336 | + streamer.showWaterMarkLogo(mLogoPath, 0.05f, 0.09f, 0, 0.20f, 0.8f); | ||
| 337 | + streamer.showWaterMarkTime(0.01f, 0.03f, 0.22f, Color.WHITE, 1.0f); | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | +} |
app/src/main/java/com/cnlive/demo/stream/FloatView.java
0 → 100644
| 1 | +package com.cnlive.demo.stream; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.util.AttributeSet; | ||
| 5 | +import android.view.Gravity; | ||
| 6 | +import android.view.MotionEvent; | ||
| 7 | +import android.view.WindowManager; | ||
| 8 | +import android.widget.RelativeLayout; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * Created by Lynn on 2017/9/19. | ||
| 12 | + */ | ||
| 13 | + | ||
| 14 | +public class FloatView extends RelativeLayout { | ||
| 15 | + private float mTouchStartX; | ||
| 16 | + private float mTouchStartY; | ||
| 17 | + private float mLastX; | ||
| 18 | + private float mLastY; | ||
| 19 | + private float mMaxX = 0; | ||
| 20 | + private float mMaxY = 0; | ||
| 21 | + | ||
| 22 | + private WindowManager mWindowManager; | ||
| 23 | + private WindowManager.LayoutParams mWmParams; | ||
| 24 | + | ||
| 25 | + public FloatView(Context context) { | ||
| 26 | + super(context); | ||
| 27 | + init(); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public FloatView(Context context, AttributeSet attrs) { | ||
| 31 | + super(context, attrs); | ||
| 32 | + init(); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public FloatView(Context context, AttributeSet attrs, int defStyleAttr) { | ||
| 36 | + super(context, attrs, defStyleAttr); | ||
| 37 | + init(); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + public void setWmParams(WindowManager.LayoutParams params) { | ||
| 41 | + mWmParams = params; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Override | ||
| 45 | + public boolean onTouchEvent(MotionEvent event) { | ||
| 46 | + //获取相对屏幕的坐标,即以屏幕左上角为原点 | ||
| 47 | + mLastX = event.getRawX(); | ||
| 48 | + mLastY = event.getRawY(); | ||
| 49 | + | ||
| 50 | + switch (event.getAction()) { | ||
| 51 | + case MotionEvent.ACTION_DOWN: | ||
| 52 | + //获取相对View的坐标,即以此View左上角为原点 | ||
| 53 | + mTouchStartX = event.getX(); | ||
| 54 | + mTouchStartY = event.getY(); | ||
| 55 | + break; | ||
| 56 | + case MotionEvent.ACTION_MOVE: | ||
| 57 | + updateViewPosition(); | ||
| 58 | + break; | ||
| 59 | + case MotionEvent.ACTION_UP: | ||
| 60 | + mTouchStartX = 0; | ||
| 61 | + mTouchStartY = 0; | ||
| 62 | + break; | ||
| 63 | + } | ||
| 64 | + return true; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + private void init() { | ||
| 68 | + mWindowManager = (WindowManager) getContext().getApplicationContext() | ||
| 69 | + .getSystemService(Context.WINDOW_SERVICE); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + private void updateViewPosition() { | ||
| 73 | + if (mWmParams != null) { | ||
| 74 | + mMaxX = mWindowManager.getDefaultDisplay().getWidth() - this.getWidth(); | ||
| 75 | + mMaxY = mWindowManager.getDefaultDisplay().getHeight() - this.getHeight(); | ||
| 76 | + | ||
| 77 | + //更新浮动窗口位置参数 | ||
| 78 | + float newX = (mLastX - mTouchStartX); | ||
| 79 | + float newY = (mLastY - mTouchStartY); | ||
| 80 | + //以屏幕左上角为原点 | ||
| 81 | + mWmParams.gravity = Gravity.LEFT | Gravity.TOP; | ||
| 82 | + | ||
| 83 | + //不能移除屏幕最左边和最上边 | ||
| 84 | + if (newX < 0) { | ||
| 85 | + newX = 0; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + if (newY < 0) { | ||
| 89 | + newY = 0; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + //不能移出屏幕最右边和最下边 | ||
| 93 | + if (newX > mMaxX) { | ||
| 94 | + newX = mMaxX; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + if (newY > mMaxY) { | ||
| 98 | + newY = mMaxY; | ||
| 99 | + } | ||
| 100 | + mWmParams.x = (int) newX; | ||
| 101 | + mWmParams.y = (int) newY; | ||
| 102 | + mWindowManager.updateViewLayout(this, mWmParams); //刷新显示 | ||
| 103 | + | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | +} |
app/src/main/java/com/cnlive/demo/stream/StreamActivity.java
| @@ -8,7 +8,9 @@ import android.content.DialogInterface; | @@ -8,7 +8,9 @@ import android.content.DialogInterface; | ||
| 8 | import android.content.Intent; | 8 | import android.content.Intent; |
| 9 | import android.content.pm.ActivityInfo; | 9 | import android.content.pm.ActivityInfo; |
| 10 | import android.content.pm.PackageManager; | 10 | import android.content.pm.PackageManager; |
| 11 | +import android.graphics.Bitmap; | ||
| 11 | import android.graphics.Color; | 12 | import android.graphics.Color; |
| 13 | +import android.hardware.Camera; | ||
| 12 | import android.opengl.GLSurfaceView; | 14 | import android.opengl.GLSurfaceView; |
| 13 | import android.os.Build; | 15 | import android.os.Build; |
| 14 | import android.os.Bundle; | 16 | import android.os.Bundle; |
| @@ -23,35 +25,42 @@ import android.view.Window; | @@ -23,35 +25,42 @@ import android.view.Window; | ||
| 23 | import android.view.WindowManager; | 25 | import android.view.WindowManager; |
| 24 | import android.widget.AdapterView; | 26 | import android.widget.AdapterView; |
| 25 | import android.widget.ArrayAdapter; | 27 | import android.widget.ArrayAdapter; |
| 28 | +import android.widget.Button; | ||
| 26 | import android.widget.CheckBox; | 29 | import android.widget.CheckBox; |
| 27 | import android.widget.Chronometer; | 30 | import android.widget.Chronometer; |
| 28 | import android.widget.CompoundButton; | 31 | import android.widget.CompoundButton; |
| 32 | +import android.widget.ImageView; | ||
| 29 | import android.widget.LinearLayout; | 33 | import android.widget.LinearLayout; |
| 30 | import android.widget.SeekBar; | 34 | import android.widget.SeekBar; |
| 31 | import android.widget.TextView; | 35 | import android.widget.TextView; |
| 32 | import android.widget.Toast; | 36 | import android.widget.Toast; |
| 33 | 37 | ||
| 34 | import com.cnlive.libs.stream.Streamer; | 38 | import com.cnlive.libs.stream.Streamer; |
| 39 | +import com.cnlive.libs.stream.base.ICNAudioEffectFilter; | ||
| 35 | import com.cnlive.libs.stream.base.IStreamer; | 40 | import com.cnlive.libs.stream.base.IStreamer; |
| 36 | import com.cnlive.libs.stream.base.ImgBeautyProFilter; | 41 | import com.cnlive.libs.stream.base.ImgBeautyProFilter; |
| 37 | import com.cnlive.libs.stream.base.ImgFilterBase; | 42 | import com.cnlive.libs.stream.base.ImgFilterBase; |
| 43 | +import com.cnlive.libs.stream.filter.CNAudioEffectFilter; | ||
| 44 | +import com.cnlive.libs.stream.filter.CNAudioFilterBase; | ||
| 45 | +import com.cnlive.libs.stream.filter.CNAudioReverbFilter; | ||
| 38 | import com.cnlive.libs.stream.model.ActivityConfig; | 46 | import com.cnlive.libs.stream.model.ActivityConfig; |
| 39 | -import com.ksyun.media.player.IMediaPlayer; | ||
| 40 | 47 | ||
| 48 | +import java.io.BufferedOutputStream; | ||
| 49 | +import java.io.FileNotFoundException; | ||
| 50 | +import java.io.FileOutputStream; | ||
| 51 | +import java.io.IOException; | ||
| 52 | +import java.text.SimpleDateFormat; | ||
| 53 | +import java.util.Date; | ||
| 54 | +import java.util.LinkedList; | ||
| 41 | import java.util.List; | 55 | import java.util.List; |
| 42 | 56 | ||
| 43 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_HARDWARE; | 57 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_HARDWARE; |
| 44 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT; | 58 | import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT; |
| 59 | +import static com.cnlive.libs.stream.base.IStreamer.ENCODE_PROFILE_BALANCE; | ||
| 60 | +import static com.cnlive.libs.stream.base.IStreamer.ENCODE_PROFILE_HIGH_PERFORMANCE; | ||
| 61 | +import static com.cnlive.libs.stream.base.IStreamer.ENCODE_PROFILE_LOW_POWER; | ||
| 45 | import static com.cnlive.libs.stream.base.IStreamer.VIDEO_RESOLUTION_360P; | 62 | import static com.cnlive.libs.stream.base.IStreamer.VIDEO_RESOLUTION_360P; |
| 46 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_DISABLE; | ||
| 47 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_ILLUSION; | ||
| 48 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_PRO; | ||
| 49 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_SMOOTH; | ||
| 50 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_SOFT; | ||
| 51 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_SOFT_EXT; | ||
| 52 | -import static com.cnlive.libs.stream.model.Filters.BEAUTY_SOFT_SHARPEN; | ||
| 53 | -import static com.cnlive.libs.stream.model.Filters.DENOISE; | ||
| 54 | -import static com.cnlive.libs.stream.model.Filters.SKIN_WHITEN; | 63 | +import static com.cnlive.libs.stream.model.Filters.*; |
| 55 | 64 | ||
| 56 | /** | 65 | /** |
| 57 | * Created by Administrator on 2016/8/29. | 66 | * Created by Administrator on 2016/8/29. |
| @@ -66,6 +75,8 @@ public class StreamActivity extends Activity { | @@ -66,6 +75,8 @@ public class StreamActivity extends Activity { | ||
| 66 | private View mDeleteView; | 75 | private View mDeleteView; |
| 67 | private View mSwitchCameraView; | 76 | private View mSwitchCameraView; |
| 68 | private View mFlashView; | 77 | private View mFlashView; |
| 78 | + private ImageView mExposureView; | ||
| 79 | + private ImageView mAddView; | ||
| 69 | private TextView mShootingText; | 80 | private TextView mShootingText; |
| 70 | private CheckBox mWaterMarkCheckBox; | 81 | private CheckBox mWaterMarkCheckBox; |
| 71 | private CheckBox mBeautyCheckBox; | 82 | private CheckBox mBeautyCheckBox; |
| @@ -74,6 +85,12 @@ public class StreamActivity extends Activity { | @@ -74,6 +85,12 @@ public class StreamActivity extends Activity { | ||
| 74 | private CheckBox mMuteCheckBox; | 85 | private CheckBox mMuteCheckBox; |
| 75 | private CheckBox mAudioOnlyCheckBox; | 86 | private CheckBox mAudioOnlyCheckBox; |
| 76 | private CheckBox mFrontMirrorCheckBox; | 87 | private CheckBox mFrontMirrorCheckBox; |
| 88 | + private CheckBox mBgImg; | ||
| 89 | + private Button mAudioMix; | ||
| 90 | + private Button mBgmFilter; | ||
| 91 | + private Button mCaptureScreen; | ||
| 92 | + | ||
| 93 | + private VerticalSeekBar mExposureSeekBar; | ||
| 77 | 94 | ||
| 78 | private ButtonObserver mObserverButton; | 95 | private ButtonObserver mObserverButton; |
| 79 | private CheckBoxObserver mCheckBoxObserver; | 96 | private CheckBoxObserver mCheckBoxObserver; |
| @@ -86,12 +103,16 @@ public class StreamActivity extends Activity { | @@ -86,12 +103,16 @@ public class StreamActivity extends Activity { | ||
| 86 | private boolean isFlashOpened = false; | 103 | private boolean isFlashOpened = false; |
| 87 | private String mBgmPath = "/sdcard/test.mp3"; | 104 | private String mBgmPath = "/sdcard/test.mp3"; |
| 88 | private String mLogoPath = "file:///sdcard/cnlive_logo.png"; | 105 | private String mLogoPath = "file:///sdcard/cnlive_logo.png"; |
| 106 | + private String mAnimatedLogoPath = "assets://cnlive_logo.gif"; | ||
| 107 | + private String mBgImagePath = "assets://bg.jpg"; | ||
| 89 | 108 | ||
| 90 | private String activityId; | 109 | private String activityId; |
| 91 | private String channelId; | 110 | private String channelId; |
| 92 | 111 | ||
| 93 | private int isHorizontalScreen = 0; | 112 | private int isHorizontalScreen = 0; |
| 94 | 113 | ||
| 114 | + private boolean mShowAnimatedLogo; | ||
| 115 | + | ||
| 95 | private final static int PERMISSION_REQUEST_CAMERA_AUDIOREC = 1; | 116 | private final static int PERMISSION_REQUEST_CAMERA_AUDIOREC = 1; |
| 96 | private static final String START_STRING = "开始直播"; | 117 | private static final String START_STRING = "开始直播"; |
| 97 | private static final String STOP_STRING = "停止直播"; | 118 | private static final String STOP_STRING = "停止直播"; |
| @@ -112,6 +133,8 @@ public class StreamActivity extends Activity { | @@ -112,6 +133,8 @@ public class StreamActivity extends Activity { | ||
| 112 | private TextView mRuddyText; | 133 | private TextView mRuddyText; |
| 113 | private AppCompatSeekBar mRuddySeekBar; | 134 | private AppCompatSeekBar mRuddySeekBar; |
| 114 | 135 | ||
| 136 | + private AnimatedImageCapture animatedImageCapture; | ||
| 137 | + | ||
| 115 | public static void startActivity(Context context, | 138 | public static void startActivity(Context context, |
| 116 | String activityID, | 139 | String activityID, |
| 117 | String channelID, | 140 | String channelID, |
| @@ -142,6 +165,7 @@ public class StreamActivity extends Activity { | @@ -142,6 +165,7 @@ public class StreamActivity extends Activity { | ||
| 142 | channelId = bundle.getString(CHANNELID); | 165 | channelId = bundle.getString(CHANNELID); |
| 143 | mIsLandscape = bundle.getBoolean(LANDSCAPE, false); | 166 | mIsLandscape = bundle.getBoolean(LANDSCAPE, false); |
| 144 | initStreamer(); | 167 | initStreamer(); |
| 168 | + startCameraPreviewWithPermCheck(); | ||
| 145 | 169 | ||
| 146 | } | 170 | } |
| 147 | 171 | ||
| @@ -163,8 +187,15 @@ public class StreamActivity extends Activity { | @@ -163,8 +187,15 @@ public class StreamActivity extends Activity { | ||
| 163 | //闪光灯 | 187 | //闪光灯 |
| 164 | mFlashView = findViewById(R.id.flash); | 188 | mFlashView = findViewById(R.id.flash); |
| 165 | mFlashView.setOnClickListener(mObserverButton); | 189 | mFlashView.setOnClickListener(mObserverButton); |
| 190 | + //亮度 | ||
| 191 | + mExposureView = (ImageView) findViewById(R.id.exposure); | ||
| 192 | + mExposureView.setOnClickListener(mObserverButton); | ||
| 193 | + //悬浮窗 | ||
| 194 | + mAddView = (ImageView) findViewById(R.id.add); | ||
| 195 | + mAddView.setOnClickListener(mObserverButton); | ||
| 196 | + | ||
| 166 | mCheckBoxObserver = new CheckBoxObserver(); | 197 | mCheckBoxObserver = new CheckBoxObserver(); |
| 167 | - //美艳 | 198 | + //美颜 |
| 168 | mBeautyCheckBox = (CheckBox) findViewById(R.id.click_to_switch_beauty); | 199 | mBeautyCheckBox = (CheckBox) findViewById(R.id.click_to_switch_beauty); |
| 169 | mBeautyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); | 200 | mBeautyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); |
| 170 | //背景音乐 | 201 | //背景音乐 |
| @@ -186,6 +217,28 @@ public class StreamActivity extends Activity { | @@ -186,6 +217,28 @@ public class StreamActivity extends Activity { | ||
| 186 | mAudioOnlyCheckBox = (CheckBox) findViewById(R.id.audio_only); | 217 | mAudioOnlyCheckBox = (CheckBox) findViewById(R.id.audio_only); |
| 187 | mAudioOnlyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); | 218 | mAudioOnlyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); |
| 188 | 219 | ||
| 220 | + //音频滤镜 | ||
| 221 | + mAudioMix = (Button) findViewById(R.id.audio_mix); | ||
| 222 | + mAudioMix.setOnClickListener(mObserverButton); | ||
| 223 | + | ||
| 224 | + //bgm升降调 | ||
| 225 | + mBgmFilter = (Button) findViewById(R.id.bgm_filter); | ||
| 226 | + mBgmFilter.setOnClickListener(mObserverButton); | ||
| 227 | + | ||
| 228 | + //截屏 | ||
| 229 | + mCaptureScreen = (Button) findViewById(R.id.click_to_capture_screenshot); | ||
| 230 | + mCaptureScreen.setOnClickListener(mObserverButton); | ||
| 231 | + | ||
| 232 | + //背景图推流 | ||
| 233 | + mBgImg = (CheckBox) findViewById(R.id.bg_img); | ||
| 234 | + mBgImg.setOnCheckedChangeListener(mCheckBoxObserver); | ||
| 235 | + | ||
| 236 | + //亮度调节seekbar | ||
| 237 | + mExposureSeekBar = (VerticalSeekBar) findViewById(R.id.exposure_seekBar); | ||
| 238 | + mExposureSeekBar.setProgress(50); | ||
| 239 | + mExposureSeekBar.setSecondaryProgress(50); | ||
| 240 | + mExposureSeekBar.setOnSeekBarChangeListener(verticalSeekBarlistener); | ||
| 241 | + | ||
| 189 | mBeautyChooseView = findViewById(R.id.beauty_choose); | 242 | mBeautyChooseView = findViewById(R.id.beauty_choose); |
| 190 | mBeautySpinner = (AppCompatSpinner) findViewById(R.id.beauty_spin); | 243 | mBeautySpinner = (AppCompatSpinner) findViewById(R.id.beauty_spin); |
| 191 | mBeautyGrindLayout = (LinearLayout) findViewById(R.id.beauty_grind); | 244 | mBeautyGrindLayout = (LinearLayout) findViewById(R.id.beauty_grind); |
| @@ -213,8 +266,27 @@ public class StreamActivity extends Activity { | @@ -213,8 +266,27 @@ public class StreamActivity extends Activity { | ||
| 213 | //分别设置预览和推流分辨率 | 266 | //分别设置预览和推流分辨率 |
| 214 | mStreamer.setPreviewResolution(VIDEO_RESOLUTION_360P); | 267 | mStreamer.setPreviewResolution(VIDEO_RESOLUTION_360P); |
| 215 | mStreamer.setTargetResolution(VIDEO_RESOLUTION_360P); | 268 | mStreamer.setTargetResolution(VIDEO_RESOLUTION_360P); |
| 269 | +// //采集分辨率 | ||
| 270 | +// mStreamer.setCameraCaptureResolution(VIDEO_RESOLUTION_360P); | ||
| 216 | //设置编码模式(硬编/软编/软编兼容) | 271 | //设置编码模式(硬编/软编/软编兼容) |
| 217 | - mStreamer.setEncodeMethod(ENCODE_METHOD_SOFTWARE_COMPAT); | 272 | + mStreamer.setEncodeMethod(ENCODE_METHOD_HARDWARE); |
| 273 | + | ||
| 274 | +// //设置场景 | ||
| 275 | +// mStreamer.setVideoEncodeScene(IStreamer.ENCODE_SCENE_GAME); | ||
| 276 | + | ||
| 277 | +// //H265 | ||
| 278 | +// mStreamer.setVideoCodecId(IStreamer.CODEC_ID_HEVC); | ||
| 279 | +// //H264 | ||
| 280 | +// mStreamer.setVideoCodecId(IStreamer.CODEC_ID_AVC); | ||
| 281 | + | ||
| 282 | +// //功耗 | ||
| 283 | +// mStreamer.setVideoEncodeProfile(ENCODE_PROFILE_LOW_POWER);低 | ||
| 284 | +// mStreamer.setVideoEncodeProfile(ENCODE_PROFILE_BALANCE);平衡 | ||
| 285 | +// mStreamer.setVideoEncodeProfile(ENCODE_PROFILE_HIGH_PERFORMANCE);高性能 | ||
| 286 | + | ||
| 287 | +// //双声道推流 | ||
| 288 | +// mStreamer.setAudioChannels(2); | ||
| 289 | + | ||
| 218 | 290 | ||
| 219 | //设置横竖屏方向 | 291 | //设置横竖屏方向 |
| 220 | mStreamer.setRotateDegrees(mIsLandscape ? 90 : 0);//设置屏幕旋转角度 | 292 | mStreamer.setRotateDegrees(mIsLandscape ? 90 : 0);//设置屏幕旋转角度 |
| @@ -243,22 +315,35 @@ public class StreamActivity extends Activity { | @@ -243,22 +315,35 @@ public class StreamActivity extends Activity { | ||
| 243 | //设置错误回调,收到该回调后,一般是发生了严重错误,比如网络断开等 | 315 | //设置错误回调,收到该回调后,一般是发生了严重错误,比如网络断开等 |
| 244 | mStreamer.setOnErrorListener(mOnErrorListener); | 316 | mStreamer.setOnErrorListener(mOnErrorListener); |
| 245 | //设置美颜滤镜 | 317 | //设置美颜滤镜 |
| 246 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), DENOISE.getFilter()); | 318 | + mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), CN_FILTER_BEAUTY_DENOISE.getFilter()); |
| 247 | mStreamer.setEnableImgBufBeauty(true); | 319 | mStreamer.setEnableImgBufBeauty(true); |
| 248 | 320 | ||
| 249 | initBeautyUI(); | 321 | initBeautyUI(); |
| 250 | 322 | ||
| 323 | + mStreamer.setFilterOnErrorListener(new ImgFilterBase.OnErrorListener() { | ||
| 324 | + @Override | ||
| 325 | + public void onError(int i) { | ||
| 326 | + Toast.makeText(StreamActivity.this, "当前机型不支持该滤镜", Toast.LENGTH_SHORT).show(); | ||
| 327 | + mStreamer.setFilter(CN_FILTER_BEAUTY_DISABLE.getFilter()); | ||
| 328 | + } | ||
| 329 | + }); | ||
| 330 | + | ||
| 251 | //手动变焦 | 331 | //手动变焦 |
| 252 | mStreamer.setCameraCapture(mStreamer.getCameraCapture()); | 332 | mStreamer.setCameraCapture(mStreamer.getCameraCapture()); |
| 253 | mCameraPreviewView.setOnTouchListener(mStreamer.getCameraTouchHelper()); | 333 | mCameraPreviewView.setOnTouchListener(mStreamer.getCameraTouchHelper()); |
| 254 | mStreamer.setCameraHintView(mCameraHintView); | 334 | mStreamer.setCameraHintView(mCameraHintView); |
| 255 | mStreamer.setEnableTouchFocus(true); | 335 | mStreamer.setEnableTouchFocus(true); |
| 256 | mStreamer.setEnableZoom(true); | 336 | mStreamer.setEnableZoom(true); |
| 337 | + | ||
| 338 | + animatedImageCapture = new AnimatedImageCapture(this, mStreamer); | ||
| 257 | } | 339 | } |
| 258 | 340 | ||
| 259 | private void initBeautyUI() { | 341 | private void initBeautyUI() { |
| 260 | String[] items = new String[]{"DISABLE", "BEAUTY_SOFT", "SKIN_WHITEN", "BEAUTY_ILLUSION", | 342 | String[] items = new String[]{"DISABLE", "BEAUTY_SOFT", "SKIN_WHITEN", "BEAUTY_ILLUSION", |
| 261 | - "BEAUTY_DENOISE", "BEAUTY_SMOOTH", "BEAUTY_SOFT_EXT", "BEAUTY_SOFT_SHARPEN", "BEAUTY_PRO"}; | 343 | + "BEAUTY_DENOISE", "BEAUTY_SMOOTH", "BEAUTY_PRO", "BEAUTY_PRO1", "BEAUTY_PRO2", "BEAUTY_PRO3", |
| 344 | + "BEAUTY_PRO4", "ToneCurve", "复古", "胶片", "Amatorka", | ||
| 345 | + "ELEGANCE", "1977", "Amaro", "Brannan", "EarlyBird", "Hefe", "Hudson", "ink", | ||
| 346 | + "Lomo", "LordKelvin", "Nash", "Rise", "Sierra", "Sutro", "Toaster", "Valencia", "Walden", "XproII"}; | ||
| 262 | ArrayAdapter<String> adapter = new ArrayAdapter<>(this, | 347 | ArrayAdapter<String> adapter = new ArrayAdapter<>(this, |
| 263 | android.R.layout.simple_spinner_item, items); | 348 | android.R.layout.simple_spinner_item, items); |
| 264 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | 349 | adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); |
| @@ -270,34 +355,35 @@ public class StreamActivity extends Activity { | @@ -270,34 +355,35 @@ public class StreamActivity extends Activity { | ||
| 270 | if (textView != null) { | 355 | if (textView != null) { |
| 271 | textView.setTextColor(getResources().getColor(R.color.colorAccent)); | 356 | textView.setTextColor(getResources().getColor(R.color.colorAccent)); |
| 272 | } | 357 | } |
| 273 | - switch (position) { | ||
| 274 | - case 0: | ||
| 275 | - mStreamer.setFilter(BEAUTY_DISABLE.getFilter()); | ||
| 276 | - break; | ||
| 277 | - case 1: | ||
| 278 | - mStreamer.setFilter(BEAUTY_SOFT.getFilter()); | ||
| 279 | - break; | ||
| 280 | - case 2: | ||
| 281 | - mStreamer.setFilter(SKIN_WHITEN.getFilter()); | ||
| 282 | - break; | ||
| 283 | - case 3: | ||
| 284 | - mStreamer.setFilter(BEAUTY_ILLUSION.getFilter()); | ||
| 285 | - break; | ||
| 286 | - case 4: | ||
| 287 | - mStreamer.setFilter(DENOISE.getFilter()); | ||
| 288 | - break; | ||
| 289 | - case 5: | ||
| 290 | - mStreamer.setFilter(BEAUTY_SMOOTH.getFilter()); | ||
| 291 | - break; | ||
| 292 | - case 6: | ||
| 293 | - mStreamer.setFilter(BEAUTY_SOFT_EXT.getFilter()); | ||
| 294 | - break; | ||
| 295 | - case 7: | ||
| 296 | - mStreamer.setFilter(BEAUTY_SOFT_SHARPEN.getFilter()); | ||
| 297 | - break; | ||
| 298 | - case 8: | ||
| 299 | - mStreamer.setFilter(BEAUTY_PRO.getFilter()); | ||
| 300 | - break; | 358 | + |
| 359 | + if (position == 0) { | ||
| 360 | + mStreamer.setFilter(CN_FILTER_BEAUTY_DISABLE.getFilter()); | ||
| 361 | + } else if (position <= 5) { | ||
| 362 | + mStreamer.setFilter(position + 15); | ||
| 363 | + } else if (position == 6) { | ||
| 364 | + mStreamer.setFilter(CN_FILTER_BEAUTY_PRO.getFilter()); | ||
| 365 | + } else if (position == 7) { | ||
| 366 | + mStreamer.setFilter(CN_FILTER_BEAUTY_PRO1.getFilter()); | ||
| 367 | + } else if (position == 8) { | ||
| 368 | + mStreamer.setFilter(CN_FILTER_BEAUTY_PRO2.getFilter()); | ||
| 369 | + } else if (position == 9) { | ||
| 370 | + mStreamer.setFilter(CN_FILTER_BEAUTY_PRO3.getFilter()); | ||
| 371 | + } else if (position == 10) { | ||
| 372 | + mStreamer.setFilter(CN_FILTER_BEAUTY_PRO4.getFilter()); | ||
| 373 | + } else if (position == 11) { | ||
| 374 | + mStreamer.setToneCurveFilterStream(StreamActivity.this.getResources().openRawResource(R.raw.tone_cuver_sample)); | ||
| 375 | + } else if (position == 12) { | ||
| 376 | + mStreamer.setToneCurveFilterStream(StreamActivity.this.getResources().openRawResource(R.raw.fugu)); | ||
| 377 | + } else if (position == 13) { | ||
| 378 | + mStreamer.setToneCurveFilterStream(StreamActivity.this.getResources().openRawResource(R.raw.jiaopian)); | ||
| 379 | + } else if (position == 14) { | ||
| 380 | + //设置风格滤镜 | ||
| 381 | + mStreamer.setSpecialEffectsFilter(CN_SPECIAL_EFFECT_AMATORKA.getFilter()); | ||
| 382 | + } else if (position == 15) { | ||
| 383 | + mStreamer.setSpecialEffectsFilter(CN_SPECIAL_EFFECT_ELEGANCE.getFilter()); | ||
| 384 | + } else if (position >= 16 && position <= 32) { | ||
| 385 | + int diff = position - 16; | ||
| 386 | + mStreamer.setFilter(CN_FILTER_BEAUTY_1977.getFilter() + diff); | ||
| 301 | } | 387 | } |
| 302 | List<ImgFilterBase> filters = mStreamer.getFilter(); | 388 | List<ImgFilterBase> filters = mStreamer.getFilter(); |
| 303 | if (filters != null && !filters.isEmpty()) { | 389 | if (filters != null && !filters.isEmpty()) { |
| @@ -363,10 +449,81 @@ public class StreamActivity extends Activity { | @@ -363,10 +449,81 @@ public class StreamActivity extends Activity { | ||
| 363 | mBeautySpinner.setSelection(4); | 449 | mBeautySpinner.setSelection(4); |
| 364 | } | 450 | } |
| 365 | 451 | ||
| 452 | + private boolean[] mChooseFilter = {false, false, false, false | ||
| 453 | + , false, false, false, false, false, false}; | ||
| 454 | + | ||
| 455 | + private void showChooseAudioFilter() { | ||
| 456 | + AlertDialog alertDialog; | ||
| 457 | + alertDialog = new AlertDialog.Builder(this) | ||
| 458 | + .setTitle("音频滤镜") | ||
| 459 | + .setMultiChoiceItems(new String[]{"录音棚", "KTV", "小舞台", "演唱会", "萝莉", | ||
| 460 | + "大叔", "庄严", "机器人"}, mChooseFilter, | ||
| 461 | + new DialogInterface.OnMultiChoiceClickListener() { | ||
| 462 | + @Override | ||
| 463 | + public void onClick(DialogInterface dialog, int which, boolean isChecked) { | ||
| 464 | + if (isChecked) { | ||
| 465 | + mChooseFilter[which] = true; | ||
| 466 | + } | ||
| 467 | + } | ||
| 468 | + }).setPositiveButton("确定", new DialogInterface.OnClickListener() { | ||
| 469 | + @Override | ||
| 470 | + public void onClick(DialogInterface dialog, int which) { | ||
| 471 | + List<CNAudioFilterBase> filterBases = new LinkedList<CNAudioFilterBase>(); | ||
| 472 | + if (mChooseFilter[0]) { | ||
| 473 | + CNAudioReverbFilter reverbFilter = new CNAudioReverbFilter(); | ||
| 474 | + reverbFilter.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_1); | ||
| 475 | + filterBases.add(reverbFilter); | ||
| 476 | + } | ||
| 477 | + if (mChooseFilter[1]) { | ||
| 478 | + CNAudioReverbFilter reverbFilter = new CNAudioReverbFilter(); | ||
| 479 | + reverbFilter.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_2); | ||
| 480 | + filterBases.add(reverbFilter); | ||
| 481 | + } | ||
| 482 | + if (mChooseFilter[2]) { | ||
| 483 | + CNAudioReverbFilter reverbFilter = new CNAudioReverbFilter(); | ||
| 484 | + reverbFilter.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_3); | ||
| 485 | + filterBases.add(reverbFilter); | ||
| 486 | + } | ||
| 487 | + if (mChooseFilter[3]) { | ||
| 488 | + CNAudioReverbFilter reverbFilter = new CNAudioReverbFilter(); | ||
| 489 | + reverbFilter.setReverbLevel(IStreamer.AUDIO_REVERB_LEVEL_4); | ||
| 490 | + filterBases.add(reverbFilter); | ||
| 491 | + } | ||
| 492 | + if (mChooseFilter[4]) { | ||
| 493 | + CNAudioEffectFilter effectFilter = new CNAudioEffectFilter(ICNAudioEffectFilter.AUDIO_EFFECT_TYPE_FEMALE); | ||
| 494 | + filterBases.add(effectFilter); | ||
| 495 | + } | ||
| 496 | + if (mChooseFilter[5]) { | ||
| 497 | + CNAudioEffectFilter effectFilter = new CNAudioEffectFilter(ICNAudioEffectFilter.AUDIO_EFFECT_TYPE_MALE); | ||
| 498 | + filterBases.add(effectFilter); | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + if (mChooseFilter[6]) { | ||
| 502 | + CNAudioEffectFilter effectFilter = new CNAudioEffectFilter(ICNAudioEffectFilter.AUDIO_EFFECT_TYPE_HEROIC); | ||
| 503 | + filterBases.add(effectFilter); | ||
| 504 | + } | ||
| 505 | + | ||
| 506 | + if (mChooseFilter[7]) { | ||
| 507 | + CNAudioEffectFilter effectFilter = new CNAudioEffectFilter(ICNAudioEffectFilter.AUDIO_EFFECT_TYPE_ROBOT); | ||
| 508 | + filterBases.add(effectFilter); | ||
| 509 | + } | ||
| 510 | + | ||
| 511 | + if (!mChooseFilter[0] && !mChooseFilter[1] && !mChooseFilter[2] && !mChooseFilter[3] && !mChooseFilter[4] && !mChooseFilter[5] && !mChooseFilter[6] && !mChooseFilter[7]) { | ||
| 512 | + filterBases = null; | ||
| 513 | + } | ||
| 514 | + mStreamer.getAudioFilterMgt().setFilter(filterBases); | ||
| 515 | + | ||
| 516 | + dialog.dismiss(); | ||
| 517 | + } | ||
| 518 | + }).create(); | ||
| 519 | + alertDialog.setCancelable(false); | ||
| 520 | + alertDialog.show(); | ||
| 521 | + } | ||
| 522 | + | ||
| 366 | @Override | 523 | @Override |
| 367 | public void onResume() { | 524 | public void onResume() { |
| 368 | super.onResume(); | 525 | super.onResume(); |
| 369 | - startCameraPreviewWithPermCheck(); | 526 | + mStreamer.setDisplayPreview(mCameraPreviewView); |
| 370 | mStreamer.resume(); | 527 | mStreamer.resume(); |
| 371 | if (mStreamer.isRecording() && !mAudioOnlyCheckBox.isChecked()) { | 528 | if (mStreamer.isRecording() && !mAudioOnlyCheckBox.isChecked()) { |
| 372 | mStreamer.setAudioOnly(false); | 529 | mStreamer.setAudioOnly(false); |
| @@ -380,17 +537,18 @@ public class StreamActivity extends Activity { | @@ -380,17 +537,18 @@ public class StreamActivity extends Activity { | ||
| 380 | public void onPause() { | 537 | public void onPause() { |
| 381 | super.onPause(); | 538 | super.onPause(); |
| 382 | mStreamer.pause(); | 539 | mStreamer.pause(); |
| 383 | - mStreamer.stopCameraPreview(); | ||
| 384 | - if (mStreamer.isRecording() && !mAudioOnlyCheckBox.isChecked()) { | ||
| 385 | - mStreamer.setAudioOnly(true); | ||
| 386 | - mStreamer.setMuteAudio(true); | ||
| 387 | - } | 540 | +// mStreamer.stopCameraPreview(); |
| 541 | +// if (mStreamer.isRecording() && !mAudioOnlyCheckBox.isChecked()) { | ||
| 542 | +// mStreamer.setAudioOnly(true); | ||
| 543 | +// mStreamer.setMuteAudio(true); | ||
| 544 | +// } | ||
| 388 | hideWaterMark(); | 545 | hideWaterMark(); |
| 389 | } | 546 | } |
| 390 | 547 | ||
| 391 | @Override | 548 | @Override |
| 392 | public void onDestroy() { | 549 | public void onDestroy() { |
| 393 | super.onDestroy(); | 550 | super.onDestroy(); |
| 551 | + animatedImageCapture.stop(); | ||
| 394 | if (mMainHandler != null) { | 552 | if (mMainHandler != null) { |
| 395 | mMainHandler.removeCallbacksAndMessages(null); | 553 | mMainHandler.removeCallbacksAndMessages(null); |
| 396 | mMainHandler = null; | 554 | mMainHandler = null; |
| @@ -429,6 +587,87 @@ public class StreamActivity extends Activity { | @@ -429,6 +587,87 @@ public class StreamActivity extends Activity { | ||
| 429 | mRecording = false; | 587 | mRecording = false; |
| 430 | } | 588 | } |
| 431 | 589 | ||
| 590 | + private int mChooseBGMFilter = 3; | ||
| 591 | + private String[] mBGMFilterName = {"-3", "-2", "-1", "0", "1", "2", "3"}; | ||
| 592 | + | ||
| 593 | + //bgm升降调 | ||
| 594 | + private void onChooseBGMFilter() { | ||
| 595 | + AlertDialog alertDialog; | ||
| 596 | + alertDialog = new AlertDialog.Builder(this) | ||
| 597 | + .setTitle("请选择背景音乐变调等级") | ||
| 598 | + .setSingleChoiceItems(mBGMFilterName, mChooseBGMFilter, new DialogInterface.OnClickListener() { | ||
| 599 | + @Override | ||
| 600 | + public void onClick(DialogInterface dialog, int which) { | ||
| 601 | + mChooseBGMFilter = which; | ||
| 602 | + } | ||
| 603 | + }) | ||
| 604 | + .setPositiveButton("确定", new DialogInterface.OnClickListener() { | ||
| 605 | + @Override | ||
| 606 | + public void onClick(DialogInterface dialog, int which) { | ||
| 607 | + int level = Integer.parseInt(mBGMFilterName[mChooseBGMFilter]); | ||
| 608 | + CNAudioEffectFilter audioEffectFilter = new CNAudioEffectFilter(ICNAudioEffectFilter.AUDIO_EFFECT_TYPE_PITCH); | ||
| 609 | + audioEffectFilter.setPitchLevel(level); | ||
| 610 | + mStreamer.getBGMAudioFilterMgt().setFilter(audioEffectFilter); | ||
| 611 | + dialog.dismiss(); | ||
| 612 | + } | ||
| 613 | + }) | ||
| 614 | + .create(); | ||
| 615 | + alertDialog.setCancelable(false); | ||
| 616 | + alertDialog.show(); | ||
| 617 | + } | ||
| 618 | + | ||
| 619 | + //截屏 | ||
| 620 | + private void onCaptureScreenShotClick() { | ||
| 621 | + mStreamer.requestScreenShot(new IStreamer.ScreenShotListener() { | ||
| 622 | + @Override | ||
| 623 | + public void onBitmapAvailable(Bitmap bitmap) { | ||
| 624 | + BufferedOutputStream bos = null; | ||
| 625 | + try { | ||
| 626 | + Date date = new Date(); | ||
| 627 | + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); | ||
| 628 | + final String fileName = "/sdcard/screenshot" + dateFormat.format(date) + ".jpg"; | ||
| 629 | + | ||
| 630 | + bos = new BufferedOutputStream(new FileOutputStream(fileName)); | ||
| 631 | + | ||
| 632 | + if (bitmap != null) { | ||
| 633 | + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos); | ||
| 634 | + runOnUiThread(new Runnable() { | ||
| 635 | + @Override | ||
| 636 | + public void run() { | ||
| 637 | + Toast.makeText(StreamActivity.this, "保存截图到" + fileName, Toast.LENGTH_SHORT).show(); | ||
| 638 | + } | ||
| 639 | + }); | ||
| 640 | + } | ||
| 641 | + } catch (FileNotFoundException e) { | ||
| 642 | + e.printStackTrace(); | ||
| 643 | + } finally { | ||
| 644 | + if (bos != null) | ||
| 645 | + try { | ||
| 646 | + bos.close(); | ||
| 647 | + } catch (IOException e) { | ||
| 648 | + e.printStackTrace(); | ||
| 649 | + } | ||
| 650 | + } | ||
| 651 | + } | ||
| 652 | + }); | ||
| 653 | + } | ||
| 654 | + | ||
| 655 | + //亮度调节 | ||
| 656 | + private void onExposureClick() { | ||
| 657 | + if (mExposureSeekBar.getVisibility() == View.VISIBLE) { | ||
| 658 | + mExposureSeekBar.setVisibility(View.GONE); | ||
| 659 | + } else { | ||
| 660 | + mExposureSeekBar.setVisibility(View.VISIBLE); | ||
| 661 | + } | ||
| 662 | + } | ||
| 663 | + | ||
| 664 | + private void onAddClick() { | ||
| 665 | + Intent intent = new Intent(getApplicationContext(), FloatActivity.class); | ||
| 666 | + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||
| 667 | + CNGlobalStreamer.setInstance(mStreamer); | ||
| 668 | + startActivity(intent); | ||
| 669 | + } | ||
| 670 | + | ||
| 432 | private class ButtonObserver implements View.OnClickListener { | 671 | private class ButtonObserver implements View.OnClickListener { |
| 433 | @Override | 672 | @Override |
| 434 | public void onClick(View view) { | 673 | public void onClick(View view) { |
| @@ -445,12 +684,28 @@ public class StreamActivity extends Activity { | @@ -445,12 +684,28 @@ public class StreamActivity extends Activity { | ||
| 445 | case R.id.click_to_shoot: | 684 | case R.id.click_to_shoot: |
| 446 | onShootClick(); | 685 | onShootClick(); |
| 447 | break; | 686 | break; |
| 687 | + case R.id.audio_mix: | ||
| 688 | + showChooseAudioFilter(); | ||
| 689 | + break; | ||
| 690 | + case R.id.bgm_filter: | ||
| 691 | + onChooseBGMFilter(); | ||
| 692 | + break; | ||
| 693 | + case R.id.click_to_capture_screenshot: | ||
| 694 | + onCaptureScreenShotClick(); | ||
| 695 | + break; | ||
| 696 | + case R.id.exposure: | ||
| 697 | + onExposureClick(); | ||
| 698 | + break; | ||
| 699 | + case R.id.add: | ||
| 700 | + onAddClick(); | ||
| 701 | + break; | ||
| 448 | default: | 702 | default: |
| 449 | break; | 703 | break; |
| 450 | } | 704 | } |
| 451 | } | 705 | } |
| 452 | } | 706 | } |
| 453 | 707 | ||
| 708 | + | ||
| 454 | private class CheckBoxObserver implements CompoundButton.OnCheckedChangeListener { | 709 | private class CheckBoxObserver implements CompoundButton.OnCheckedChangeListener { |
| 455 | 710 | ||
| 456 | @Override | 711 | @Override |
| @@ -477,6 +732,9 @@ public class StreamActivity extends Activity { | @@ -477,6 +732,9 @@ public class StreamActivity extends Activity { | ||
| 477 | case R.id.audio_only: | 732 | case R.id.audio_only: |
| 478 | onAudioOnlyChecked(isChecked); | 733 | onAudioOnlyChecked(isChecked); |
| 479 | break; | 734 | break; |
| 735 | + case R.id.bg_img: | ||
| 736 | + onBgImageChecked(isChecked); | ||
| 737 | + break; | ||
| 480 | default: | 738 | default: |
| 481 | break; | 739 | break; |
| 482 | } | 740 | } |
| @@ -484,6 +742,17 @@ public class StreamActivity extends Activity { | @@ -484,6 +742,17 @@ public class StreamActivity extends Activity { | ||
| 484 | 742 | ||
| 485 | } | 743 | } |
| 486 | 744 | ||
| 745 | + //背景图推流 | ||
| 746 | + private void onBgImageChecked(boolean isChecked) { | ||
| 747 | + if (isChecked) { | ||
| 748 | + mStreamer.stopCameraPreview(); | ||
| 749 | + mStreamer.startImageCapture(mBgImagePath); | ||
| 750 | + } else { | ||
| 751 | + mStreamer.startCameraPreview(); | ||
| 752 | + mStreamer.stopImageCapture(); | ||
| 753 | + } | ||
| 754 | + } | ||
| 755 | + | ||
| 487 | //转换摄像头 | 756 | //转换摄像头 |
| 488 | private void onSwitchCamera() { | 757 | private void onSwitchCamera() { |
| 489 | mStreamer.switchCamera(); | 758 | mStreamer.switchCamera(); |
| @@ -536,11 +805,11 @@ public class StreamActivity extends Activity { | @@ -536,11 +805,11 @@ public class StreamActivity extends Activity { | ||
| 536 | private void onBeautyChecked(boolean isChecked) { | 805 | private void onBeautyChecked(boolean isChecked) { |
| 537 | if (mStreamer.getVideoEncodeMethod() == | 806 | if (mStreamer.getVideoEncodeMethod() == |
| 538 | IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT) { | 807 | IStreamer.ENCODE_METHOD_SOFTWARE_COMPAT) { |
| 539 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), | ||
| 540 | - DENOISE.getFilter()); | ||
| 541 | - mStreamer.setEnableImgBufBeauty(true); | 808 | + mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), isChecked ? |
| 809 | + CN_FILTER_BEAUTY_DENOISE.getFilter() : | ||
| 810 | + CN_FILTER_BEAUTY_DISABLE.getFilter()); | ||
| 811 | + mStreamer.setEnableImgBufBeauty(isChecked); | ||
| 542 | } else { | 812 | } else { |
| 543 | -// showChooseFilter(); | ||
| 544 | mBeautyChooseView.setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE); | 813 | mBeautyChooseView.setVisibility(isChecked ? View.VISIBLE : View.INVISIBLE); |
| 545 | } | 814 | } |
| 546 | } | 815 | } |
| @@ -587,17 +856,21 @@ public class StreamActivity extends Activity { | @@ -587,17 +856,21 @@ public class StreamActivity extends Activity { | ||
| 587 | //显示水印 | 856 | //显示水印 |
| 588 | private void showWaterMark() { | 857 | private void showWaterMark() { |
| 589 | if (!mIsLandscape) { | 858 | if (!mIsLandscape) { |
| 590 | - /** | ||
| 591 | - * 设置并显示logo水印 | ||
| 592 | - * | ||
| 593 | - * @param path logo图片文件的路径 | ||
| 594 | - * @param x logo的显示位置,0-1之间,相对于视频 | ||
| 595 | - * @param y logo的显示位置,0-1之间,相对于视频 | ||
| 596 | - * @param w logo的显示宽度,0-1之间,相对于视频,为0时会根据h及logo图片的比例自适应 | ||
| 597 | - * @param h logo的显示高度,0-1之间,相对于视频,为0时会根据w及logo图片的比例自适应 | ||
| 598 | - * @param alpha logo的透明度,0-1之间 | ||
| 599 | - */ | ||
| 600 | - mStreamer.showWaterMarkLogo(mLogoPath, 0.08f, 0.04f, 0.20f, 0, 0.8f); | 859 | + if (!mShowAnimatedLogo) { |
| 860 | + /** | ||
| 861 | + * 设置并显示logo水印 | ||
| 862 | + * | ||
| 863 | + * @param path logo图片文件的路径 | ||
| 864 | + * @param x logo的显示位置,0-1之间,相对于视频 | ||
| 865 | + * @param y logo的显示位置,0-1之间,相对于视频 | ||
| 866 | + * @param w logo的显示宽度,0-1之间,相对于视频,为0时会根据h及logo图片的比例自适应 | ||
| 867 | + * @param h logo的显示高度,0-1之间,相对于视频,为0时会根据w及logo图片的比例自适应 | ||
| 868 | + * @param alpha logo的透明度,0-1之间 | ||
| 869 | + */ | ||
| 870 | + mStreamer.showWaterMarkLogo(mLogoPath, 0.08f, 0.04f, 0.20f, 0, 0.8f); | ||
| 871 | + } else { | ||
| 872 | + animatedImageCapture.showAnimatedWaterMard(mAnimatedLogoPath, 0.08f, 0.04f, 0.25f, 0, 0.8f); | ||
| 873 | + } | ||
| 601 | 874 | ||
| 602 | /** | 875 | /** |
| 603 | * 在推流视频中显示时间水印 | 876 | * 在推流视频中显示时间水印 |
| @@ -610,15 +883,21 @@ public class StreamActivity extends Activity { | @@ -610,15 +883,21 @@ public class StreamActivity extends Activity { | ||
| 610 | */ | 883 | */ |
| 611 | mStreamer.showWaterMarkTime(0.03f, 0.01f, 0.35f, Color.RED, 1.0f); | 884 | mStreamer.showWaterMarkTime(0.03f, 0.01f, 0.35f, Color.RED, 1.0f); |
| 612 | } else { | 885 | } else { |
| 613 | - mStreamer.showWaterMarkLogo(mLogoPath, 0.05f, 0.09f, 0, 0.08f, 0.8f); | 886 | + if (!mShowAnimatedLogo) { |
| 887 | + mStreamer.showWaterMarkLogo(mLogoPath, 0.05f, 0.09f, 0, 0.08f, 0.8f); | ||
| 888 | + } else { | ||
| 889 | + animatedImageCapture.showAnimatedWaterMard(mAnimatedLogoPath, 0.05f, 0.09f, 0, 0.25f, 0.8f); | ||
| 890 | + } | ||
| 614 | mStreamer.showWaterMarkTime(0.01f, 0.03f, 0.22f, Color.RED, 1.0f); | 891 | mStreamer.showWaterMarkTime(0.01f, 0.03f, 0.22f, Color.RED, 1.0f); |
| 615 | } | 892 | } |
| 893 | + mShowAnimatedLogo = !mShowAnimatedLogo; | ||
| 616 | } | 894 | } |
| 617 | 895 | ||
| 618 | //隐藏水印 | 896 | //隐藏水印 |
| 619 | private void hideWaterMark() { | 897 | private void hideWaterMark() { |
| 620 | mStreamer.hideWaterMarkLogo(); | 898 | mStreamer.hideWaterMarkLogo(); |
| 621 | mStreamer.hideWaterMarkTime(); | 899 | mStreamer.hideWaterMarkTime(); |
| 900 | + animatedImageCapture.hideAnimatedWaterMark(); | ||
| 622 | } | 901 | } |
| 623 | 902 | ||
| 624 | //摄像头权限检测 | 903 | //摄像头权限检测 |
| @@ -643,50 +922,6 @@ public class StreamActivity extends Activity { | @@ -643,50 +922,6 @@ public class StreamActivity extends Activity { | ||
| 643 | } | 922 | } |
| 644 | } | 923 | } |
| 645 | 924 | ||
| 646 | - //选择美颜滤镜 | ||
| 647 | - private void showChooseFilter() { | ||
| 648 | - AlertDialog alertDialog; | ||
| 649 | - alertDialog = new AlertDialog.Builder(this) | ||
| 650 | - .setTitle("请选择美颜滤镜") | ||
| 651 | - .setSingleChoiceItems( | ||
| 652 | - /** | ||
| 653 | - * BEAUTY_DISABLE 禁用美颜 无 | ||
| 654 | - * DENOISE 自然 美白(中)+磨皮(低)+红润(低) | ||
| 655 | - * SKIN_WHITEN 白肤 美白(强)+磨皮(中)+红润(低) | ||
| 656 | - * BEAUTY_ILLUSION 柔肤 美白(中)+磨皮(中)+红润(低) | ||
| 657 | - * BEAUTY_SMOOTH 粉嫩 美白(中)+磨皮(强)+红润(中) | ||
| 658 | - * BEAUTY_SOFT 嫩肤 美白(中)+磨皮(中)+红润(强) | ||
| 659 | - */ | ||
| 660 | - new String[]{"BEAUTY_SOFT", "SKIN_WHITEN", "BEAUTY_ILLUSION", "DENOISE", | ||
| 661 | - "BEAUTY_SMOOTH"}, -1, | ||
| 662 | - new DialogInterface.OnClickListener() { | ||
| 663 | - @Override | ||
| 664 | - public void onClick(DialogInterface dialog, int which) { | ||
| 665 | - switch (which) { | ||
| 666 | - case 0: | ||
| 667 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), BEAUTY_SOFT.getFilter()); | ||
| 668 | - break; | ||
| 669 | - case 1: | ||
| 670 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), SKIN_WHITEN.getFilter()); | ||
| 671 | - break; | ||
| 672 | - case 2: | ||
| 673 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), BEAUTY_ILLUSION.getFilter()); | ||
| 674 | - break; | ||
| 675 | - case 3: | ||
| 676 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), DENOISE.getFilter()); | ||
| 677 | - break; | ||
| 678 | - case 4: | ||
| 679 | - mStreamer.getImgTexFilterMgt().setFilter(mStreamer.getGLRender(), BEAUTY_SMOOTH.getFilter()); | ||
| 680 | - break; | ||
| 681 | - } | ||
| 682 | - dialog.dismiss(); | ||
| 683 | - } | ||
| 684 | - }) | ||
| 685 | - .create(); | ||
| 686 | - alertDialog.setCancelable(false); | ||
| 687 | - alertDialog.show(); | ||
| 688 | - } | ||
| 689 | - | ||
| 690 | private IStreamer.OnInfoListener mOnInfoListener = new IStreamer.OnInfoListener() { | 925 | private IStreamer.OnInfoListener mOnInfoListener = new IStreamer.OnInfoListener() { |
| 691 | @Override | 926 | @Override |
| 692 | public void onInfo(int what, int msg1, int msg2) { | 927 | public void onInfo(int what, int msg1, int msg2) { |
| @@ -894,4 +1129,33 @@ public class StreamActivity extends Activity { | @@ -894,4 +1129,33 @@ public class StreamActivity extends Activity { | ||
| 894 | return ""; | 1129 | return ""; |
| 895 | } | 1130 | } |
| 896 | }; | 1131 | }; |
| 1132 | + | ||
| 1133 | + private VerticalSeekBar.OnSeekBarChangeListener verticalSeekBarlistener = new VerticalSeekBar.OnSeekBarChangeListener() { | ||
| 1134 | + @Override | ||
| 1135 | + public void onProgressChanged(VerticalSeekBar seekBar, int progress, boolean fromUser) { | ||
| 1136 | + Camera.Parameters parameters = mStreamer.getCameraCapture().getCameraParameters(); | ||
| 1137 | + if (parameters != null) { | ||
| 1138 | + int minValue = parameters.getMinExposureCompensation(); | ||
| 1139 | + int maxValue = parameters.getMaxExposureCompensation(); | ||
| 1140 | + int range = 100 / (maxValue - minValue); | ||
| 1141 | + parameters.setExposureCompensation(progress / range - maxValue); | ||
| 1142 | + } | ||
| 1143 | + mStreamer.getCameraCapture().setCameraParameters(parameters); | ||
| 1144 | + } | ||
| 1145 | + | ||
| 1146 | + @Override | ||
| 1147 | + public void onStartTrackingTouch(VerticalSeekBar seekBar) { | ||
| 1148 | + | ||
| 1149 | + } | ||
| 1150 | + | ||
| 1151 | + @Override | ||
| 1152 | + public void onStopTrackingTouch(VerticalSeekBar seekBar) { | ||
| 1153 | + | ||
| 1154 | + } | ||
| 1155 | + | ||
| 1156 | + @Override | ||
| 1157 | + public void onRequestDisallowInterceptTouchEvent(boolean enable) { | ||
| 1158 | + | ||
| 1159 | + } | ||
| 1160 | + }; | ||
| 897 | } | 1161 | } |
app/src/main/java/com/cnlive/demo/stream/VerticalSeekBar.java
0 → 100644
| 1 | +package com.cnlive.demo.stream; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.graphics.drawable.Drawable; | ||
| 5 | +import android.graphics.drawable.GradientDrawable; | ||
| 6 | +import android.util.AttributeSet; | ||
| 7 | +import android.view.Gravity; | ||
| 8 | +import android.view.MotionEvent; | ||
| 9 | +import android.view.View; | ||
| 10 | +import android.widget.FrameLayout; | ||
| 11 | +import android.widget.TextView; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 自定义竖直SeekBar. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +public class VerticalSeekBar extends FrameLayout { | ||
| 18 | + public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) { | ||
| 19 | + super(context, attrs, defStyle); | ||
| 20 | + initialize(context); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + public VerticalSeekBar(Context context, AttributeSet attrs) { | ||
| 24 | + super(context, attrs); | ||
| 25 | + initialize(context); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + public VerticalSeekBar(Context context) { | ||
| 29 | + super(context); | ||
| 30 | + initialize(context); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + private View mProgressBg; | ||
| 34 | + private View mSecondaryProgressBg; | ||
| 35 | + private View mBg; | ||
| 36 | + private FrameLayout mSlider; | ||
| 37 | + private int mProgress = 50; | ||
| 38 | + private int mSecondaryProgress = 50; | ||
| 39 | + private int mMax = 100; | ||
| 40 | + private boolean mTrackingTouch = false; | ||
| 41 | + private OnSeekBarChangeListener mOnSeekBarChangeListener; | ||
| 42 | + private int mBGSize = 6; //进度条大小 | ||
| 43 | + private int mBGRadius = 20; //进度条圆角 | ||
| 44 | + private int mCircleSize = 50; //圆的大小 | ||
| 45 | + private TextView mTextView; //字 | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + public interface OnSeekBarChangeListener { | ||
| 49 | + void onProgressChanged(VerticalSeekBar seekBar, int progress, boolean fromUser); | ||
| 50 | + | ||
| 51 | + void onStartTrackingTouch(VerticalSeekBar seekBar); | ||
| 52 | + | ||
| 53 | + void onStopTrackingTouch(VerticalSeekBar seekBar); | ||
| 54 | + | ||
| 55 | + void onRequestDisallowInterceptTouchEvent(boolean enable); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + private void initialize(Context context) { | ||
| 59 | + LayoutParams params = new LayoutParams(mBGSize, LayoutParams.MATCH_PARENT); | ||
| 60 | + params.gravity = Gravity.CENTER_HORIZONTAL; | ||
| 61 | + mBg = new View(context); | ||
| 62 | + addView(mBg, params); | ||
| 63 | + | ||
| 64 | + GradientDrawable drawable = new GradientDrawable(); | ||
| 65 | + drawable.setColor(0xffbcbab8); | ||
| 66 | + drawable.setCornerRadius(mBGRadius); | ||
| 67 | + mBg.setBackgroundDrawable(drawable); | ||
| 68 | + | ||
| 69 | + params = new LayoutParams(mBGSize, 0); | ||
| 70 | + params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; | ||
| 71 | + mSecondaryProgressBg = new View(context); | ||
| 72 | + addView(mSecondaryProgressBg, params); | ||
| 73 | + | ||
| 74 | + drawable = new GradientDrawable(); | ||
| 75 | + drawable.setColor(0xffff5959); | ||
| 76 | + drawable.setCornerRadius(mBGRadius); | ||
| 77 | + mSecondaryProgressBg.setBackgroundDrawable(drawable); | ||
| 78 | + | ||
| 79 | + params = new LayoutParams(mBGSize, 0); | ||
| 80 | + params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; | ||
| 81 | + mProgressBg = new View(context); | ||
| 82 | + addView(mProgressBg, params); | ||
| 83 | + | ||
| 84 | + drawable = new GradientDrawable(); | ||
| 85 | + drawable.setColor(0xffff5959); | ||
| 86 | + drawable.setCornerRadius(mBGRadius); | ||
| 87 | + mProgressBg.setBackgroundDrawable(drawable); | ||
| 88 | + | ||
| 89 | + params = new LayoutParams(mCircleSize, mCircleSize); | ||
| 90 | + params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; | ||
| 91 | + mSlider = new FrameLayout(context); | ||
| 92 | + addView(mSlider, params); | ||
| 93 | + | ||
| 94 | + drawable = new GradientDrawable(); | ||
| 95 | + drawable.setColor(0xffdddddd); | ||
| 96 | + drawable.setShape(GradientDrawable.OVAL); | ||
| 97 | + | ||
| 98 | + params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); | ||
| 99 | + params.gravity = Gravity.CENTER; | ||
| 100 | + mTextView = new TextView(context); | ||
| 101 | + mTextView.setGravity(Gravity.CENTER); | ||
| 102 | + mTextView.setTextSize(12); | ||
| 103 | + mTextView.setIncludeFontPadding(false); | ||
| 104 | + mTextView.setText("0"); | ||
| 105 | + mTextView.setTextColor(0xff666666); | ||
| 106 | + //mSlider.addView(mTextView, params); | ||
| 107 | + | ||
| 108 | + mSlider.setBackgroundDrawable(drawable); | ||
| 109 | + | ||
| 110 | + setClickable(true); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + public String getText() { | ||
| 114 | + return mTextView.getText().toString(); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + public void setmText(String mText) { | ||
| 118 | + mTextView.setText(mText); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) { | ||
| 122 | + mOnSeekBarChangeListener = l; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + public void setThumb(Drawable d) { | ||
| 126 | + mSlider.setBackgroundDrawable(d); | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + public void setThumbSize(int w, int h) { | ||
| 130 | + LayoutParams params = (LayoutParams) mSlider.getLayoutParams(); | ||
| 131 | + params.width = w; | ||
| 132 | + params.height = h; | ||
| 133 | + mSlider.setLayoutParams(params); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + public void setProgressSize(int size) { | ||
| 137 | + LayoutParams params = (LayoutParams) mProgressBg.getLayoutParams(); | ||
| 138 | + params.width = size; | ||
| 139 | + mProgressBg.setLayoutParams(params); | ||
| 140 | + | ||
| 141 | + params = (LayoutParams) mSecondaryProgressBg.getLayoutParams(); | ||
| 142 | + params.width = size; | ||
| 143 | + mSecondaryProgressBg.setLayoutParams(params); | ||
| 144 | + | ||
| 145 | + params = (LayoutParams) mBg.getLayoutParams(); | ||
| 146 | + params.width = size; | ||
| 147 | + mBg.setLayoutParams(params); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + public void setProgressBackground(Drawable drawable) { | ||
| 151 | + mBg.setBackgroundDrawable(drawable); | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + public void setProgressDrawable(Drawable drawable) { | ||
| 155 | + mProgressBg.setBackgroundDrawable(drawable); | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + public void setSecondaryProgressDrawable(Drawable drawable) { | ||
| 159 | + mSecondaryProgressBg.setBackgroundDrawable(drawable); | ||
| 160 | + } | ||
| 161 | + | ||
| 162 | + public void setMax(int max) { | ||
| 163 | + mMax = max; | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + public void setProgress(int progress) { | ||
| 167 | + setProgress(progress, false); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + public int getProgress() { | ||
| 171 | + return mProgress; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + private void setProgress(int progress, boolean fromUser) { | ||
| 175 | + if (progress < 0) | ||
| 176 | + progress = 0; | ||
| 177 | + if (progress > mMax) | ||
| 178 | + progress = mMax; | ||
| 179 | + | ||
| 180 | + if (mProgress != progress) { | ||
| 181 | + mProgress = progress; | ||
| 182 | + updateProgress(fromUser); | ||
| 183 | + } | ||
| 184 | + mTextView.setText(progress + ""); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + private void updateProgress(boolean fromUser) { | ||
| 188 | + int paddingTop = getPaddingTop(); | ||
| 189 | + int paddingBottom = getPaddingBottom(); | ||
| 190 | + int h = getHeight() - paddingTop - paddingBottom; | ||
| 191 | + if (h > 0) { | ||
| 192 | + LayoutParams params = (LayoutParams) mProgressBg.getLayoutParams(); | ||
| 193 | + params.height = h * mProgress / mMax; | ||
| 194 | + mProgressBg.setLayoutParams(params); | ||
| 195 | + | ||
| 196 | + params = (LayoutParams) mSlider.getLayoutParams(); | ||
| 197 | + params.bottomMargin = (h - params.height) * mProgress / mMax; | ||
| 198 | + mSlider.setLayoutParams(params); | ||
| 199 | + } | ||
| 200 | + if (mOnSeekBarChangeListener != null) { | ||
| 201 | + mOnSeekBarChangeListener.onProgressChanged(this, mProgress, fromUser); | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + public void setSecondaryProgress(int progress) { | ||
| 206 | + if (progress < 0) | ||
| 207 | + progress = 0; | ||
| 208 | + if (progress > mMax) | ||
| 209 | + progress = mMax; | ||
| 210 | + | ||
| 211 | + mSecondaryProgress = progress; | ||
| 212 | + updateSecondaryProgress(); | ||
| 213 | + } | ||
| 214 | + | ||
| 215 | + private void updateSecondaryProgress() { | ||
| 216 | + int paddingTop = getPaddingTop(); | ||
| 217 | + int paddingBottom = getPaddingBottom(); | ||
| 218 | + int h = getHeight() - paddingTop - paddingBottom; | ||
| 219 | + if (h > 0) { | ||
| 220 | + LayoutParams params = (LayoutParams) mSecondaryProgressBg.getLayoutParams(); | ||
| 221 | + params.height = h * mSecondaryProgress / mMax; | ||
| 222 | + mSecondaryProgressBg.setLayoutParams(params); | ||
| 223 | + } | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + | ||
| 227 | + @Override | ||
| 228 | + public boolean dispatchTouchEvent(MotionEvent ev) { | ||
| 229 | + int action = ev.getAction(); | ||
| 230 | + int y = (int) ev.getY(); | ||
| 231 | + int paddingTop = getPaddingTop(); | ||
| 232 | + int paddingBottom = getPaddingBottom(); | ||
| 233 | + int h = getHeight() - paddingTop - paddingBottom; | ||
| 234 | + if (y < paddingTop) { | ||
| 235 | + y = paddingTop; | ||
| 236 | + } | ||
| 237 | + if (y > h + paddingTop) { | ||
| 238 | + y = h + paddingTop; | ||
| 239 | + } | ||
| 240 | + y -= paddingTop; | ||
| 241 | + if (action == MotionEvent.ACTION_DOWN) { | ||
| 242 | + if (mOnSeekBarChangeListener != null) { | ||
| 243 | + mOnSeekBarChangeListener.onRequestDisallowInterceptTouchEvent(true); | ||
| 244 | + } | ||
| 245 | + int progress = mMax * (h - y) / h; | ||
| 246 | + if (progress != mProgress) { | ||
| 247 | + if (mTrackingTouch == false) { | ||
| 248 | + mTrackingTouch = true; | ||
| 249 | + if (mOnSeekBarChangeListener != null) { | ||
| 250 | + mOnSeekBarChangeListener.onStartTrackingTouch(this); | ||
| 251 | + } | ||
| 252 | + } | ||
| 253 | + setProgress(progress, true); | ||
| 254 | + } | ||
| 255 | + } else if (action == MotionEvent.ACTION_MOVE) { | ||
| 256 | + int progress = mMax * (h - y) / h; | ||
| 257 | + if (progress != mProgress) { | ||
| 258 | + if (mTrackingTouch == false) { | ||
| 259 | + mTrackingTouch = true; | ||
| 260 | + if (mOnSeekBarChangeListener != null) { | ||
| 261 | + mOnSeekBarChangeListener.onStartTrackingTouch(this); | ||
| 262 | + } | ||
| 263 | + } | ||
| 264 | + setProgress(progress, true); | ||
| 265 | + } | ||
| 266 | + } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { | ||
| 267 | + if (mOnSeekBarChangeListener != null) { | ||
| 268 | + mOnSeekBarChangeListener.onRequestDisallowInterceptTouchEvent(false); | ||
| 269 | + } | ||
| 270 | + mTrackingTouch = false; | ||
| 271 | + if (mOnSeekBarChangeListener != null) { | ||
| 272 | + mOnSeekBarChangeListener.onStopTrackingTouch(this); | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + return super.dispatchTouchEvent(ev); | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + @Override | ||
| 279 | + protected void onSizeChanged(int w, int h, int oldw, int oldh) { | ||
| 280 | + updateProgress(false); | ||
| 281 | + //updateSecondaryProgress(); | ||
| 282 | + super.onSizeChanged(w, h, oldw, oldh); | ||
| 283 | + } | ||
| 284 | + | ||
| 285 | +} |
app/src/main/res/anim/slide_in_bottom.xml
0 → 100644
app/src/main/res/anim/slide_in_top.xml
0 → 100644
app/src/main/res/anim/slide_left_in.xml
0 → 100644
app/src/main/res/anim/slide_left_out.xml
0 → 100644
app/src/main/res/anim/slide_out_bottom.xml
0 → 100644
app/src/main/res/anim/slide_out_top.xml
0 → 100644
app/src/main/res/anim/slide_right_in.xml
0 → 100644
app/src/main/res/anim/slide_right_out.xml
0 → 100644
app/src/main/res/anim/slide_stand.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<set xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| 3 | + <translate | ||
| 4 | + android:duration="300" | ||
| 5 | + android:fromYDelta="0%" | ||
| 6 | + android:startOffset="0" | ||
| 7 | + android:toYDelta="0%" /> | ||
| 8 | +</set> | ||
| 0 | \ No newline at end of file | 9 | \ No newline at end of file |
app/src/main/res/drawable-xxhdpi/close.png
0 → 100644
498 Bytes
app/src/main/res/drawable-xxhdpi/exposure.png
0 → 100644
1.04 KB
app/src/main/res/drawable-xxhdpi/flash.png
0 → 100644
750 Bytes
app/src/main/res/drawable-xxhdpi/float_back.png
0 → 100644
1.2 KB
app/src/main/res/drawable-xxhdpi/float_close.png
0 → 100644
1.1 KB
app/src/main/res/drawable-xxhdpi/float_view.png
0 → 100644
688 Bytes
app/src/main/res/drawable-xxhdpi/icon_record_close_off.png deleted
100644 → 0
5.67 KB
app/src/main/res/drawable-xxhdpi/icon_record_flash_off.png deleted
100644 → 0
3.77 KB
app/src/main/res/drawable-xxhdpi/icon_record_flip_off.png deleted
100644 → 0
4.03 KB
app/src/main/res/drawable-xxhdpi/switch_camera.png
0 → 100644
975 Bytes
app/src/main/res/layout/activity_float.xml
0 → 100644
| 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:layout_width="match_parent" | ||
| 5 | + android:layout_height="match_parent" | ||
| 6 | + android:background="#00000000" | ||
| 7 | + tools:context="com.cnlive.demo.stream.FloatActivity"> | ||
| 8 | + | ||
| 9 | + <LinearLayout | ||
| 10 | + android:layout_width="match_parent" | ||
| 11 | + android:layout_height="48dp" | ||
| 12 | + android:background="@color/colorAccent"> | ||
| 13 | + | ||
| 14 | + <ImageView | ||
| 15 | + android:id="@+id/float_back" | ||
| 16 | + android:layout_width="25dp" | ||
| 17 | + android:layout_height="25dp" | ||
| 18 | + android:layout_alignParentLeft="true" | ||
| 19 | + android:layout_alignParentTop="true" | ||
| 20 | + android:layout_gravity="center_vertical" | ||
| 21 | + android:layout_marginLeft="10dp" | ||
| 22 | + android:scaleType="centerCrop" | ||
| 23 | + android:src="@drawable/float_back" /> | ||
| 24 | + | ||
| 25 | + </LinearLayout> | ||
| 26 | +</RelativeLayout> |
app/src/main/res/layout/activity_stream.xml
| @@ -19,6 +19,18 @@ | @@ -19,6 +19,18 @@ | ||
| 19 | android:layout_alignParentBottom="true" | 19 | android:layout_alignParentBottom="true" |
| 20 | android:layout_alignParentTop="true" /> | 20 | android:layout_alignParentTop="true" /> |
| 21 | 21 | ||
| 22 | + <com.cnlive.demo.stream.VerticalSeekBar | ||
| 23 | + android:id="@+id/exposure_seekBar" | ||
| 24 | + android:layout_width="wrap_content" | ||
| 25 | + android:layout_height="200dp" | ||
| 26 | + android:layout_alignParentRight="true" | ||
| 27 | + android:layout_marginRight="20dp" | ||
| 28 | + android:layout_marginTop="100dp" | ||
| 29 | + android:progressBackgroundTint="#9b9b9b" | ||
| 30 | + android:progressTint="@color/colorAccent" | ||
| 31 | + android:thumbTint="#C0303030" | ||
| 32 | + android:visibility="gone" /> | ||
| 33 | + | ||
| 22 | <include | 34 | <include |
| 23 | android:id="@+id/actionbar" | 35 | android:id="@+id/actionbar" |
| 24 | layout="@layout/titlebar" | 36 | layout="@layout/titlebar" |
app/src/main/res/layout/bottombar.xml
| @@ -21,6 +21,56 @@ | @@ -21,6 +21,56 @@ | ||
| 21 | android:layout_marginBottom="10dp" | 21 | android:layout_marginBottom="10dp" |
| 22 | android:orientation="horizontal"> | 22 | android:orientation="horizontal"> |
| 23 | 23 | ||
| 24 | + <Button | ||
| 25 | + android:id="@+id/audio_mix" | ||
| 26 | + android:layout_width="0dp" | ||
| 27 | + android:layout_height="wrap_content" | ||
| 28 | + android:layout_marginLeft="10dp" | ||
| 29 | + android:layout_weight="1" | ||
| 30 | + android:background="#11123456" | ||
| 31 | + android:text="音频滤镜" | ||
| 32 | + android:textColor="@android:color/white" | ||
| 33 | + android:textSize="14sp" /> | ||
| 34 | + | ||
| 35 | + <Button | ||
| 36 | + android:id="@+id/bgm_filter" | ||
| 37 | + android:layout_width="0dp" | ||
| 38 | + android:layout_height="wrap_content" | ||
| 39 | + android:layout_marginLeft="10dp" | ||
| 40 | + android:layout_weight="1" | ||
| 41 | + android:background="#11123456" | ||
| 42 | + android:text="bgm升降调" | ||
| 43 | + android:textColor="@android:color/white" /> | ||
| 44 | + | ||
| 45 | + <Button | ||
| 46 | + android:id="@+id/click_to_capture_screenshot" | ||
| 47 | + android:layout_width="0dp" | ||
| 48 | + android:layout_height="wrap_content" | ||
| 49 | + android:layout_marginLeft="10dp" | ||
| 50 | + android:layout_weight="1" | ||
| 51 | + android:background="#11123456" | ||
| 52 | + android:text="截屏" | ||
| 53 | + android:textColor="@android:color/white" /> | ||
| 54 | + | ||
| 55 | + <CheckBox | ||
| 56 | + android:id="@+id/bg_img" | ||
| 57 | + android:layout_width="0dp" | ||
| 58 | + android:layout_height="wrap_content" | ||
| 59 | + android:layout_gravity="center" | ||
| 60 | + android:layout_marginLeft="10dp" | ||
| 61 | + android:layout_weight="1" | ||
| 62 | + android:background="#11123456" | ||
| 63 | + android:text="背景图推流" | ||
| 64 | + android:textColor="@android:color/white" | ||
| 65 | + android:textSize="14sp" /> | ||
| 66 | + </LinearLayout> | ||
| 67 | + | ||
| 68 | + <LinearLayout | ||
| 69 | + android:layout_width="match_parent" | ||
| 70 | + android:layout_height="wrap_content" | ||
| 71 | + android:layout_marginBottom="10dp" | ||
| 72 | + android:orientation="horizontal"> | ||
| 73 | + | ||
| 24 | <CheckBox | 74 | <CheckBox |
| 25 | android:id="@+id/watermark" | 75 | android:id="@+id/watermark" |
| 26 | android:layout_width="0dp" | 76 | android:layout_width="0dp" |
| @@ -126,7 +176,7 @@ | @@ -126,7 +176,7 @@ | ||
| 126 | android:layout_weight="1" | 176 | android:layout_weight="1" |
| 127 | android:background="#11123456" | 177 | android:background="#11123456" |
| 128 | android:checked="true" | 178 | android:checked="true" |
| 129 | - android:text="美颜" | 179 | + android:text="美颜滤镜" |
| 130 | android:textColor="@android:color/white" | 180 | android:textColor="@android:color/white" |
| 131 | android:textSize="14sp"></CheckBox> | 181 | android:textSize="14sp"></CheckBox> |
| 132 | 182 |
app/src/main/res/layout/float_preview.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<com.cnlive.demo.stream.FloatView | ||
| 3 | + xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 4 | + android:layout_width="wrap_content" | ||
| 5 | + android:layout_height="wrap_content" | ||
| 6 | + android:orientation="horizontal"> | ||
| 7 | + | ||
| 8 | +</com.cnlive.demo.stream.FloatView> | ||
| 0 | \ No newline at end of file | 9 | \ No newline at end of file |
app/src/main/res/layout/titlebar.xml
| @@ -17,36 +17,56 @@ | @@ -17,36 +17,56 @@ | ||
| 17 | android:textSize="19sp" /> | 17 | android:textSize="19sp" /> |
| 18 | 18 | ||
| 19 | <ImageView | 19 | <ImageView |
| 20 | + android:id="@+id/add" | ||
| 21 | + android:layout_width="22dp" | ||
| 22 | + android:layout_height="22dp" | ||
| 23 | + android:layout_gravity="center_vertical" | ||
| 24 | + android:layout_marginRight="10dp" | ||
| 25 | + android:layout_marginTop="8dp" | ||
| 26 | + android:layout_toLeftOf="@+id/exposure" | ||
| 27 | + android:background="@drawable/float_view" /> | ||
| 28 | + | ||
| 29 | + <ImageView | ||
| 30 | + android:id="@+id/exposure" | ||
| 31 | + android:layout_width="22dp" | ||
| 32 | + android:layout_height="22dp" | ||
| 33 | + android:layout_gravity="center_vertical" | ||
| 34 | + android:layout_marginRight="10dp" | ||
| 35 | + android:layout_marginTop="8dp" | ||
| 36 | + android:layout_toLeftOf="@+id/flash" | ||
| 37 | + android:background="@drawable/exposure" /> | ||
| 38 | + | ||
| 39 | + <ImageView | ||
| 20 | android:id="@+id/flash" | 40 | android:id="@+id/flash" |
| 21 | - android:layout_width="wrap_content" | ||
| 22 | - android:layout_height="wrap_content" | 41 | + android:layout_width="22dp" |
| 42 | + android:layout_height="22dp" | ||
| 23 | android:layout_gravity="center_vertical" | 43 | android:layout_gravity="center_vertical" |
| 24 | android:layout_marginRight="12dp" | 44 | android:layout_marginRight="12dp" |
| 25 | android:layout_marginTop="8dp" | 45 | android:layout_marginTop="8dp" |
| 26 | android:layout_toLeftOf="@+id/switch_cam" | 46 | android:layout_toLeftOf="@+id/switch_cam" |
| 27 | - android:background="@drawable/icon_record_flash_off"></ImageView> | 47 | + android:background="@drawable/flash"></ImageView> |
| 28 | 48 | ||
| 29 | <ImageView | 49 | <ImageView |
| 30 | android:id="@+id/switch_cam" | 50 | android:id="@+id/switch_cam" |
| 31 | - android:layout_width="wrap_content" | ||
| 32 | - android:layout_height="wrap_content" | 51 | + android:layout_width="22dp" |
| 52 | + android:layout_height="22dp" | ||
| 33 | android:layout_alignParentTop="true" | 53 | android:layout_alignParentTop="true" |
| 34 | android:layout_gravity="center_vertical" | 54 | android:layout_gravity="center_vertical" |
| 35 | android:layout_marginRight="12dp" | 55 | android:layout_marginRight="12dp" |
| 36 | android:layout_marginTop="8dp" | 56 | android:layout_marginTop="8dp" |
| 37 | android:layout_toLeftOf="@+id/backoff" | 57 | android:layout_toLeftOf="@+id/backoff" |
| 38 | - android:background="@drawable/icon_record_flip_off" /> | 58 | + android:background="@drawable/switch_camera" /> |
| 39 | 59 | ||
| 40 | <ImageView | 60 | <ImageView |
| 41 | android:id="@+id/backoff" | 61 | android:id="@+id/backoff" |
| 42 | - android:layout_width="wrap_content" | ||
| 43 | - android:layout_height="wrap_content" | 62 | + android:layout_width="22dp" |
| 63 | + android:layout_height="22dp" | ||
| 44 | android:layout_alignParentEnd="true" | 64 | android:layout_alignParentEnd="true" |
| 45 | android:layout_alignParentRight="true" | 65 | android:layout_alignParentRight="true" |
| 46 | android:layout_alignParentTop="true" | 66 | android:layout_alignParentTop="true" |
| 47 | android:layout_gravity="center_vertical" | 67 | android:layout_gravity="center_vertical" |
| 48 | android:layout_marginRight="2dp" | 68 | android:layout_marginRight="2dp" |
| 49 | android:layout_marginTop="8dp" | 69 | android:layout_marginTop="8dp" |
| 50 | - android:background="@drawable/icon_record_close_off" /> | 70 | + android:background="@drawable/close" /> |
| 51 | 71 | ||
| 52 | </RelativeLayout> | 72 | </RelativeLayout> |
| 53 | \ No newline at end of file | 73 | \ No newline at end of file |
app/src/main/res/raw/beep.ogg
0 → 100644
No preview for this file type
app/src/main/res/raw/fragment_sharder.glsl
0 → 100644
| 1 | +#extension GL_OES_EGL_image_external : require | ||
| 2 | +precision mediump float; | ||
| 3 | +uniform samplerExternalOES texture; | ||
| 4 | +varying vec2 v_TexCoordinate; | ||
| 5 | + | ||
| 6 | +void main () { | ||
| 7 | + vec4 color = texture2D(texture, v_TexCoordinate); | ||
| 8 | + gl_FragColor = color; | ||
| 9 | +} | ||
| 0 | \ No newline at end of file | 10 | \ No newline at end of file |
app/src/main/res/raw/fugu.acv
0 → 100644
No preview for this file type
app/src/main/res/raw/jiaopian.acv
0 → 100644
No preview for this file type
app/src/main/res/raw/realm_properties
0 → 100644
app/src/main/res/raw/tone_cuver_sample.acv
0 → 100644
No preview for this file type
app/src/main/res/raw/vetext_sharder.glsl
0 → 100644
| 1 | +attribute vec4 vPosition; | ||
| 2 | +attribute vec4 vTexCoordinate; | ||
| 3 | +uniform mat4 textureTransform; | ||
| 4 | +varying vec2 v_TexCoordinate; | ||
| 5 | + | ||
| 6 | +void main () { | ||
| 7 | + v_TexCoordinate = (textureTransform * vTexCoordinate).xy; | ||
| 8 | + gl_Position = vPosition; | ||
| 9 | +} | ||
| 0 | \ No newline at end of file | 10 | \ No newline at end of file |
app/src/main/res/values/styles.xml
| @@ -8,4 +8,28 @@ | @@ -8,4 +8,28 @@ | ||
| 8 | <item name="colorAccent">@color/colorAccent</item> | 8 | <item name="colorAccent">@color/colorAccent</item> |
| 9 | </style> | 9 | </style> |
| 10 | 10 | ||
| 11 | + <style name="DefaultAnimation" parent="@android:style/Animation.Activity"> | ||
| 12 | + <item name="android:activityOpenEnterAnimation">@anim/slide_right_in</item> | ||
| 13 | + <item name="android:activityOpenExitAnimation">@anim/slide_left_out</item> | ||
| 14 | + <item name="android:activityCloseEnterAnimation">@anim/slide_left_in</item> | ||
| 15 | + <item name="android:activityCloseExitAnimation">@anim/slide_right_out</item> | ||
| 16 | + </style> | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + <style name="Theme.NoTitleBar" parent="Theme.AppCompat.Light.NoActionBar"> | ||
| 20 | + <item name="android:windowContentOverlay">@null</item> | ||
| 21 | + <item name="android:windowAnimationStyle">@style/DefaultAnimation</item> | ||
| 22 | + </style> | ||
| 23 | + | ||
| 24 | + <style name="RiseAnimation" parent="@android:style/Animation.Activity"> | ||
| 25 | + <item name="android:activityOpenEnterAnimation">@anim/slide_in_bottom</item> | ||
| 26 | + <item name="android:activityOpenExitAnimation">@anim/slide_stand</item> | ||
| 27 | + <item name="android:activityCloseEnterAnimation">@anim/slide_left_in</item> | ||
| 28 | + <item name="android:activityCloseExitAnimation">@anim/slide_right_out</item> | ||
| 29 | + </style> | ||
| 30 | + | ||
| 31 | + <style name="Theme.RiseAnimation" parent="@style/Theme.NoTitleBar"> | ||
| 32 | + <item name="android:windowAnimationStyle">@style/RiseAnimation</item> | ||
| 33 | + </style> | ||
| 34 | + | ||
| 11 | </resources> | 35 | </resources> |