鸿蒙视频播放器可以播放暂停重播拖拽

本文详细介绍了如何在鸿蒙OS中开发一个视频播放器,包括视频的播放、暂停、重播和进度拖拽功能。通过创建SurfaceProvider、Slider和Button组件,结合Player接口实现视频操作。同时,文章提供了布局文件和关键代码片段,展示了从svg文件转换为xml资源的方法。
摘要由CSDN通过智能技术生成

鸿蒙系统HarmonyOS,今天来写一个视频播放器。

官方给的例子比较简单。

官方示例参考

鸿蒙OS 视频播放开发指导_w3cschool


本例可以实现视频播放、暂停、重播、画面显示、拖拽视频进度。

看最后界面。

 

图1本文不细讲,它是增加了弹框授权。

图2是开始状态,图三是播放状态。

图2现在没有视频预览,有空再做。

准备。

需要准备resources/rawfile/video_1.mp4, 
resources/base/graphic/icon_play.xml, 
resources/base/graphic/icon_pause.xml, 
resources/base/graphic/icon_redo.xml, 
4个文件。
其中3个xml文件是用svg文件转化成xml文件的。
先在iconfont-阿里巴巴矢量图标库网上下载几个播放、暂停图标文件,选择下载svg文件,
然后在DevEco Studio->module右键->New->Svg To xml,就能把svg文件转化成xml文件了。

下面给个resources/base/graphic/icon_play.xml的示例。

<?xml version="1.0" encoding="UTF-8"?>

<vector xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:width="200vp" ohos:height="200vp" ohos:viewportWidth="1024" ohos:viewportHeight="1024"> 
  <path ohos:fillColor="#FF000000" ohos:pathData="M157.54,860.55V163.45c0,-19.69 25.6,-33.48 43.32,-17.72l653.78,340.68c15.75,11.82 15.75,37.42 0,49.23L200.86,880.25c-17.72,13.78 -43.32,1.97 -43.32,-19.69z"></path>
</vector>

在DevEco Studio中新建一个Ability吧,取名SampleVideoPlayerAbility,
会得到
src/main/java/com/example/myapplication/slice/SampleVideoPlayerAbilitySlice.java, 
src/main/java/com/example/myapplication/video/SampleVideoPlayerAbility.java, 
resources/base/layout/ability_sample_video_player.xml, 
3个文件。
本例中,实现代码在SampleVideoPlayerAbilitySlice.java文件中
布局在文件ability_sample_video_player.xml中。

上布局文件ability_sample_video_player.xml。

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">

    <SurfaceProvider
        ohos:id="$+id:sp"
        ohos:height="200vp"
        ohos:width="match_parent"
        >
    </SurfaceProvider>

    <DependentLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:alignment="center"
        >

        <Slider
            ohos:id="$+id:slider"
            ohos:height="40vp"
            ohos:width="match_parent"
            ohos:max="6"
            ohos:min="0"
            ohos:progress="2"
            ohos:progress_element="#00FF00"
            ohos:progress_hint_text_alignment="end"
            ohos:progress_hint_text_color="#ff0000"
            ohos:progress_hint_text_size="20fp"
            >
        </Slider>

        <Text
            ohos:id="$+id:text_slider_max"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:align_parent_end="true"
            ohos:background_element="$graphic:background_ability_main_video"
            ohos:below="$id:slider"
            ohos:layout_alignment="horizontal_center"
            ohos:text="0"
            ohos:text_size="20vp"
            />
        <Text
            ohos:id="$+id:text_slider_current"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:background_element="$graphic:background_ability_main_video"
            ohos:below="$id:slider"
            ohos:layout_alignment="horizontal_center"
            ohos:text="0s"
            ohos:text_size="20vp"
            />
    </DependentLayout>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:alignment="center"
        ohos:orientation="horizontal">

        <Button
            ohos:id="$+id:button_play_or_pause"
            ohos:height="50vp"
            ohos:width="50vp"
            ohos:background_element="$graphic:icon_play"
            ></Button>

        <Button
            ohos:id="$+id:button_restart"
            ohos:height="50vp"
            ohos:width="50vp"
            ohos:background_element="$graphic:icon_redo"
            ></Button>
    </DirectionalLayout>

</DirectionalLayout>

上Slice.java文件。重点部分在注释里说明了。

package com.example.myapplication.slice;

import com.example.myapplication.ResourceTable;
import com.example.myapplication.utils.Common;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Slider;
import ohos.agp.components.Text;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.VectorElement;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.ToastDialog;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.global.resource.RawFileEntry;
import ohos.media.common.Source;
import ohos.media.player.Player;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;

public class SampleVideoPlayerAbilitySlice extends AbilitySlice {
    private Slider slider = null;
    private Button buttonPlay = null, buttonRestart = null;
    private Text textSliderMax = null;
    private Text textSliderCurrent = null;
    private SurfaceProvider sp = null;
    private MyEventHandler handler = null;
    private Player player = null;
    // 可选,在本例中实现视频进度拖拽
    private Slider.ValueChangedListener sliderValueChangedListener = new Slider.ValueChangedListener() {
        int position = 0;

        @Override
        public void onProgressUpdated(Slider slider, int i, boolean b) {
            Common.d("onProgressUpdated() " + i);
            position = i;
            //不能在此拖拽视频
        }

        @Override
        public void onTouchStart(Slider slider) {
            Common.d("onTouchStart() ");

        }

        @Override
        public void onTouchEnd(Slider slider) {
            if (player != null) {
                Common.d("onTouchEnd()getCurrentTime()=" + player.getCurrentTime());
            } else {
                Common.d("onTouchEnd() ");
            }
            //拖拽视频
            player.rewindTo(position * 1000);
        }
    };
    // 播放完成 、播放出错、拖拽完成回调函数
    private Player.IPlayerCallback playerCallback = new Player.IPlayerCallback() {
        @Override
        public void onPrepared() {
            Common.d("onPrepared()");

        }

        @Override
        public void onMessage(int i, int i1) {
            Common.d("onMessage()");

        }

        @Override
        public void onError(int i, int i1) {
            Common.d("onError()");
        }

        @Override
        public void onResolutionChanged(int i, int i1) {
            Common.d("onResolutionChanged()");

        }

        @Override
        public void onPlayBackComplete() {
            //播放完毕
            Common.d("onPlayBackComplete()");
            Common.d("onPlayBackComplete()getCurrentTime()=" + player.getCurrentTime());
            player.pause();
            InnerEvent event = InnerEvent.get(handler.MSG_UPDATE_BUTTON_PAUSED);
            handler.sendEvent(event);
        }

        @Override
        public void onRewindToComplete() {
            // 视频player.rewindTo()执行完成
            Common.d("onRewindToComplete()");
            Common.d("onRewindToComplete()getCurrentTime()=" + player.getCurrentTime());
        }

        @Override
        public void onBufferingChange(int i) {
            Common.d("onBufferingChange()");

        }

        @Override
        public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {
            Common.d("onNewTimedMetaData()");

        }

        @Override
        public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {
            if (player != null) {
                Common.d("onMediaTimeIncontinuity()getCurrentTime()=" + player.getCurrentTime());
                if (false) {
                    //onMediaTimeIncontinuity()并不会每秒更新,所以此函数不适合更新进度条。
                    InnerEvent event = InnerEvent.get(handler.MSG_UPDATE_PROGRESS);
                    handler.sendEvent(event);
                }
            } else {
                Common.d("onMediaTimeIncontinuity()");
            }
        }

    };
    // 可选,在本例中没有使用
    private SurfaceOps.Callback surfaceOpsCallBack = new SurfaceOps.Callback() {
        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {
            //Common.d("surfaceCreated()");
        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) {
            //Common.d("surfaceChanged()");
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
            //Common.d("surfaceDestroyed()");
        }
    };
    private Component.ClickedListener listener = new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            switch (component.getId()) {
                case ResourceTable.Id_button_play_or_pause:
                    //点一下播放,再点一下暂停
                    handlePlayOrPause();
                    break;
                case ResourceTable.Id_button_restart:
                    //从头播放、重新播放
                    if (player == null) {
                    } else if (player.isNowPlaying()) {
                        rewind();
                    } else {
                        handlePlayOrPause();
                    }
                    break;
            }
        }
    };

    @Override
    public void onStart(Intent intent) {
        Common.d("video", "onStart");
        super.onStart(intent);
        //设置布局
        Common.d("before setUIContent");
        super.setUIContent(ResourceTable.Layout_ability_sample_video_player);
        Common.d("after setUIContent");
        buttonPlay = (Button) findComponentById(ResourceTable.Id_button_play_or_pause);
        buttonRestart = (Button) findComponentById(ResourceTable.Id_button_restart);
        buttonPlay.setClickedListener(listener);
        buttonRestart.setClickedListener(listener);
        //进度条可以拖拽
        slider = (Slider) findComponentById(ResourceTable.Id_slider);
        slider.setValueChangedListener(sliderValueChangedListener);
        // handler的处理函数运行在主线程中,用来更新界面
        // 如果参数传入用EventRunner.create(),则handler的处理函数在子线程中运行,并不在主线程中运行。
        handler = new MyEventHandler(EventRunner.current());
        textSliderMax = (Text) findComponentById(ResourceTable.Id_text_slider_max);
        textSliderCurrent = (Text) findComponentById(ResourceTable.Id_text_slider_current);
        //视频内容显示在SurfaceProvider组件中
        sp = (SurfaceProvider) findComponentById(ResourceTable.Id_sp);
        sp.pinToZTop(true);
        sp.getSurfaceOps().get().addCallback(surfaceOpsCallBack);
        // 可忽略,弹框授权
        Common.grand(this, "ohos.permission.READ_MEDIA");
        Common.d("onStart() ---");

    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }


    private void rewind() {
        player.rewindTo(0);
    }

    private void handlePlayOrPause() {
        Common.d("handlePlayOrPause()");
        try {
            if (player == null) {
                //首次播放
                Common.d("handlePlayOrPause() try to play from start");
                play();
                Element element = new VectorElement(this, ResourceTable.Graphic_icon_pause);
                buttonPlay.setBackground(element);

                new ToastDialog(getContext())
                        .setText("playing")
                        // Toast显示在界面底部
                        .setAlignment(LayoutAlignment.BOTTOM)
                        .show();
            } else if (!player.isNowPlaying()) {
                //暂停了或者播放完成了,则继续播放或者从头播放
                Common.d("handlePlayOrPause() go on playing");
                player.play();
                Element element = new VectorElement(this, ResourceTable.Graphic_icon_pause);
                buttonPlay.setBackground(element);
                new ToastDialog(getContext())
                        .setText("playing")
                        .setAlignment(LayoutAlignment.BOTTOM)
                        .show();
            } else if (player.isNowPlaying()) {
                Common.d("handlePlayOrPause() try to pause");
                pause();
                Element element = new VectorElement(this, ResourceTable.Graphic_icon_play);
                buttonPlay.setBackground(element);
                new ToastDialog(getContext())
                        .setText("paused")
                        .setAlignment(LayoutAlignment.BOTTOM)
                        .show();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Common.d("e:" + e);
        }
    }

    public void preparePlayer() {
        // 步骤 1:实例化对象
        if (player == null) {
            player = new Player(this);
        }
        try {
            if (true) {
                //此文件放在rawfile/目录中
                RawFileEntry entry = getResourceManager().getRawFileEntry("resources/rawfile/vid_2.mp4");//这一行和官方例子有点不一样 请注意
                FileDescriptor fd = null;
                fd = entry.openRawFileDescriptor().getFileDescriptor();
                Common.d("fd=" + fd + " valid=" + fd.valid());
                player.setSource(entry.openRawFileDescriptor());
            } else {
                //此文件放在sdcard中,但我运行总说没有权限读取文件。
                FileInputStream fileInputStream = new FileInputStream(new File("/sdcard/xxx.mp4"));
                FileDescriptor fd = fileInputStream.getFD();
                Source source = new Source(fd);
                player.setSource(source);
            }
            player.prepare();
            //设置显示在sp中
            player.setVideoSurface(sp.getSurfaceOps().get().getSurface());
            //播放完成或者播放出错的回调函数
            player.setPlayerCallback(playerCallback);
            //ms为单位,视频时长
            int duration = player.getDuration();
            Common.d("duration=" + duration);
            slider.setMaxValue(duration);
            textSliderMax.setText((duration + 500) / 1000 + "s");
            textSliderCurrent.setText("0s");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void play() {
        Common.d("play()");
        preparePlayer();
        try {
            //因为Player的PlayerCallback中onMediaTimeIncontinuity()并不会每秒更新,
            //所以在onMediaTimeIncontinuity()中更新进度条的话,进度条可能长时间不动,一动就是好几秒过去了
            //Player的PlayerCallback中没有一个函数是每秒更新的,
            //所以PlayerCallback()中不适合更新进度条。
            //还是用线程每秒更新吧
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Common.d("run() duration=" + player.getDuration());
                    int duration = player.getDuration();
                    for (int i = 0; i < (duration + 500) / 1000; i++) {
                        if (player == null) break;
                        if (player.isNowPlaying()) {
                            InnerEvent event = InnerEvent.get(handler.MSG_UPDATE_PROGRESS);
                            handler.sendEvent(event);
                        }
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            //set progress --
            player.play();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void stop() {
        Common.d("stop()");
        if (player != null) {
            player.stop();
            player.release();
            player = null;
        }
    }

    public void pause() {
        if (player != null) {
            player.pause();
        }
    }

    public class MyEventHandler extends EventHandler {
        public final int MSG_UPDATE_PROGRESS = 1;
        public final int MSG_UPDATE_BUTTON_PLAYING = 2;
        public final int MSG_UPDATE_BUTTON_PAUSED = 3;

        public MyEventHandler(EventRunner runner) throws IllegalArgumentException {
            super(runner);
        }

        @Override
        protected void processEvent(InnerEvent event) {
            //运行在主线程中
            super.processEvent(event);
            int eventId = event.eventId;
            switch (eventId) {
                case MSG_UPDATE_PROGRESS:
                    int duration = player.getDuration();
                    int currentTime = player.getCurrentTime();
                    int delta = (duration - currentTime + 500) / 1000;
                    String t = delta + " s remaining";
                    Common.d("hint1:" + t + " progress=" + (currentTime + 500) / 1000);
                    slider.setProgressValue(currentTime);
                    slider.setProgressHintText(t);
                    textSliderCurrent.setText((currentTime + 500) / 1000 + "s");
                    Common.d("hint2:" + slider.getProgressHintText() + " progress=" + slider.getProgress());
                    break;
                case MSG_UPDATE_BUTTON_PLAYING:
                    Element element = new VectorElement(SampleVideoPlayerAbilitySlice.this, ResourceTable.Graphic_icon_pause);
                    buttonPlay.setBackground(element);
                    break;
                case MSG_UPDATE_BUTTON_PAUSED:
                    Element elementPaused = new VectorElement(SampleVideoPlayerAbilitySlice.this, ResourceTable.Graphic_icon_play);
                    buttonPlay.setBackground(elementPaused);
                    break;
            }
        }
    }
}

有任何问题欢迎指正!

如果觉得有用的话,就请我喝瓶水吧~~~


 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值