jjzjj

android - 旋转 android VideoView

coder 2023-11-18 原文

我的应用程序只提供纵向模式。在纵向 Activity 中,我有一个全屏 VideoView。我想要做的是在横向模式下将 VideoView(实际视频、视频缓冲区)旋转 90 度。使 Activity 处于横向模式不是一种选择。 扩展 VideoView 和 Canvas 旋转将不起作用,因为它是 SurfaceView 而不是实际 View 。 有什么方法可以通过 videoView 实现吗?

最佳答案

VideoView 不支持视频的旋转,即使合成矩阵设置正确并且使用了旋转属性也是如此。

您可以做的是使用 TextureView 并设置其属性 rotation="90"(例如)。然后它会旋转框架,但宽高比是你需要自己处理的。为此,您可以使用 textureView.setScaleX((screenHeight * 1.0f)/screenWidth)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextureView
        android:id="@+id/playback_video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:rotation="90" />
</RelativeLayout>

它也应该处理流式视频。但请将其视为一个示例,而不是发布现成的代码。我重命名了一些东西并删除了其他东西,它们与问题无关,这可能会破坏某些东西,但总的来说这是可行的例子。

public class PlaybackActivity extends Activity implements MediaPlayer.OnErrorListener, OnPreparedListener,
        OnCompletionListener, OnVideoSizeChangedListener, OnBufferingUpdateListener, OnInfoListener,
        SurfaceTextureListener
{

    private MediaPlayer mediaPlayer;
    private TextureView videoView;
    private boolean startedPlayback = false;
    private boolean playerReady = false;
    public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;

    private void createMediaPlayer() {
        mediaPlayer = new MediaPlayer();
    }

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

    public void onCompletion(MediaPlayer mp) {
        Log.w(TAG, "Video playback finished");
    }

    @Override
    public boolean onError(MediaPlayer player, int what, int extra) {
        if (what == MediaPlayer.MEDIA_ERROR_UNKNOWN) {
            /*
             * Restart play back in case we did not start anything yet. This may
             * be the case when we tried to tune in in very first secs of the
             * broadcast when there is no data yet.
             */
            if (liveBroadcast && mediaPlayer != null && !mediaPlayer.isPlaying() && !startedPlayback) {
                if (checkCount-- > 0) {
                    mediaPlayer.reset();
                    checkBroadcast();
                } else {
                    Log.w(TAG, "Broadcast finished");
                }
            } else {
                Log.w(TAG, "No media in stream");
            }
        } else if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
            Log.w(TAG, "Media service died unexpectedly");
        } else {
            Log.w(TAG, "Unknown media error");
        }
        return true;
    }

   @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra) {
        switch (what) {
        case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:
            Log.w(TAG, "Media is too complex to decode it fast enough.");
            startedPlayback = true;
            break;
        case MEDIA_INFO_NETWORK_BANDWIDTH:
            Log.w(TAG, "Bandwith in recent past.");
            break;
        case MediaPlayer.MEDIA_INFO_BUFFERING_START:
            Log.w(TAG, "Start of media bufferring.");
            startedPlayback = true;
            break;
        case MediaPlayer.MEDIA_INFO_BUFFERING_END:
            Log.w(TAG, "End of media bufferring.");
            startedPlayback = true;
            break;
        case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
            Log.w(TAG, "Media is not properly interleaved.");
            break;
        case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE:
            Log.w(TAG, "Stream is not seekable.");
            break;
        case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:
            Log.w(TAG, "New set of metadata is available.");
            break;
        case MediaPlayer.MEDIA_INFO_UNKNOWN:
        default:
            Log.w(TAG, "Unknown playback info (" + what + ":" + extra + ").");
            break;
        }
        return true;
    }

    private void startPlayback() {
        if (mediaPlayer != null) {
            onLoaded(mediaPlayer);
            mediaPlayer.start();
        }
    }

    private void pausePlayback() {
        if (mediaPlayer != null && mediaPlayer.isPlaying())
            mediaPlayer.pause();
    }

    private void resumePlayback() {
        if (mediaPlayer != null && mediaPlayer.isPlaying())
            mediaPlayer.start();
    }

    private void onLoaded(MediaPlayer mp) {
    }

    public void onPrepared(MediaPlayer mp) {
        playerReady = true;
        startPlayback();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        setContentView(R.layout.playback);
        videoView = (TextureView) findViewById(R.id.playback_video);
        videoView.setOnClickListener(videoViewClickHandler);
        videoView.setSurfaceTextureListener(this);
        createMediaPlayer();
    }

   @Override
    protected void onDestroy() {
        releaseMediaPlayer();
        if (surface != null) {
            surface.release();
            surface = null;
        }
        super.onDestroy();
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        this.surface = new Surface(surface);
        loadMedia(someurl);
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        if (this.surface != null) {
            releaseMediaPlayer();
            this.surface.release();
            this.surface = null;
        }
        return true;
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    }

    @Override
    public void onVideoSizeChanged(MediaPlayer mp, int w, int h) {
        if (w > 0 && h > 0 && !videoSizeSetupDone) {
            Log.w(TAG, "Video size changed: " + w + "x" + h);
            changeVideoSize(w, h);
        }
    }

    private boolean videoSizeSetupDone = false;

    private void changeVideoSize(int width, int height) {
        DisplayMetrics metrics = new DisplayMetrics();
        RelativeLayout.LayoutParams params;

        Utils.getScreenMetrics(this, metrics);
        VideoOrientation orientation = someVideoSource.getVideoOrientation();
        if (orientation == LANDSCAPE) {
            params = new RelativeLayout.LayoutParams(metrics.widthPixels, metrics.heightPixels);
        } else {
            float rotation = orientation == BroadcastVideoOrientation.BroadcastVideoFrontCamera ? -90.0f : 90.0f;
            params = new RelativeLayout.LayoutParams(metrics.heightPixels, metrics.widthPixels);
            float scale = (width * 1.0f) / (height * 1.0f);
            videoView.setRotation(rotation);
            videoView.setScaleX(scale);
        }
        params.addRule(RelativeLayout.CENTER_IN_PARENT, -1);
        videoView.setLayoutParams(params);
        videoSizeSetupDone = true;
    }

    private void loadMedia(String url) {
        if (surface == null)
            return;
        Log.d(App.TAG, "Loading url: " + url);

        startedPlayback = false;
        try {
            mediaPlayer.reset();
            mediaPlayer.setSurface(surface);
            mediaPlayer.setDataSource(url);
            mediaPlayer.setOnPreparedListener(this);
            mediaPlayer.setOnCompletionListener(this);
            mediaPlayer.setOnErrorListener(this);
            mediaPlayer.setOnVideoSizeChangedListener(this);
            mediaPlayer.setScreenOnWhilePlaying(true);
            mediaPlayer.setOnBufferingUpdateListener(this);
            mediaPlayer.setOnInfoListener(this);
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mediaPlayer.prepareAsync();
        } catch (Exception e) {
            Log.w(TAG, "Media load failed");
            Utils.alert(this, "Playback Error", e.getMessage(), finishHandler);
        }
    }
}

希望这对您有所帮助。我一直在寻找这个解决方案很长一段时间。几乎尝试了所有方法,这似乎是唯一的方法。

关于android - 旋转 android VideoView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8586526/

有关android - 旋转 android VideoView的更多相关文章

  1. 旋转矩阵的几何意义 - 2

    点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度;     在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。

  2. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  3. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  4. 欧拉角、旋转矩阵及四元数 - 2

    欧拉角、旋转矩阵及四元数1.简介2.欧拉角2.1欧拉角定义2.2右手系和左手系2.3转换流程3.旋转矩阵4.四元数4.1四元数与欧拉角和旋转矩阵之间等效变换4.2测试Matlab代码5.总结1.简介常用姿态参数表达方式包括方向余弦矩阵、欧拉轴/角参数、欧拉角、四元数以及罗德里格参数等。高分辨率光学遥感卫星主要采用欧拉角与四元数对姿态参数进行描述。这里着重讲解欧拉角、旋转矩阵和四元数。2.欧拉角2.1欧拉角定义欧拉角是表征刚体旋转的一种方法之一,由莱昂哈德·欧拉引入的三个角度,用于描述刚体相对于固定坐标系的方向。在摄影测量、空间科学或其它技术领域,一般用一组(三个)欧拉角描述两个空间坐标之间的旋

  5. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  6. 华为OD机试 -旋转骰子(Python) | 机试题算法思路 【2023】 - 2

    最近更新的博客华为OD机试-卡片组成的最大数字(Python)|机试题算法思路华为OD机试-网上商城优惠活动(一)(Python)|机试题算法思路华为OD机试-统计匹配的二元组个数(Python)|机试题算法思路华为OD机试-找到它(Python)|机试题算法思路华为OD机试-九宫格按键输入(Python)|机试算法备考思路华为OD机试-身高排序(Python)|备考思路使用说明参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。华为OD清单查看地址:blog.csdn.net/hihell/catego

  7. Android 10.0 设置默认launcher后安装另外launcher后默认Launcher失效的功能修复 - 2

    1.前言 在10.0的系统rom定制化开发中,在系统中有多个launcher的时候,会在开机进入launcher的时候弹窗launcher列表,让用户选择进入哪个launcher,这样显得特别的不方便所以产品开发中,要求用RoleManager的相关api来设置默认Launcher,但是在设置完默认Launcher以后,在安装一款Launcher的时候,默认Launcher就会失效,在系统设置的默认应用中Launcher选项就为空,点击home键的时候会弹出默认Launcher列表,让选择进入哪个默认Launcher.所以需要从安装Launcher的流程来分析相关的设置。来解决问题设置默认La

  8. AiBote 2022 新研发的自动化框架,支持 Android 和 Windows 系统。速度非常快 - 2

    Ai-Bot基于流行的Node.js和JavaScript语言的一款新自动化框架,支持Windows和Android自动化。1、Windowsxpath元素定位算法支持支持Windows应用、.NET、WPF、Qt、Java和Electron客户端程序和ie、edgechrome浏览器2、Android支持原生APP和H5界面,元素定位速度是appium十倍,无线远程自动化操作多台安卓设备3、基于opencv图色算法,支持找图和多点找色,1080*2340全分辨率找图50MS以内4、内置免费OCR人工智能技术,无限制获取图片文字和找字功能。5、框架协议开源,除官方node.jsSDK外,用户可

  9. Android Gradle 7.1+新版本依赖变化 - 2

    前一段时间由于工作需要把可爱的小雪狐舍弃了,找到了小蜜蜂。但是新版本的小蜜蜂出现了很多和旧版本不一样的位置。1.功能位置迁移,原来在工程build.gradle的buildscript和allprojects移动至setting.gradle并改名为pluginManagement和dependencyResolutionManagement。里面的东西依旧可以按照原来的copy过来。pluginManagement{repositories{gradlePluginPortal()google()mavenCentral()}}dependencyResolutionManagement{r

  10. ruby - Ruboto 的最佳教程(适用于 Android 的 ruby​​)? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我几乎用完了Ruby,但现在想试试Ruboto,android上的ruby​​。谷歌未能给我足够的(几乎没有结果)。所以任何人都可以分享一些关于Ruboto的教程。

随机推荐