Android播放器实现视频窗口实时放大缩小功能

简介: 很多开发者希望大牛直播SDK的Android平台RTSP/RTMP播放端实现视频窗口的放大缩小功能,为此,我们做了个简单的demo,通过播放端回调RGB数据,直接在上层view操作处理即可,Github:https://github.

很多开发者希望我们的Android平台RTSP/RTMP播放端实现视频窗口的放大缩小功能,为此,我们做了个简单的demo,通过播放端回调RGB数据,直接在上层view操作处理即可,Github:https://github.com/daniulive/SmarterStreaming

无视频无真相:http://www.iqiyi.com/w_19s9sa7epp.html

基本流程如下:

  1. 基础的初始化和参数设定

        libPlayer = new SmartPlayerJniV2();
        myContext = this.getApplicationContext();
        sSurfaceView = (SurfaceView) this.findViewById(R.id.surface);
        surface_renderer = new RGBSurfaceRenderer(sSurfaceView);
    

private void InitAndSetConfig() {

    playerHandle = libPlayer.SmartPlayerOpen(myContext);

    if (playerHandle == 0) {
        Log.e(TAG, "surfaceHandle with nil..");
        return;
    }

    libPlayer.SetSmartPlayerEventCallbackV2(playerHandle,
            new EventHandeV2());

    libPlayer.SmartPlayerSetBuffer(playerHandle, playBuffer);

    // set report download speed(默认2秒一次回调 用户可自行调整report间隔)
    libPlayer.SmartPlayerSetReportDownloadSpeed(playerHandle, 1, 2);

    libPlayer.SmartPlayerSetFastStartup(playerHandle, isFastStartup ? 1 : 0);

    //设置RTSP超时时间
    int rtsp_timeout = 10;
    libPlayer.SmartPlayerSetRTSPTimeout(playerHandle, rtsp_timeout);

    //设置RTSP TCP/UDP模式自动切换
    int is_auto_switch_tcp_udp = 1;
    libPlayer.SmartPlayerSetRTSPAutoSwitchTcpUdp(playerHandle, is_auto_switch_tcp_udp);

    libPlayer.SmartPlayerSaveImageFlag(playerHandle, 1);

    // It only used when playback RTSP stream..
    // libPlayer.SmartPlayerSetRTSPTcpMode(playerHandle, 1);

    playbackUrl = "rtmp://202.69.69.180:443/webcast/bshdlive-pc";

    //playbackUrl = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov";

    libPlayer.SmartPlayerSetUrl(playerHandle, playbackUrl);
}

2.  设置External Render,回调RGBA数据

libPlayer.SmartPlayerSetExternalRender(playerHandle, new RGBAExternalRender());

Log.i(TAG, "Start playback stream++");

            InitAndSetConfig();

            // External Render
            libPlayer.SmartPlayerSetExternalRender(playerHandle, new RGBAExternalRender());

            libPlayer.SmartPlayerSetAudioOutputType(playerHandle, 0);

            if (isMute) {
                libPlayer.SmartPlayerSetMute(playerHandle, isMute ? 1
                        : 0);
            }

            if (isHardwareDecoder) {
                int isSupportHevcHwDecoder = libPlayer.SetSmartPlayerVideoHevcHWDecoder(playerHandle, 1);

                int isSupportH264HwDecoder = libPlayer
                        .SetSmartPlayerVideoHWDecoder(playerHandle, 1);

                Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder);
            }

            libPlayer.SmartPlayerSetLowLatencyMode(playerHandle, isLowLatency ? 1
                    : 0);

            libPlayer.SmartPlayerSetFlipVertical(playerHandle, is_flip_vertical ? 1 : 0);

            libPlayer.SmartPlayerSetFlipHorizontal(playerHandle, is_flip_horizontal ? 1 : 0);

            libPlayer.SmartPlayerSetRotation(playerHandle, rotate_degrees);

            int iPlaybackRet = libPlayer
                    .SmartPlayerStartPlay(playerHandle);

            if (iPlaybackRet != 0) {
                Log.e(TAG, "Call SmartPlayerStartPlay failed..");
                return;
            }

            surface_renderer.StartRender();

            btnStartStopPlayback.setText("停止播放 ");

            isPlaying = true;
            Log.i(TAG, "Start playback stream--");
  1. 回调RGBA数据:

class RGBAExternalRender implements NTExternalRender {

    // public static final int NT_FRAME_FORMAT_RGBA = 1;
    // public static final int NT_FRAME_FORMAT_ABGR = 2;
    // public static final int NT_FRAME_FORMAT_I420 = 3;

    private int width_ = 0;
    private int height_ = 0;
    private int row_bytes_ = 0;
    private ByteBuffer rgba_buffer_ = null;

    @Override
    public int getNTFrameFormat() {
        Log.i(TAG, "RGBAExternalRender::getNTFrameFormat return "
                + NT_FRAME_FORMAT_RGBA);
        return NT_FRAME_FORMAT_RGBA;
    }

    @Override
    public void onNTFrameSizeChanged(int width, int height) {
        width_ = width;
        height_ = height;

        row_bytes_ = width_ * 4;

        Log.i(TAG, "RGBAExternalRender::onNTFrameSizeChanged width_:"
                + width_ + " height_:" + height_);

        rgba_buffer_ = ByteBuffer.allocateDirect(row_bytes_ * height_);
    }

    @Override
    public ByteBuffer getNTPlaneByteBuffer(int index) {
        if (index == 0) {
            return rgba_buffer_;
        } else {
            Log.e(TAG,
                    "RGBAExternalRender::getNTPlaneByteBuffer index error:"
                            + index);
            return null;
        }
    }

    @Override
    public int getNTPlanePerRowBytes(int index) {
        if (index == 0) {
            return row_bytes_;
        } else {
            Log.e(TAG,
                    "RGBAExternalRender::getNTPlanePerRowBytes index error:"
                            + index);
            return 0;
        }
    }

    public void onNTRenderFrame(int width, int height, long timestamp) {
        if (rgba_buffer_ == null)
            return;

       // rgba_buffer_.rewind();

        // copy buffer

        // test
        // byte[] test_buffer = new byte[16];
        // rgba_buffer_.get(test_buffer);

        //Log.i(TAG, "RGBAExternalRender:onNTRenderFrame w=" + width + " h="
        //        + height + " timestamp=" + timestamp);

        // Log.i(TAG, "RGBAExternalRender:onNTRenderFrame rgba:" +
        // bytesToHexString(test_buffer));

        if ( surface_renderer != null)
        {
            surface_renderer.SetRGBImage(width, height, rgba_buffer_);
        }
    }
}
  1. 对视频view进行放大缩小等状态处理:

    @SuppressLint("ClickableViewAccessibility")
    public RGBSurfaceRenderer(SurfaceView view)
    {

    surface_holder_ = view.getHolder();
    if (surface_holder_ == null)
    {
        Log.e(TAG, "RGBSurfaceRenderer, surfaceHolder with null..");
        return;
    }
    surface_holder_.addCallback(this);
    
    view.setOnTouchListener(new View.OnTouchListener()
    {
        @Override
        public boolean onTouch(View v, MotionEvent event)
        {
            Log.e(TAG, "onTouch called..");
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    start_point_.set(event.getX(), event.getY());
                    status_ = DRAG;
                    break;
    
                case MotionEvent.ACTION_POINTER_DOWN:
                    float distance = spacing(event);
                    if (distance > 10f) {
    
                        status_ = ZOOM;
                        start_distance_ = distance;
                    }
                    break;
    
                case MotionEvent.ACTION_MOVE:
                    if (status_ == DRAG) {
                        dragAction(event);
                    } else {
    
                        if (event.getPointerCount() == 1)
                            return true;
                        zoomAcition(event);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_POINTER_UP:
                    status_ = NONE;
                    break;
                default:
                    break;
            }
    
            return true;
        }
    });
    

    }

  2. 关闭播放:

if (isPlaying) {

            Log.i(TAG, "Stop playback stream++");

            int iRet = libPlayer.SmartPlayerStopPlay(playerHandle);

            if (iRet != 0) {
                Log.e(TAG, "Call SmartPlayerStopPlay failed..");
                return;
            }

            surface_renderer.StopRender();

            libPlayer.SmartPlayerClose(playerHandle);
            playerHandle = 0;

            isPlaying = false;
            btnStartStopPlayback.setText("开始播放 ");

            Log.i(TAG, "Stop playback stream--");
        } 
目录
相关文章
|
8月前
|
编解码 安全 Android开发
如何修复 Android 和 Windows 不支持视频编解码器的问题?
视频播放时遇到“编解码器不支持”错误(如0xc00d36c4或0xc00d5212)是常见问题,即使文件格式为MP4或MKV。编解码器是编码和解码数据的工具,不同设备和版本支持不同的编解码器。解决方法包括:1) 安装所需编解码器,如K-Lite Codec Pack;2) 使用自带编解码器的第三方播放器,如VLC、KMPlayer等。这些方法能帮助你顺利播放视频。
|
5月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
Android开发
Android Stadio Build 窗口字符串乱码问题
在使用Android Studio过程中,如果遇到Build窗口字符串乱码问题,可以通过编辑`studio.vmoptions`文件添加`-Dfile.encoding=UTF-8`配置并重启Android Studio来解决。
555 2
Android Stadio Build 窗口字符串乱码问题
|
6月前
|
存储 编解码 开发工具
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
本文详细探讨了在Android平台上实现HTTP-FLV播放器的过程。首先介绍了FLV格式的基础,包括文件头和标签结构。接着分析了HTTP-FLV传输原理,通过分块传输实现流畅播放。然后重点讲解了播放器的实现步骤,涵盖网络请求、数据解析、音视频解码与渲染,以及播放控制功能的设计。文章还讨论了性能优化和网络异常处理的方法,并总结了HTTP-FLV播放器的技术价值,尤其是在特定场景下的应用意义。
245 11
|
11月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
242 4
Android开发表情emoji功能开发
|
11月前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
335 3
|
11月前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
110 2
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
1301 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
API Android开发 数据安全/隐私保护
Android经典实战之窗口和WindowManager
本文介绍了Android开发中“窗口”的基本概念及其重要性。窗口是承载用户界面的基础单位,而`WindowManager`系统服务则负责窗口的创建、更新和移除等操作。了解这些概念有助于开发复杂且用户体验良好的应用。
235 2
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件

热门文章

最新文章