- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 android vlc (LibVLC
) 和 TextureView
以便在我的 android 应用程序中播放实时 rtsp 流。一切工作正常,但是我需要为某些对象检测任务尽可能获取当前播放帧,我正在使用 getBitmap()
函数来执行此操作。这里的问题是这个函数太慢了,随着 TextureView
中渲染的图像大小的增加,它需要越来越多的时间。
那么还有其他更快的方法吗?
请注意,我已经在 TextureView 和 SurfaceView 上尝试了 getDrawingCache()
函数,但它总是返回一个透明位图,所以经过一些小的研究我意识到这是因为 VLC 使用硬件加速以在表面纹理上渲染帧。
我确实也找到了许多与 this answer by fadden 类似的解决方案谈论使用 glReadPixels()
函数并指向 grafika作为代码示例的引用。然而(不幸的是)我几乎没有使用 OpenGL 的技能。 因此,如果您可以验证链接的答案,能否请您将我链接到一个简单的直接代码示例(关于我的案例)?
public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener,
org.videolan.libvlc.media.MediaPlayer.OnBufferingUpdateListener,
org.videolan.libvlc.media.MediaPlayer.OnCompletionListener,
org.videolan.libvlc.media.MediaPlayer.OnPreparedListener,
org.videolan.libvlc.media.MediaPlayer.OnVideoSizeChangedListener {
private AppCompatActivity me = this;
private MediaPlayer mMediaPlayer;
private TextureView mTextureViewmTextureView;
private String mUrl = "/storage/emulated/0/videos/test.mp4";
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
mMediaPlayer = new MediaPlayer(VLCInstance.get());
mTextureViewmTextureView = (TextureView) findViewById(R.id.player);
mTextureView.setSurfaceTextureListener(this);
}
private void attachViewSurface() {
final IVLCVout vlcVout = mMediaPlayer.getVLCVout();
mMediaPlayer.setScale(0);
vlcVout.detachViews();
vlcVout.setVideoView(mTextureView);
vlcVout.setWindowSize(mTextureView.getWidth(), mTextureView.getHeight());
vlcVout.attachViews();
mTextureView.setKeepScreenOn(true);
}
private void play(String path) {
try {
Media media;
if (new File(path).exists()) {
media = new Media(VLCInstance.get(), path);
} else {
media = new Media(VLCInstance.get(), Uri.parse(path));
}
mMediaPlayer.setMedia(media);
mMediaPlayer.play();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
attachViewSurface();
if (mMediaPlayer.hasMedia())
mMediaPlayer.play();
else
play(mUrl);
}
public Bitmap getImage() {
return mTextureView.getBitmap();
}
}
最佳答案
经过这么长时间,我决定给出这个答案,我现在将其用作替代方案。我发现 JavaCPP 的 FFMpegFrameGrabber
可用于播放 rtsp 流
或视频文件,但是这里有两个问题:
FFMpegFrameGrabber.Grab()
直接读取可用的下一帧,速度太慢,以至于我无法在我的设备上抓取每秒不超过 6 帧 (CPU:1.5 GHz 64 位八核 ARM Cortex-A53)FFMpegFrameGrabber
没有渲染能力,它只是将当前视频帧抓取到一个 OpenCV Mat
对象或 Javacv Frame
对象(你可以使用相同库的类 AndroidFrameConverter
将 Frame 对象转换为 Bitmap
)。关于第一个问题,在我的情况下,我不需要超过 5 fps
就可以解决它。
对于第二个,我开发了一个基于 OpenGL 位图的渲染器
,它几乎可以立即渲染抓取器抓取的位图图像(非常快)。这是我的代码:
app.gradle:
implementation group: 'org.bytedeco', name: 'javacv-platform', version: '1.4.3'
implementation group: 'org.bytedeco', name: 'javacv', version: '1.4.3'
抓取器:
class Player extends AsyncTask<BitmapRenderer, Bitmap, Object> {
BitmapRenderer glRenderer;
FFmpegFrameGrabber grabber = null;
@Override
protected Bitmap doInBackground(BitmapRenderer... objects) {
glRenderer = objects[0];
try {
grabber = new FFmpegFrameGrabber("/storage/emulated/0/Download/test.mp4");
grabber.start();
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
Frame grabbedImage;
while ((grabbedImage = grabber.grabImage()) != null) {
Log.e("Android", "Frame Grabbed " + grabbedImage.imageWidth + "x" + grabbedImage.imageHeight);
AndroidFrameConverter frameConverter = new AndroidFrameConverter();
Bitmap bitmap = frameConverter.convert(grabbedImage);
publishProgress(bitmap);
opencv_core.Mat grabbedMat = converter.convert(grabbedImage);
if (grabbedMat != null)
imwrite("/storage/emulated/0/Download/videoplayback.jpg", grabbedMat);
}
} catch (FrameGrabber.Exception e) {
e.printStackTrace();
Log.e("Android", e.getMessage(), e);
}
return null;
}
@Override
protected void onProgressUpdate(Bitmap... values) {
super.onProgressUpdate(values);
glRenderer.draw(values[0]);
}
@Override
protected void onPostExecute(Object objects) {
super.onPostExecute(objects);
try {
grabber.stop();
grabber.release();
} catch (FrameGrabber.Exception e1) {
}
}
}
渲染器:
package com.example.gphspc.javacvtest;
import android.graphics.Bitmap;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.view.ViewGroup;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class BitmapRenderer implements GLSurfaceView.Renderer {
private int[] textures;
private Bitmap bitmap;
private GLSurfaceView glSurfaceView;
private int parentWidth, parentHeight;
private boolean sizeModified = false;
public BitmapRenderer(GLSurfaceView glSurfaceView) {
this.glSurfaceView = glSurfaceView;
this.glSurfaceView.setEGLContextClientVersion(1);
this.glSurfaceView.setRenderer(this);
this.glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
private static final float[] VERTEX_COORDINATES = new float[]{
-1.0f, +1.0f, 0.0f,
+1.0f, +1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
+1.0f, -1.0f, 0.0f
};
private static final float[] TEXTURE_COORDINATES = new float[]{
0.0f, 0.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f
};
private static final Buffer TEXCOORD_BUFFER = ByteBuffer.allocateDirect(TEXTURE_COORDINATES.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer().put(TEXTURE_COORDINATES).rewind();
private static final Buffer VERTEX_BUFFER = ByteBuffer.allocateDirect(VERTEX_COORDINATES.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer().put(VERTEX_COORDINATES).rewind();
public void draw(Bitmap bitmap) {
if (bitmap == null)
return;
this.bitmap = bitmap;
if (!sizeModified) {
ViewGroup.LayoutParams layoutParams = glSurfaceView.getLayoutParams();
Dimension newDims = getRelativeSize(new Dimension(bitmap.getWidth(), bitmap.getHeight()), glSurfaceView.getWidth(), glSurfaceView.getHeight());
layoutParams.width = newDims.getWidth();
layoutParams.height = newDims.getHeight();
glSurfaceView.setLayoutParams(layoutParams);
sizeModified = true;
}
glSurfaceView.requestRender();
}
public static Dimension getRelativeSize(Dimension dimension, int width, int height) {
int toWidth = width, toHeight = height;
int imgWidth = (int) dimension.getWidth();
int imgHeight = (int) dimension.getHeight();
if (imgWidth > imgHeight) {
toWidth = (int) ((double) height / ((double) imgHeight / imgWidth));
if (toWidth > width)
toWidth = width;
toHeight = (int) (toWidth * ((double) imgHeight / imgWidth));
} else if (imgWidth < imgHeight) {
toHeight = (int) ((double) width / ((double) imgWidth / imgHeight));
if (toHeight > height)
toHeight = height;
toWidth = (int) (toHeight * ((double) imgWidth / imgHeight));
}
return new Dimension(toWidth, toHeight);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
textures = new int[1];
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
ViewGroup.LayoutParams layoutParams = glSurfaceView.getLayoutParams();
parentWidth = layoutParams.width;
parentHeight = layoutParams.height;
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
// gl.glOrthof(0f, width, 0f, height, -1f, 1f);
}
@Override
public void onDrawFrame(GL10 gl) {
if (bitmap != null) {
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, VERTEX_BUFFER);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, TEXCOORD_BUFFER);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
}
}
}
class Dimension {
int width = 0, height = 0;
public Dimension(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
关于java - TextureView.getBitmap() 函数是否有任何快速替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53988652/
我在 TextureView 中显示 CameraView 并使用 TextureView.getBitmap() 从 TextureView 获取位图>。 每次我调用 TextureView.get
我有一个附加了 mediaPlayer 的 TextureView,所以我可以使用它的表面来显示视频,到目前为止它运行良好。现在我尝试创建淡入淡出动画来显示视频,但我不知道该怎么做。 我尝试使用 se
我想使用 TextureView 在其中显示相机预览。最后我想使用 TextureView 为相机预览设置不透明度。但我有问题: 10-22 12:21:14.773: W/TextureView(5
是否有可能拥有一个具有透明度的 TextureView,以便其下方的 View (z 顺序)可见? TextureView 的 IOW 部分是透明的,TextureView 位于其上的 View 会透
我正在尝试实现 this page 上显示的示例.我已经尝试了三种运行 android 4 及更高版本的不同设备,在所有情况下我都会遇到黑屏并显示此警告: 01-27 20:01:22.683: W/
我尝试使用 SurfaceView 和 TextureView 播放相同的视频,并注意到使用 TextureView 渲染的图像更混叠(不如 SurfaceView 更“流畅”。 这是什么原因?有什么
我有一个具有固定宽度和高度的 TextureView,我想在其中显示一个相机预览。我需要裁剪相机预览,使其在我的 TextureView 中看起来不会被拉伸(stretch)。如何进行种植?如果我需要
我正在查看 Android 的 Camera2 API。在 https://github.com/googlesamples/android-Camera2Basic/blob/master/Appl
我正在查看 Android 的 Camera2 API。在 https://github.com/googlesamples/android-Camera2Basic/blob/master/Appl
我正在使用 android vlc (LibVLC) 和 TextureView 以便在我的 android 应用程序中播放实时 rtsp 流。一切工作正常,但是我需要为某些对象检测任务尽可能获取当前
我有一个 TextureView。我可以从我的 TextureView 中的评估文件夹播放视频。现在我想检查视频是否完成。我写了 setOnCompletionListener 但没有工作。这是我的来
我目前正在开发一个使用 Camera2 的应用程序。我在缩放和平移的 TextureView 上显示预览(我只需要显示图像的一部分)。我的问题是我需要分析整个图像。 我的 CameraDevice.S
我正在使用来自 Android Developers Samples 的相机代码在预览时获取图像的一部分并对其进行一些处理。 我想获取预览中从 [0,0] 到 [99,99] 的正方形图像,对其进行处
我正在制作一个自定义的安卓相机应用。到目前为止,我遇到的唯一问题是让 textureView 在横向模式下进行调整(将相机旋转 90 度)。 View 出来扭曲,而不是在正确的方向。肖像模式工作正常。
SurfaceTexture 预览被拉伸(stretch)了!我正在开发一个 camera2 应用程序。每个设备都工作不好。视频和照片的预览被拉伸(stretch)。 public class Cam
我有两个具有 TextureView 的 fragment 来显示相机预览或播放视频。 在使用应用程序一段时间后,玩弄屏幕,我在 logcat 中收到此错误 OpenGLRenderer﹕ GL_IN
我正在尝试将 TextureView 上的绘制过程复制到其他 TextureView 中。主要目标是能够创建一个相同视频的“墙”,该视频正在从附加到 TextureView 的 MediaPlayer
我正在尝试在 TextureView 上使用 ExoPlaye 播放视频。为了缩放和裁剪视频以适合 View ,我使用矩阵。我的自定义 View 扩展了“TextureView”这是我的代码: @Ov
我有一个 WpxHp 预览,我想拍摄 HxW 照片,因此我希望用户看到一个预览窗口,它与我要拍摄的照片具有相同的纵横比,而不是预览窗口。如何使用 TextureView 裁剪预览? -- 编辑 我的想
当我的 Android 应用程序启动时,我尝试播放启动 Activity 的视频,但我遇到了永远不会调用 onSurfaceTextureAvailable 接口(interface)方法的问题。 代
我是一名优秀的程序员,十分优秀!