Draw manually to MediaPlayer's surface - android

I have TextureView that I set to MetoaPlayer to play video:
TextureView textureView = new TextureView(context);
addView(textureView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
textureView.setSurfaceTextureListener(this);
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface)
{
return false;
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
this.surface = new Surface(surface);
mediaPlayer.setSurface(this.surface);
prepareAndPlay();
}
Video plays normally.
But when I try to draw manyally on surface, video playing not started!
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
this.surface = new Surface(surface);
mediaPlayer.setSurface(this.surface);
// DRAW FIRST FRAME ON SURFACE
Bitmap bitmap = BitmapFactory.decodeFile(firstFramePath);
Canvas canvas = surface.lockCanvas(null);
canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), null);
bitmap.recycle();
surface.unlockCanvasAndPost(canvas);
prepareAndPlay();
}
When I call play() on mediaplayer playing not started.
I suppose, that Surface move to invalid state and MediaPlayer cannot play video. But logcat is empty.
There is any way to draw on same surface both with media player.

You can't do this.
A "surface" is the producer side of a producer-consumer pair. You can't have two producers. (If you want the gory details, see this doc.)
With a TextureView you can change the underlying SurfaceTexture using setSurfaceTexture(), which should allow you to switch from one to the other. This feature is used (for different reasons) in Grafika's "double decode" activity.

Related

Rendering VLC Video output on Android OpenGL

I am trying for weeks to render a vlc streaming to OpenGL on Android.
I guess I am missing something. Here is what I have so far.
This is my custom class:
public class VLCVideoView extends SurfaceView implements SurfaceHolder.Callback, IVideoPlayer, GLSurfaceView.Renderer
My initialization on VLC:
try {
// Create a new media player
libvlc = LibVLC.getInstance();
libvlc.setHardwareAcceleration(LibVLC.HW_ACCELERATION_FULL);
libvlc.eventVideoPlayerActivityCreated(true);
libvlc.setSubtitlesEncoding("");
libvlc.setAout(LibVLC.AOUT_OPENSLES);
libvlc.setVout(LibVLC.VOUT_OPEGLES2);
//libvlc.setVout(LibVLC.VOUT_ANDROID_SURFACE);
libvlc.setTimeStretching(true);
libvlc.setChroma("RV32");
libvlc.setVerboseMode(true);
LibVLC.restart(context);
holder.setFormat(PixelFormat.RGBA_8888);
mSurface = holder.getSurface();
libvlc.attachSurface(mSurface, this);
Implemented the entire GLSurfaceView.Renderer methods:
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
#Override
public void onSurfaceChanged(GL10 gl, int width, int height)
#Override
public void onDrawFrame(GL10 gl)
As well, as:
#Override
protected void onDraw(Canvas canvas) for the VLCVideoView
{
if (mSurface!= null)
{
try {
Canvas surfaceCanvas = mSurface.lockCanvas(null);
if(surfaceCanvas != null)
{
super.onDraw(surfaceCanvas);
mSurface.unlockCanvasAndPost(surfaceCanvas);
}
} catch (Surface.OutOfResourcesException excp) {
excp.printStackTrace();
}
}
}
I have a rotating cube being draw, but instead of having the streaming frames as textures, it simple appears streaming on a flat surface.
Any clue?

Decode H264 frames to byte array

I have a stream of H264 video that I need to show in my Android app. If I consigure MediaCodec with a surface, then the video gets decoded to my app no problem, and I can see it in this surface.
But I also need to obtain a Bitmap of the video in certain moments (i.e. to store certain frames on the SD card). Is it possible to configure the MediaCodec library to return an array of bytes instead of working directly towards a surface??
Another option would be to obtain the bitmap directly from the surface, but I couldn't find this option on the SDK either.
You can use MediaMetadataRetriever:
MediaMetadataRetriever mediaMetadataRetriever=new MediaMetadataRetriever(); //should be stored as an instance variable
mediaMetadataRetriever.setDataSource(mVideo.getVideoUrl(Video.SD));
int time = videoView.getCurrentPosition()* 1000; //micro-to-milliseconds
Bitmap bmFrame = mediaMetadataRetriever.getFrameAtTime(time);
Edit:
To retrieve raw byte data without the involvement of UI, you can advance time frame by frame, get the Bitmap and convert it to byte array.
To be more efficient, consider forking FFmpegMediaMetadataRetriever, whose FFmpegMediaMetadataRetriever.java has method private native byte [] _getFrameAtTime(long timeUs, int option); skipping the whole Bitmap conversion process.
Try this:
public class GameView extends SurfaceView {
private Bitmap bmp;
private SurfaceHolder holder;
public GameView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas(null);
onDraw(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.icon);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bmp, 10, 10, null);
}
}

My Surface View is Not working

This is the main scenario: KillThemAll Game
In constructor of my CustomView which extends SurfaceView class, I set the background as :
this.setBackgroundDrawable(getResources().getDrawable(R.drawable.moon_light));
if I set background in one of the methods of SurfaceHolder.Callback(), the game and all the animations become freezed...
getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
setBackgroundDrawable(getResources().getDrawable(R.drawable.moon_light));
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
why?
Looks to me you just want to draw a background. Since you have overridden onDraw() in your SurfaceView implementation to do animation, you should not rely on View methods to do drawing because they will conflict with your custom drawing.
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(background, 0, 0, null); // background replaces canvas.drawColor(Color.BLACK);
// draw your sprites here
}
You just have to make sure background is a Bitmap that is bigger than the canvas size. You could also use drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) to scale it to the right size but it won't respect the aspect ratio.
Replace this line
this.setBackgroundDrawable(getResources().getDrawable(R.drawable.moon_light));
with
bm = BitmapFactory.decodeResource(getResources(), R.drawable.moon_light);
I think you're making a miskate on your src and dest rects.Try to print the values of both on the log and see whether they are correct

Antialiasing in TextureView

I tried to play the same video with a SurfaceView and a TextureView and noticed that the image rendered with the TextureView is more aliased (less 'smooth') than with the SurfaceView.
What is the reason for this ? Is there any way to configure rendering of TextureView to look better ?
The TextureView is used like this:
TextureView textureView = new TextureView(this);
textureView.setSurfaceTextureListener(new SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Log.i("test", "onSurfaceTextureAvailable()");
MediaPlayer player = MediaPlayer.create(TestActivity.this, Uri.parse(VIDEO_URL));
Surface surface = new Surface(surfaceTexture);
player.setSurface(surface);
player.start();
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Log.i("test", "onSurfaceTextureUpdated()");
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.i("test", "onSurfaceTextureSizeChanged()");
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.i("test", "onSurfaceTextureDestroyed()");
return false;
}
});
setContentView(textureView);
And for the SurfaceView:
SurfaceView surfaceView = new SurfaceView(this);
surfaceView.getHolder().addCallback(new Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("test", "surfaceCreated()");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("test", "surfaceDestroyed()");
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i("test", "surfaceChanged()");
MediaPlayer player = MediaPlayer.create(TestActivity.this, Uri.parse(VIDEO_URL));
player.setSurface(holder.getSurface());
player.start();
}
});
setContentView(surfaceView);
Well, it seems that applying a scaling (other than 1) on the TextureView does the 'smoothing' effect that I'm looking for.
textureView.setScaleX(1.00001f);
That sounds like a strange hack...but it works. Would be interesting to dig out what's done in the scaling that changes the rendering aspect...

How to draw an overlay on a SurfaceView used by Camera on Android?

I have a simple program that draws the preview of the Camera into a SurfaceView. What I'm trying to do is using the onPreviewFrame method, which is invoked each time a new frame is drawn into the SurfaceView, in order to execute the invalidate method which is supposed to invoke the onDraw method. In fact, the onDraw method is being invoked, but nothing there is being printed (I guess the camera preview is overwriting the text I'm trying to draw).
This is a simplify version of the SurfaceView subclass I have:
public class Superficie extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder mHolder;
public Camera camera;
Superficie(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(final SurfaceHolder holder) {
camera = Camera.open();
try {
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(new PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera arg1) {
invalidar();
}
});
} catch (IOException e) {}
}
public void invalidar(){
invalidate();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(w, h);
camera.setParameters(parameters);
camera.startPreview();
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// nothing gets drawn :(
Paint p = new Paint(Color.RED);
canvas.drawText("PREVIEW", canvas.getWidth() / 2,
canvas.getHeight() / 2, p);
}
}
SurfaceView probably does not work like a regular View in this regard.
Instead, do the following:
Put your SurfaceView inside of a
FrameLayout or RelativeLayout in
your layout XML file, since both of
those allow stacking of widgets on
the Z-axis
Move your drawing logic
into a separate custom View class
Add an instance of the custom View
class to the layout XML file as a
child of the FrameLayout or
RelativeLayout, but have it appear
after the SurfaceView
This will cause your custom View class to appear to float above the SurfaceView.
Try calling setWillNotDraw(false) from surfaceCreated:
public void surfaceCreated(SurfaceHolder holder) {
try {
setWillNotDraw(false);
mycam.setPreviewDisplay(holder);
mycam.startPreview();
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG,"Surface not created");
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(area, rectanglePaint);
Log.w(this.getClass().getName(), "On Draw Called");
}
and calling invalidate from onTouchEvent:
public boolean onTouch(View v, MotionEvent event) {
invalidate();
return true;
}
I think you should call the super.draw() method first before you do anything in surfaceView's draw method.

Categories

Resources