3. Use MediaPlayer+SurfaceView to play video files

Use MediaPlayer + SurfaceView to play video files

MediaPlayer is mainly used to play audio and does not provide an image output interface, so you need to use other components to display the images played by MediaPlayer. You can use SurfaceView to display it.

Surface and SurfaceHolder | Android Open Source Project | Android Open Source Project (google.cn)

Surface, SurfaceHolder and SurfaceView

Surface objects enable applications to render images to be displayed on the screen. Through the SurfaceHolder interface, applications can edit and control Surface.

Surface

Surface is an interface for producers and consumers to exchange buffers.

SurfaceHolder

SurfaceHolder is the system’s interface for sharing Surface ownership with apps. Some clients used with Surface require SurfaceHolder because the API for getting and setting Surface parameters is implemented through SurfaceHolder. A SurfaceView contains a SurfaceHolder.

Most components that interact with a View involve a SurfaceHolder.

SurfaceView

A SurfaceView is a component and can be manipulated like any other View, but the SurfaceView’s contents are transparent.

When the SurfaceView’s View component is about to become visible, the framework asks the SurfaceControl to request a new surface from SurfaceFlinger. To receive callbacks when a Surface is created or destroyed, use the SurfaceHolder interface. By default, a newly created Surface is placed behind the app interface Surface. You can override the default Z-order to put the new Surface in front.

When the activity starts, you get callbacks in the following order:

  1. onCreate()
  2. onResume()
  3. surfaceCreated()
  4. surfaceChanged()

If you click “Back” you get:

  1. onPause()
  2. surfaceDestroyed() (called before the Surface disappears)

This sentence echoes the previous The API for obtaining and setting Surface parameters is implemented through SurfaceHolder

Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().

Make Surface available through SurfaceHolder.Callback

See

Understanding of the three

What is surfaceview? Is it different from surface? What is the relationship between Surface Holder and them? – Zhihu (zhihu.com)

Canvas is a data structure built by the Java layer and is a canvas for View. ViewGroup will split its Canvas to sub-Views. View will draw the graphic data on the Canvas it obtains in the onDraw method.

Surface is a data structure built by the Native layer and is the canvas used by SurfaceFlinger. It is a data structure that is used to draw directly to the screen.

The Views generally used by developers are drawn on Canvas, and then the data information of the Canvas in the top-level View (usually DecorView) is converted to a Surface. SurfaceFlinger will synthesize the Surface of each application window and then draw it to the screen.

So why is there a SurfaceView?

This is because the measurement, layout, and drawing calculations of View are relatively large. After the calculation is completed, the data is converted from Canvas to Surface, and then the screen is drawn. This process is time-consuming. There will be no problem for regular UI drawing, but it is unacceptable for application scenarios such as Camera preview and video playback.

SurfaceView is designed to solve this problem. SurfaceView content is no longer drawn on the Canvas, but directly on a Surface it holds. Since many steps are eliminated, its drawing performance is greatly improved. The SurfaceView itself is only used to control the size and position of the Surface.

Understanding and using surfaceview and surfaceholder-CSDN Blog

The SurfaceHolder object can be obtained through SurfaceView’s getHolder() function, and Surface is within the SurfaceHolder object. Although Surface saves the pixel data of the current window, it does not directly interact with Surface during use. The Canvas object is obtained by SurfaceHolder’s Canvas lockCanvas() or Canvas lockCanvas(Rect dirty) function, and the content is drawn on the Canvas. to modify the data in Surface. If the Surface is not editable or has not been created yet, calling this function will return null.
The content of Surface in unlockCanvas() and lockCanvas() is not cached, so the content of Surface needs to be completely redrawn. In order to improve efficiency and only redraw the changed part, you can call the lockCanvas(Rect dirty) function to specify a dirty area, so Content outside this area will be cached.

After calling the lockCanvas function to obtain the Canvas, SurfaceView will obtain a synchronization lock of the Surface until the unlockCanvasAndPost(Canvas canvas) function is called to release the lock. The synchronization mechanism here ensures that it will not be changed (destroyed or modified) during the Surface drawing process.

When the drawing in Canvas is completed, call the function unlockCanvasAndPost(Canvas canvas) to notify the system that the Surface has been drawn, so that the system will display the drawn content.

The relationship between Surface, SurfaceView, SurfaceHolder and SurfaceHolder.Callback_surfaceholder callback2 callback-CSDN blog

By extension, it can be considered that Surface in Android is a place used to draw graphics or images.

You can see part or all of the content of Surface through SurfaceView.

SurfaceHolder is an interface that acts like a listener on Surface. Provides access to and control the Surface-related methods behind SurfaceView.

Coding

public class MediaPlayerVideoActivity extends AppCompatActivity implements View.OnClickListener, SurfaceHolder.Callback {<!-- -->
    /**
     * MediaPlayer + SurfaceView play video
     */
    private static final String TAG = "MediaPlayerVideoActivity";

    private Button mStartButton;
    private Button mPauseButton;
    private Button mStopButton;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private MediaPlayer mPlayer = null;
    private boolean isRelease = false; //Flag to determine whether MediaPlayer is released

    public static Intent newIntent(Context packageContext) {<!-- -->
        Intent intent = new Intent(packageContext, MediaPlayerVideoActivity.class);
        return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {<!-- -->
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_media_player_vedio);

        bindViews();
    }

    private void bindViews() {<!-- -->
        mStartButton = (Button) findViewById(R.id.btn_media_player_video_start);
        mPauseButton = (Button) findViewById(R.id.btn_media_player_video_pause);
        mStopButton = (Button) findViewById(R.id.btn_media_player_video_stop);

        mStartButton.setOnClickListener(this);
        mPauseButton.setOnClickListener(this);
        mStopButton.setOnClickListener(this);

        mSurfaceView = (SurfaceView) findViewById(R.id.sfv_media_player_video);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setFixedSize(320, 220); //Displayed resolution, not set to video default
    }

    @Override
    public void onClick(View v) {<!-- -->
        switch (v.getId()){<!-- -->
            case R.id.btn_media_player_video_start:
                if (isRelease) {<!-- -->
                    loadVideo();
                    isRelease = false;
                }
                mPlayer.start();
                mStartButton.setEnabled(false);
                mPauseButton.setEnabled(true);
                mStopButton.setEnabled(true);
                break;
            case R.id.btn_media_player_video_pause:
                mPlayer.pause();
                mStartButton.setEnabled(true);
                mPauseButton.setEnabled(false);
                mStopButton.setEnabled(false);
                break;
            case R.id.btn_media_player_video_stop:
                mPlayer.reset();
                isRelease = true;
                // If you want to play again after stop(), you have to prepare() again
                mStartButton.setEnabled(true);
                mPauseButton.setEnabled(false);
                mStopButton.setEnabled(false);
                break;
        }
    }

    @SuppressLint("LongLogTag")
    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {<!-- -->
        mPlayer = new MediaPlayer();
        loadVideo();
    }

    private void loadVideo() {<!-- -->
        // Play files in the raw directory
// mPlayer = MediaPlayer.create(MediaPlayerVideoActivity.this, R.raw.big_buck_bunny);

        // Play local resources

        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        File file = new File("//sdcard/Movies/lesson.mp4");
        try {<!-- -->
            mPlayer.setDataSource(getApplicationContext(), Uri.parse(file.getAbsolutePath()));
        } catch (IOException e) {<!-- -->
            e.printStackTrace();
        }
        try {<!-- -->
            mPlayer.prepare();
        } catch (IOException e) {<!-- -->
            e.printStackTrace();
        }
        mPlayer.setDisplay(mSurfaceHolder); //Set the display video to be displayed on SurfaceView
    }


    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {<!-- -->

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {<!-- -->
        if (mPlayer.isPlaying()) {<!-- -->
            mPlayer.stop();
        }
        mPlayer.release();
    }
}