1
votes

I'm trying to play a video from Android using a SurfaceView.

I can load the video, the sound is played correctly but I can't see the video.

Here is my activity :

public class VideoTest extends Activity implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback, OnErrorListener {

    private static final String TAG = "MediaPlayerDemo";
    private int mVideoWidth;
    private int mVideoHeight;
    private MediaPlayer mMediaPlayer;
    private SurfaceView mPreview;
    private SurfaceHolder holder;
    private String path;
    private Bundle extras;
    public static final String MEDIA = "media";
    private static final int LOCAL_AUDIO = 1;
    public static final int STREAM_AUDIO = 2;
    private static final int RESOURCES_AUDIO = 3;
    private static final int LOCAL_VIDEO = 4;
    public static final int STREAM_VIDEO = 5;
    private boolean mIsVideoSizeKnown = false;
    private boolean mIsVideoReadyToBePlayed = false;
    private RelativeLayout layout;

    /**
     *
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        mPreview = new SurfaceView(this);
        holder = mPreview.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        extras = getIntent().getExtras();

        mPreview.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        layout = new RelativeLayout(this);
        layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
        layout.addView(mPreview, 0);

        setContentView(layout);

    }

    private void playVideo(Integer Media) {
        doCleanUp();
        try {
            mMediaPlayer = MediaPlayer.create(this, R.raw.video);
            mMediaPlayer.setDisplay(holder);

            mMediaPlayer.setOnBufferingUpdateListener(this);
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnVideoSizeChangedListener(this);
            mMediaPlayer.setOnErrorListener(this);

            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage(), e);
        }
    }

    public void onBufferingUpdate(MediaPlayer arg0, int percent) {
        Log.d(TAG, "onBufferingUpdate percent:" + percent + " pos : " + mMediaPlayer.getCurrentPosition() + " / " + mMediaPlayer.getDuration());
        if (mMediaPlayer.isPlaying()) {
            Log.d(TAG, "Playing");
        } else {
            mMediaPlayer.start();
        }
    }

    public void onCompletion(MediaPlayer arg0) {
        Log.d(TAG, "onCompletion called");
    }

    public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
        Log.v(TAG, "onVideoSizeChanged called");
        if (width == 0 || height == 0) {
            Log.e(TAG, "invalid video width(" + width + ") or height(" + height
                    + ")");
            return;
        }
        mIsVideoSizeKnown = true;
        mVideoWidth = width;
        mVideoHeight = height;
        if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            startVideoPlayback();
        }
    }

    public void onPrepared(MediaPlayer mediaplayer) {
        Log.d(TAG, "onPrepared called");
        mIsVideoReadyToBePlayed = true;
        if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            startVideoPlayback();
        }
    }

    public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {
        Log.d(TAG, "surfaceChanged called. Width : " + j + ", height : " + k);
        holder = surfaceholder;
        mMediaPlayer.setDisplay(holder);
        mIsVideoSizeKnown = true;
        mVideoWidth = j;
        mVideoHeight = k;
        if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            startVideoPlayback();
        }
    }

    public void surfaceDestroyed(SurfaceHolder surfaceholder) {
        Log.d(TAG, "surfaceDestroyed called");
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated called");
        playVideo(extras.getInt(MEDIA));

    }

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaPlayer();
        doCleanUp();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaPlayer();
        doCleanUp();
    }

    private void releaseMediaPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    private void doCleanUp() {
        mVideoWidth = 0;
        mVideoHeight = 0;
        mIsVideoReadyToBePlayed = false;
        mIsVideoSizeKnown = false;
    }

    private void startVideoPlayback() {
        Log.v(TAG, "startVideoPlayback " + mVideoWidth + "x" + mVideoHeight);

        int width = mPreview.getWidth();
        int height = mPreview.getHeight();
        float boxWidth = width;
        float boxHeight = height;

        float videoWidth = mMediaPlayer.getVideoWidth();
        float videoHeight = mMediaPlayer.getVideoHeight();

        Log.i(TAG, String.format("startVideoPlayback @ video %dx%d - box %dx%d", (int) videoWidth, (int) videoHeight, width, height));

        float wr = boxWidth / videoWidth;
        float hr = boxHeight / videoHeight;
        float ar = videoWidth / videoHeight;

        if (wr > hr) {
            width = (int) (boxHeight * ar);
        } else {
            height = (int) (boxWidth / ar);
        }

        Log.i(TAG, String.format("Scaled to %dx%d", width, height));

        holder.setFixedSize(width, height);
        mMediaPlayer.start();
    }

    public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
        Log.e(TAG, "ERROR called : " + arg1 + ", " + arg2);
        return false;
    }
}

The onVideoSizeChanged function is never called but the onPrepared and surfaceChanged are called. Thus the startVideoPlayback function is called, but the video width and height are 0.

My video is playing, as I can hear the sound, but nothing is displayed on the screen. I also tried to give raw width and height to the setFixedSize function of the SurfaceHolder object but I still don't have anything displayed.

Can you help me? I'm using Android 8

EDIT

Here is the log I have when I'm playing a video from the resources :

WARN    info/warning (1, 35)
WARN    info/warning (1, 44)
DEBUG   Duration : 101248
DEBUG   surfaceChanged called. Width : 480, height : 270
INFO    Info (1,35)
INFO    Info (1,44)
DEBUG   onPrepared called
VERBOSE startVideoPlayback 480x270
INFO    startVideoPlayback @ video 0x0 - box 480x270
INFO    Scaled to 480x0
DEBUG   surfaceDestroyed called

EDIT 2

I tried with another video and it's working.

Here are the specifications of the "not working" video :

  • Container : MP4 - QuickTime
  • Rate : 2 340 Kbps
  • Format : H.264/MPEG-4-AVC
  • Size : 1280*640
  • Aspect pixel : undefined
  • Image proportion : 2.000
  • Encoding profile : [email protected]

Here are the specifications for the working video :

  • Container : MP4 - QuickTime
  • Rate : 537 Kbps
  • Format : H.264/MPEG-4-AVC
  • Size : 640*360
  • Aspect pixel : undefined
  • Image proportion : 16:9
  • Encoding profile : [email protected]

Do you know what's wrong with the first video ?

1
Does logcat show an error?stefana
@Nfear I edited my question to add the log. I only have some warnings but I don't know what they meanFabien Henon
Actually it's working with another video, I updated my question. Do you know why the first video is not working (with the same encoding)Fabien Henon
I've experienced the same problem. The reason why is because the video you are trying to play is beyond the dimensions of the screen. I found this out by fluke when I was doing some testing. In order to get around this, I had to use the MediaCodec API instead so the videos can play regardless of the resolution. My gut feeling is due to the size of the video. When you changed the resolution for the second video, it worked because it can comfortably fit on the screen.rayryeng

1 Answers

0
votes

Some things to try:

1) Should you be calling mMediaPlayer.prepareAsync() in your playVideo method?

2) Try removing your if statement in onPrepared

3) I would call holder.setSizeFromLayout(); and then mMediaPlayer.start() from your onPrepared callback (rather than onBufferingUpdate).