Sujith S Manjavana
Sujith S Manjavana

Reputation: 1586

How to attach MediaPlayer with SurfaceView in android?

I'm building video player with android media player object. i'm able to hear the audio but the video does not appear on surfaceView. here is my code

public class PlayerActivity extends Activity implements SurfaceHolder.Callback {
    String path;
    private MediaPlayer mp;
    private SurfaceView mPreview;
    private SurfaceHolder holder;
    boolean pausing = false;
    public static String filepath;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        getWindow().setFormat(PixelFormat.UNKNOWN);
        mPreview = (SurfaceView)findViewById(R.id.surfaceView);
        holder = mPreview.getHolder();
        holder.setFixedSize(176, 144);
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mp = new MediaPlayer();

        mp.setDisplay(holder);
        try {
            Intent intent = getIntent();
            Uri fileuri = intent.getData();
            filepath=fileuri.getPath();
        } catch(Exception e) {}

        try {
            mp.setDataSource(filepath);
            mp.prepare();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mp.start();
    }
}

target is android 2.3 and above. how to fix it please help me..

Upvotes: 13

Views: 36942

Answers (3)

Dilanka Laksiri
Dilanka Laksiri

Reputation: 268

Kotlin user - You can use MediaPlayer with SurfaceView like following example. Just create VideoLayout class with following codes.

class VideoLayout(
    context: Context, attrs: AttributeSet?
) : FrameLayout(context, attrs), SurfaceTextureListener {

    private var isLoop: Boolean
    private var isSound: Boolean
    private var videoAlign: Int
    private var videoScale: Int
    private var mVideoWidth = 0f
    private var mVideoHeight = 0f
    private var fileName: String? = null
    private var fileFormat: FileFormat = FILE
    private var videoSurface: TextureView? = null
    private var mediaPlayer: MediaPlayer? = null

    enum class Scale {
        ORIGINAL, CROP, STRETCH, FIT_SCREEN
    }

    enum class FileFormat {
        FILE, URI, URL, OTHER
    }

    enum class Align {
        TOP, TOP_LEFT, TOP_RIGHT, CENTER, CENTER_LEFT,
        CENTER_RIGHT, BOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT
    }

    init {
        val styledAttributes = context.theme.obtainStyledAttributes(
            attrs, R.styleable.VideoLayout, 0, 0
        )

        fileName = styledAttributes.getString(R.styleable.VideoLayout_path_or_url)
        videoScale = styledAttributes.getInteger(R.styleable.VideoLayout_scaleType, 3)
        videoAlign = styledAttributes.getInteger(R.styleable.VideoLayout_align, 4)
        isLoop = styledAttributes.getBoolean(R.styleable.VideoLayout_loop, true)
        isSound = styledAttributes.getBoolean(R.styleable.VideoLayout_sound, false)

        styledAttributes.recycle()

        fileName?.let {
            setupView()
            it.setFileFormat()

            if (videoScale != 2) {
                calculateVideoSize()
                surfaceSetup()
            }
        }
    }

    private fun setupView() {
        videoSurface = TextureView(context)
        addView(videoSurface)
        videoSurface?.surfaceTextureListener = this
    }

    private fun String.setFileFormat() {
        fileFormat = if (contains("http://") || contains("https://")) {
            URL
        } else if (contains("content://")) {
            URI
        } else if(contains("file://")) {
            FILE
        } else {
            OTHER
        }
    }

    private fun calculateVideoSize() {
        try {
            val metaRetriever = MediaMetadataRetriever()
            fileName?.let {

                if (fileFormat != OTHER) {
                    when(fileFormat) {
                        FILE -> {
                            val fName = it.substringAfterLast('/')
                            val file = File(context.getExternalFilesDir(
                                Environment.DIRECTORY_DOWNLOADS), fName
                            )
                            Timber.d("$fName file exists: ${file.exists()}")

                            val fd = FileInputStream(file).fd
                            metaRetriever.setDataSource(fd)
                        }
                        URI -> {
                            val uri = it.toUri()
                            metaRetriever.setDataSource(context, uri)
                        }
                        else -> metaRetriever.setDataSource(it, HashMap())
                    }
                } else {
                    val afd = context.assets.openFd(it)
                    metaRetriever.setDataSource(
                        afd.fileDescriptor, afd.startOffset, afd.length
                    )
                }
            }

            metaRetriever.extractMetadata(METADATA_KEY_VIDEO_HEIGHT)?.let {
                mVideoHeight = it.toFloat()
            }
            metaRetriever.extractMetadata(METADATA_KEY_VIDEO_WIDTH)?.let {
                mVideoWidth = it.toFloat()
            }
            metaRetriever.release()
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: NumberFormatException) {
            e.printStackTrace()
        }
    }

    private fun updateTextureViewSize(viewWidth: Int, viewHeight: Int) {
        var scaleX = 1.0f
        var scaleY = 1.0f

        val viewWidthFloat = viewWidth.toFloat()
        val viewHeightFloat = viewHeight.toFloat()

        val viewWidthFactor = viewWidthFloat / viewHeightFloat
        val videoWidthFactor = mVideoWidth / mVideoHeight

        when (videoScale) {
            0 -> { //original
                scaleX = mVideoWidth / viewWidthFloat
                scaleY = mVideoHeight / viewHeightFloat
            }
            1 -> { // crop
                if (viewWidthFactor > videoWidthFactor) {
                    scaleY = ((viewWidthFloat / mVideoWidth) * mVideoHeight) / viewHeightFloat
                } else {
                    scaleX = ((viewHeightFloat / mVideoHeight) * mVideoWidth) / viewWidthFloat
                }
            }

            3 -> { // fit-screen
                if (viewWidthFactor > videoWidthFactor) {
                    scaleX = ((viewHeightFloat / mVideoHeight) * mVideoWidth) / viewWidthFloat
                } else {
                    scaleY = ((viewWidthFloat / mVideoWidth) * mVideoHeight) / viewHeightFloat
                }
            }
        }

        var pivotPointX = 0f
        var pivotPointY = 0f

        when (videoAlign) {
            0 -> {
                pivotPointX = viewWidthFloat / 2f
                pivotPointY = 0f
            }
            1 -> {
                pivotPointX = 0f
                pivotPointY = 0f
            }
            2 -> {
                pivotPointX = viewWidthFloat
                pivotPointY = 0f
            }
            3 -> {
                pivotPointX = viewWidthFloat / 2f
                pivotPointY = viewHeightFloat / 2f
            }
            4 -> {
                pivotPointX = 0f
                pivotPointY = viewHeightFloat / 2f
            }
            5 -> {
                pivotPointX = viewWidthFloat
                pivotPointY = viewHeightFloat / 2f
            }
            6 -> {
                pivotPointX = viewWidthFloat / 2f
                pivotPointY = viewHeightFloat
            }
            7 -> {
                pivotPointX = 0f
                pivotPointY = viewHeightFloat
            }
            8 -> {
                pivotPointX = viewWidthFloat
                pivotPointY = viewHeightFloat
            }
        }

        val matrix = Matrix().apply {
            setScale(scaleX, scaleY, pivotPointX, pivotPointY)
        }

        videoSurface?.setTransform(matrix)
        videoSurface?.layoutParams = LayoutParams(viewWidth, viewHeight)
    }

    private fun surfaceSetup() {
        val screenHeight = resources.displayMetrics.heightPixels
        val screenWidth = resources.displayMetrics.widthPixels
        updateTextureViewSize(screenWidth, screenHeight)
    }

    private fun surfaceAvailableWorkers(surfaceTexture: SurfaceTexture) {
        val surface = Surface(surfaceTexture)
        try {
            mediaPlayer = MediaPlayer()
            mediaPlayer?.let { player ->
                fileName?.let {
                    if (fileFormat != OTHER) {
                        when(fileFormat) {
                            FILE -> {
                                val fName = it.substringAfterLast('/')
                                val file = File(context.getExternalFilesDir(
                                    Environment.DIRECTORY_DOWNLOADS), fName
                                )
                                Timber.d("$fName file exists: ${file.exists()}")

                                val fd = FileInputStream(file).fd
                                player.setDataSource(fd)
                            }
                            URI -> {
                                val uri = it.toUri()
                                player.setDataSource(context, uri)
                            }
                            else -> player.setDataSource(fileName)
                        }
                    } else {
                        val afd = context.assets.openFd(it)
                        player.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
                    }

                    if (!isSound) player.setVolume(0f, 0f)
                    player.setSurface(surface)
                    player.isLooping = isLoop
                    player.prepareAsync()
                    player.setOnPreparedListener { obj: MediaPlayer -> obj.start() }
                }
            }
        } catch (ignored: IllegalArgumentException) {
        } catch (ignored: SecurityException) {
        } catch (ignored: IllegalStateException) {
        } catch (ignored: IOException) {
        }
    }

    private fun changeVideo() {
        try {
            onDestroyVideoLayout()
            mediaPlayer = MediaPlayer()
            mediaPlayer?.let { player ->
                fileName?.let {
                    if (fileFormat != OTHER) {
                        when(fileFormat) {
                            FILE -> {
                                val fName = it.substringAfterLast('/')
                                val file = File(context.getExternalFilesDir(
                                    Environment.DIRECTORY_DOWNLOADS), fName
                                )
                                Timber.d("$fName file exists: ${file.exists()}")

                                val fd = FileInputStream(file).fd
                                player.setDataSource(fd)
                            }
                            URI -> {
                                val uri = it.toUri()
                                player.setDataSource(context, uri)
                            }
                            else -> player.setDataSource(fileName)
                        }
                    } else {
                        val afd = context.assets.openFd(it)
                        player.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
                    }

                    if (!isSound) player.setVolume(0f, 0f)
                    player.isLooping = isLoop
                    player.setSurface(Surface(videoSurface?.surfaceTexture))
                    player.prepareAsync()
                    player.setOnPreparedListener { obj: MediaPlayer -> obj.start() }
                }
            }
        } catch (ignored: IllegalArgumentException) {
        } catch (ignored: IOException) {
        } catch (ignored: IllegalStateException) {
        } catch (ignored: SecurityException) {
        }
    }

    override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
        surfaceAvailableWorkers(surface)
    }

    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {}
    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = false
    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}

    fun onDestroyVideoLayout() {
        mediaPlayer?.let {
            try {
                it.stop()
                it.release()
                mediaPlayer = null
            } catch (e: IllegalStateException) {
                e.printStackTrace()
            }
        }
    }

    fun onResumeVideoLayout() {
        mediaPlayer?.let {
            if (!it.isPlaying) {
                try {
                    it.start()
                } catch (e: IllegalStateException) {
                    e.printStackTrace()
                }
            }
        }
    }

    fun onPauseVideoLayout() {
        mediaPlayer?.let {
            if (it.isPlaying) {
                try {
                    it.pause()
                } catch (e: IllegalStateException) {
                    e.printStackTrace()
                }
            }
        }
    }

    fun setPathOrUrl(fileName: String) {
        this.fileName = fileName
        fileName.setFileFormat()

        if (videoSurface == null) {
            setupView()
        }

        if (videoScale != 2) {
            calculateVideoSize()
            surfaceSetup()
        }

        if (videoSurface != null) {
            changeVideo()
        }
    }

    fun setIsLoop(isLoop: Boolean) {
        this.isLoop = isLoop
    }

    fun setScale(scale: Scale) {
        videoScale = scale.ordinal
    }

    fun setAlign(align: Align) {
        videoAlign = align.ordinal
    }
}

Add following to your layout.xml

<com.your.package.VideoLayout
    android:id="@+id/video_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:loop="true"
    app:sound="false"
    app:scaleType="crop"
    app:align="center"/>

Also this supports video scaleType like center_crop, fit_screen, stretch and original.

Upvotes: 0

goher
goher

Reputation: 9

It is not working for me, throwing error illegal state exception in surfaceCreated() method at line mp.setDisplay(holder);

Declare mp.setDisplay(holder) as given below :

@Override
public void surfaceCreated(SurfaceHolder holder) 
{  
    mediaPlayer.setDataSource(this,uri);
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.prepare(); 
    mp.setDisplay(holder);
    mp.start;
}

Upvotes: 0

Sujith S Manjavana
Sujith S Manjavana

Reputation: 1586

finally i fixed it myself. just called the mp.setDisplay(holder); inside the surfaceCreated() function. and the final code is

public class PlayerActivity extends Activity implements SurfaceHolder.Callback {
    String path;
    private MediaPlayer mp;
    private SurfaceView mPreview;
    private SurfaceHolder holder;
    boolean pausing = false;
    public static String filepath;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        getWindow().setFormat(PixelFormat.UNKNOWN);
        mPreview = (SurfaceView)findViewById(R.id.surfaceView);
        holder = mPreview.getHolder();
        holder.setFixedSize(800, 480);
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mp = new MediaPlayer();


        try{
            Intent intent = getIntent();

            Uri fileuri = intent.getData();
            filepath=fileuri.getPath();
        }catch(Exception e){}


    }
    protected void onPause(){
        super.onPause();
        mp.release();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        mp.setDisplay(holder);
        play();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub

    }
    void play(){
        try {
            mp.setDataSource(filepath);

            mp.prepare(); 

        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mp.start();
    }
}

Upvotes: 28

Related Questions