Skip to content

Commit

Permalink
增加系统播放内核的 PlaceholderSurface
Browse files Browse the repository at this point in the history
  • Loading branch information
CarGuo committed Jun 17, 2024
1 parent 22c0d75 commit 30d6fbf
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ protected void onCreate(Bundle savedInstanceState) {
//String url = "http://baobab.wd jcdn.com/14564977406580.mp4";
List<GSYVideoModel> urls = new ArrayList<>();
urls.add(new GSYVideoModel("http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear3/prog_index.m3u8", "标题1"));
urls.add(new GSYVideoModel("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4", "标题2"));
urls.add(new GSYVideoModel("https://res.exexm.com/cw_145225549855002", "标题3"));
urls.add(new GSYVideoModel("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4", "标题4"));
// urls.add(new GSYVideoModel("https://res.exexm.com/cw_145225549855002", "标题2"));
// urls.add(new GSYVideoModel("http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear3/prog_index.m3u8", "标题3"));
// urls.add(new GSYVideoModel("https://res.exexm.com/cw_145225549855002", "标题4"));
binding.detailPlayer.setUp(urls, true, 0);

//增加封面
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
package com.shuyu.gsyvideoplayer.placeholder;

import static java.lang.annotation.ElementType.TYPE_USE;

import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.os.Build;
import android.os.Handler;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** Generates a {@link SurfaceTexture} using EGL/GLES functions. */
@RequiresApi(17)
public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableListener, Runnable {

/** Listener to be called when the texture image on {@link SurfaceTexture} has been updated. */
public interface TextureImageListener {
/** Called when the {@link SurfaceTexture} receives a new frame from its image producer. */
void onFrameAvailable();
}

/**
* Secure mode to be used by the EGL surface and context. One of {@link #SECURE_MODE_NONE}, {@link
* #SECURE_MODE_SURFACELESS_CONTEXT} or {@link #SECURE_MODE_PROTECTED_PBUFFER}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({SECURE_MODE_NONE, SECURE_MODE_SURFACELESS_CONTEXT, SECURE_MODE_PROTECTED_PBUFFER})
public @interface SecureMode {}

/** No secure EGL surface and context required. */
public static final int SECURE_MODE_NONE = 0;

/** Creating a surfaceless, secured EGL context. */
public static final int SECURE_MODE_SURFACELESS_CONTEXT = 1;

/** Creating a secure surface backed by a pixel buffer. */
public static final int SECURE_MODE_PROTECTED_PBUFFER = 2;

private static final int EGL_SURFACE_WIDTH = 1;
private static final int EGL_SURFACE_HEIGHT = 1;

private static final int[] EGL_CONFIG_ATTRIBUTES =
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 0,
EGL14.EGL_CONFIG_CAVEAT, EGL14.EGL_NONE,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
EGL14.EGL_NONE
};

private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0;

private final Handler handler;
private final int[] textureIdHolder;
@Nullable private final TextureImageListener callback;

@Nullable private EGLDisplay display;
@Nullable private EGLContext context;
@Nullable private EGLSurface surface;
@Nullable private SurfaceTexture texture;

/**
* @param handler The {@link Handler} that will be used to call {@link
* SurfaceTexture#updateTexImage()} to update images on the {@link SurfaceTexture}. Note that
* {@link #init(int)} has to be called on the same looper thread as the {@link Handler}'s
* looper.
*/
public EGLSurfaceTexture(Handler handler) {
this(handler, /* callback= */ null);
}

/**
* @param handler The {@link Handler} that will be used to call {@link
* SurfaceTexture#updateTexImage()} to update images on the {@link SurfaceTexture}. Note that
* {@link #init(int)} has to be called on the same looper thread as the looper of the {@link
* Handler}.
* @param callback The {@link TextureImageListener} to be called when the texture image on {@link
* SurfaceTexture} has been updated. This callback will be called on the same handler thread
* as the {@code handler}.
*/
public EGLSurfaceTexture(Handler handler, @Nullable TextureImageListener callback) {
this.handler = handler;
this.callback = callback;
textureIdHolder = new int[1];
}

/**
* Initializes required EGL parameters and creates the {@link SurfaceTexture}.
*
* @param secureMode The {@link SecureMode} to be used for EGL surface.
*/
public void init(@SecureMode int secureMode) {
display = getDefaultDisplay();
EGLConfig config = chooseEGLConfig(display);
context = createEGLContext(display, config, secureMode);
surface = createEGLSurface(display, config, context, secureMode);
generateTextureIds(textureIdHolder);
texture = new SurfaceTexture(textureIdHolder[0]);
texture.setOnFrameAvailableListener(this);
}

/** Releases all allocated resources. */
@SuppressWarnings("nullness:argument")
public void release() {
handler.removeCallbacks(this);
try {
if (texture != null) {
texture.release();
GLES20.glDeleteTextures(1, textureIdHolder, 0);
}
} finally {
if (display != null && !display.equals(EGL14.EGL_NO_DISPLAY)) {
EGL14.eglMakeCurrent(
display, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
}
if (surface != null && !surface.equals(EGL14.EGL_NO_SURFACE)) {
EGL14.eglDestroySurface(display, surface);
}
if (context != null) {
EGL14.eglDestroyContext(display, context);
}
// EGL14.eglReleaseThread could crash before Android K (see [internal: b/11327779]).
if (Build.VERSION.SDK_INT >= 19) {
EGL14.eglReleaseThread();
}
if (display != null && !display.equals(EGL14.EGL_NO_DISPLAY)) {
// Android is unusual in that it uses a reference-counted EGLDisplay. So for
// every eglInitialize() we need an eglTerminate().
EGL14.eglTerminate(display);
}
display = null;
context = null;
surface = null;
texture = null;
}
}

/**
* Returns the wrapped {@link SurfaceTexture}. This can only be called after {@link #init(int)}.
*/
public SurfaceTexture getSurfaceTexture() {
return texture;
}

// SurfaceTexture.OnFrameAvailableListener

@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
handler.post(this);
}

// Runnable

@Override
public void run() {
// Run on the provided handler thread when a new image frame is available.
dispatchOnFrameAvailable();
if (texture != null) {
try {
texture.updateTexImage();
} catch (RuntimeException e) {
// Ignore
}
}
}

private void dispatchOnFrameAvailable() {
if (callback != null) {
callback.onFrameAvailable();
}
}

private static EGLDisplay getDefaultDisplay() {
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);

int[] version = new int[2];
boolean eglInitialized =
EGL14.eglInitialize(display, version, /* majorOffset= */ 0, version, /* minorOffset= */ 1);
return display;
}

private static EGLConfig chooseEGLConfig(EGLDisplay display) {
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
boolean success =
EGL14.eglChooseConfig(
display,
EGL_CONFIG_ATTRIBUTES,
/* attrib_listOffset= */ 0,
configs,
/* configsOffset= */ 0,
/* config_size= */ 1,
numConfigs,
/* num_configOffset= */ 0);

return configs[0];
}

private static EGLContext createEGLContext(
EGLDisplay display, EGLConfig config, @SecureMode int secureMode) {
int[] glAttributes;
if (secureMode == SECURE_MODE_NONE) {
glAttributes = new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
} else {
glAttributes =
new int[] {
EGL14.EGL_CONTEXT_CLIENT_VERSION,
2,
EGL_PROTECTED_CONTENT_EXT,
EGL14.EGL_TRUE,
EGL14.EGL_NONE
};
}
EGLContext context =
EGL14.eglCreateContext(
display, config, android.opengl.EGL14.EGL_NO_CONTEXT, glAttributes, 0);
return context;
}

private static EGLSurface createEGLSurface(
EGLDisplay display, EGLConfig config, EGLContext context, @SecureMode int secureMode) {
EGLSurface surface;
if (secureMode == SECURE_MODE_SURFACELESS_CONTEXT) {
surface = EGL14.EGL_NO_SURFACE;
} else {
int[] pbufferAttributes;
if (secureMode == SECURE_MODE_PROTECTED_PBUFFER) {
pbufferAttributes =
new int[] {
EGL14.EGL_WIDTH,
EGL_SURFACE_WIDTH,
EGL14.EGL_HEIGHT,
EGL_SURFACE_HEIGHT,
EGL_PROTECTED_CONTENT_EXT,
EGL14.EGL_TRUE,
EGL14.EGL_NONE
};
} else {
pbufferAttributes =
new int[] {
EGL14.EGL_WIDTH,
EGL_SURFACE_WIDTH,
EGL14.EGL_HEIGHT,
EGL_SURFACE_HEIGHT,
EGL14.EGL_NONE
};
}
surface = EGL14.eglCreatePbufferSurface(display, config, pbufferAttributes, /* offset= */ 0);
}

boolean eglMadeCurrent =
EGL14.eglMakeCurrent(display, /* draw= */ surface, /* read= */ surface, context);
return surface;
}

private static void generateTextureIds(int[] textureIdHolder) {
GLES20.glGenTextures(/* n= */ 1, textureIdHolder, /* offset= */ 0);
}
}
Loading

0 comments on commit 30d6fbf

Please sign in to comment.