Skip to content

Commit

Permalink
Merge pull request Doikki#789 from Doikki/dev
Browse files Browse the repository at this point in the history
接入GLSurfaceView,实现视频加水印功能
  • Loading branch information
Doikki authored Aug 17, 2022
2 parents f0d4148 + 0cd6e6a commit fe01896
Show file tree
Hide file tree
Showing 13 changed files with 581 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#extension GL_OES_EGL_image_external : require
precision mediump float;
// External texture containing video decoder output.
uniform samplerExternalOES uTexSampler0;
// Texture containing the overlap bitmap.
uniform sampler2D uTexSampler1;
// Horizontal scaling factor for the overlap bitmap.
uniform float uScaleX;
// Vertical scaling factory for the overlap bitmap.
uniform float uScaleY;
varying vec2 vTexCoords;
void main() {
vec4 videoColor = texture2D(uTexSampler0, vTexCoords);
vec4 overlayColor = texture2D(uTexSampler1,
vec2(vTexCoords.x * uScaleX,
vTexCoords.y * uScaleY));
// Blend the video decoder output and the overlay bitmap.
gl_FragColor = videoColor * (1.0 - overlayColor.a)
+ overlayColor * overlayColor.a;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
attribute vec4 aFramePosition;
attribute vec4 aTexCoords;
uniform mat4 uTexTransform;
varying vec2 vTexCoords;
void main() {
gl_Position = aFramePosition;
vTexCoords = (uTexTransform * aTexCoords).xy;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import xyz.doikki.dkplayer.util.IntentKeys
import xyz.doikki.dkplayer.util.Utils
import xyz.doikki.dkplayer.widget.component.DebugInfoView
import xyz.doikki.dkplayer.widget.component.PlayerMonitor
import xyz.doikki.dkplayer.widget.render.gl.GLSurfaceRenderViewFactory
import xyz.doikki.videocontroller.StandardVideoController
import xyz.doikki.videocontroller.component.*
import xyz.doikki.videoplayer.player.BaseVideoView
Expand Down Expand Up @@ -104,6 +105,10 @@ class PlayerActivity : BaseActivity<VideoView>() {
//播放状态监听
mVideoView.addOnStateChangeListener(mOnStateChangeListener)

// 临时切换RenderView, 如需全局请通过VideoConfig配置,详见MyApplication
if (intent.getBooleanExtra(IntentKeys.CUSTOM_RENDER, false)) {
mVideoView.setRenderViewFactory(GLSurfaceRenderViewFactory.create())
}
//临时切换播放核心,如需全局请通过VideoConfig配置,详见MyApplication
//使用IjkPlayer解码
// mVideoView.setPlayerFactory(IjkPlayerFactory.create())
Expand Down Expand Up @@ -208,11 +213,12 @@ class PlayerActivity : BaseActivity<VideoView>() {
"https://cms-bucket.nosdn.127.net/eb411c2810f04ffa8aaafc42052b233820180418095416.jpeg"

@JvmStatic
fun start(context: Context, url: String, title: String, isLive: Boolean) {
fun start(context: Context, url: String, title: String, isLive: Boolean, customRender: Boolean = false) {
val intent = Intent(context, PlayerActivity::class.java)
intent.putExtra(IntentKeys.URL, url)
intent.putExtra(IntentKeys.IS_LIVE, isLive)
intent.putExtra(IntentKeys.TITLE, title)
intent.putExtra(IntentKeys.CUSTOM_RENDER, customRender)
context.startActivity(intent)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public void onCreate() {
.setPlayerFactory(ExoMediaPlayerFactory.create())
// 设置自己的渲染view,内部默认TextureView实现
// .setRenderViewFactory(SurfaceRenderViewFactory.create())
// GLSurfaceView 可对视频加滤镜
// .setRenderViewFactory(GLSurfaceRenderViewFactory.create())
// 根据手机重力感应自动切换横竖屏,默认false
// .setEnableOrientation(true)
// 监听系统中其他播放器是否获取音频焦点,实现不与其他播放器同时播放的效果,默认true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,19 @@ protected void initView() {
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_vod:
PlayerActivity.start(getActivity(), VOD_URL, getString(R.string.str_api_vod), false);
PlayerActivity.start(getActivity(), VOD_URL, getString(R.string.str_api_vod), false, false);
break;
case R.id.btn_live:
PlayerActivity.start(getActivity(), LIVE_URL, getString(R.string.str_api_live), true);
PlayerActivity.start(getActivity(), LIVE_URL, getString(R.string.str_api_live), true, false);
break;
case R.id.btn_music:
PlayerActivity.start(getActivity(), MUSIC_URL, getString(R.string.str_api_music), false);
PlayerActivity.start(getActivity(), MUSIC_URL, getString(R.string.str_api_music), false, false);
break;
case R.id.btn_file:
// 此处演示的是播放私有目录的文件,如果是共有目录需要存储权限
String url = "file://" + requireContext().getExternalCacheDir().getAbsolutePath() + "/test.mp4";
L.d("play local file: " + url);
PlayerActivity.start(getActivity(), url, getString(R.string.str_file), false);
PlayerActivity.start(getActivity(), url, getString(R.string.str_file), false, false);
break;
case R.id.btn_raw_assets:
startActivity(new Intent(getActivity(), PlayRawAssetsActivity.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
import android.view.View;

import xyz.doikki.dkplayer.R;
import xyz.doikki.dkplayer.activity.extend.DefinitionPlayerActivity;
import xyz.doikki.dkplayer.activity.api.PlayerActivity;
import xyz.doikki.dkplayer.activity.extend.ADActivity;
import xyz.doikki.dkplayer.activity.extend.CacheActivity;
import xyz.doikki.dkplayer.activity.extend.CustomExoPlayerActivity;
import xyz.doikki.dkplayer.activity.extend.CustomIjkPlayerActivity;
import xyz.doikki.dkplayer.activity.extend.DanmakuActivity;
import xyz.doikki.dkplayer.activity.extend.DefinitionPlayerActivity;
import xyz.doikki.dkplayer.activity.extend.FullScreenActivity;
import xyz.doikki.dkplayer.activity.extend.PadActivity;
import xyz.doikki.dkplayer.activity.extend.PlayListActivity;
import xyz.doikki.dkplayer.fragment.BaseFragment;
import xyz.doikki.dkplayer.util.DataUtil;

public class ExtensionFragment extends BaseFragment implements View.OnClickListener {
@Override
Expand All @@ -33,6 +35,7 @@ protected void initView() {
findViewById(R.id.btn_custom_exo_player).setOnClickListener(this);
findViewById(R.id.btn_custom_ijk_player).setOnClickListener(this);
findViewById(R.id.btn_definition).setOnClickListener(this);
findViewById(R.id.btn_custom_render_view).setOnClickListener(this);
}

@Override
Expand Down Expand Up @@ -65,6 +68,9 @@ public void onClick(View v) {
case R.id.btn_definition:
startActivity(new Intent(getActivity(), DefinitionPlayerActivity.class));
break;
case R.id.btn_custom_render_view:
PlayerActivity.start(getActivity(), DataUtil.SAMPLE_URL, getString(R.string.str_custom_render_view), false, true);
break;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public class IntentKeys {
public static final String SEAMLESS_PLAY = "seamless_play";
public static final String TITLE = "title";
public static final String IS_LIVE = "isLive";
public static final String CUSTOM_RENDER = "custom_render";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.doikki.dkplayer.widget.render.gl;

import static com.google.android.exoplayer2.util.Assertions.checkNotNull;

import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.opengl.GLES20;
import android.opengl.GLUtils;

import com.google.android.exoplayer2.util.GlProgram;
import com.google.android.exoplayer2.util.GlUtil;

import java.io.IOException;

import javax.microedition.khronos.opengles.GL10;

/**
* Video processor that demonstrates how to overlay a bitmap on video output using a GL shader. The
* bitmap is drawn using an Android {@link Canvas}.
*/
/* package */ final class BitmapOverlayVideoProcessor implements GLSurfaceRenderView.VideoProcessor {

private static final int OVERLAY_WIDTH = 512;
private static final int OVERLAY_HEIGHT = 256;

private final Context context;
private final Paint paint;
private final int[] textures;
private final Bitmap overlayBitmap;
private final Bitmap logoBitmap;
private final Canvas overlayCanvas;

private GlProgram program;

private float bitmapScaleX;
private float bitmapScaleY;

public BitmapOverlayVideoProcessor(Context context) {
this.context = context.getApplicationContext();
paint = new Paint();
paint.setTextSize(64);
paint.setAntiAlias(true);
paint.setARGB(0xFF, 0xFF, 0x00, 0x00);
textures = new int[1];
overlayBitmap = Bitmap.createBitmap(OVERLAY_WIDTH, OVERLAY_HEIGHT, Bitmap.Config.ARGB_8888);
overlayCanvas = new Canvas(overlayBitmap);
try {
logoBitmap =
((BitmapDrawable)
context.getPackageManager().getApplicationIcon(context.getPackageName()))
.getBitmap();
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException(e);
}
}

@Override
public void initialize() {
try {
program =
new GlProgram(
context,
/* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl",
/* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl");
} catch (IOException e) {
throw new IllegalStateException(e);
}
program.setBufferAttribute(
"aFramePosition",
GlUtil.getNormalizedCoordinateBounds(),
GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE);
program.setBufferAttribute(
"aTexCoords",
GlUtil.getTextureCoordinateBounds(),
GlUtil.HOMOGENEOUS_COORDINATE_VECTOR_SIZE);
GLES20.glGenTextures(1, textures, 0);
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, /* level= */ 0, overlayBitmap, /* border= */ 0);
}

@Override
public void setSurfaceSize(int width, int height) {
bitmapScaleX = (float) width / OVERLAY_WIDTH;
bitmapScaleY = (float) height / OVERLAY_HEIGHT;
}

@Override
public void draw(int frameTexture, float[] transformMatrix) {
// Draw to the canvas and store it in a texture.
String text = "视频水印";
overlayBitmap.eraseColor(Color.TRANSPARENT);
overlayCanvas.drawBitmap(logoBitmap, /* left= */ 32, /* top= */ 32, paint);
overlayCanvas.drawText(text, /* x= */ 200, /* y= */ 130, paint);
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
GLUtils.texSubImage2D(
GL10.GL_TEXTURE_2D, /* level= */ 0, /* xoffset= */ 0, /* yoffset= */ 0, overlayBitmap);
GlUtil.checkGlError();

// Run the shader program.
GlProgram program = checkNotNull(this.program);
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* texUnitIndex= */ 0);
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* texUnitIndex= */ 1);
program.setFloatUniform("uScaleX", bitmapScaleX);
program.setFloatUniform("uScaleY", bitmapScaleY);
program.setFloatsUniform("uTexTransform", transformMatrix);
program.bindAttributesAndUniforms();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
GlUtil.checkGlError();
}

@Override
public void release() {
if (program != null) {
program.delete();
}
}
}
Loading

0 comments on commit fe01896

Please sign in to comment.