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

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

很多开发者希望Android播放端实现视频窗口的放大缩小功能,为此,我们做了个简单的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--");

3. 回调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_);
            }
        }
    }

4. 对视频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;
                }
            });
        }

5. 关闭播放:

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--");
            } 



相关文章
|
4月前
|
Android开发
Android Stadio Build 窗口字符串乱码问题
在使用Android Studio过程中,如果遇到Build窗口字符串乱码问题,可以通过编辑`studio.vmoptions`文件添加`-Dfile.encoding=UTF-8`配置并重启Android Studio来解决。
186 1
Android Stadio Build 窗口字符串乱码问题
|
2月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
74 4
Android开发表情emoji功能开发
|
2月前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
40 2
|
2月前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
56 3
|
4月前
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
437 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
3月前
|
API Android开发 数据安全/隐私保护
Android经典实战之窗口和WindowManager
本文介绍了Android开发中“窗口”的基本概念及其重要性。窗口是承载用户界面的基础单位,而`WindowManager`系统服务则负责窗口的创建、更新和移除等操作。了解这些概念有助于开发复杂且用户体验良好的应用。
72 2
|
3月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
4月前
|
编解码 开发工具 Android开发
Android平台RTSP|RTMP播放器如何实现TextureView渲染
本文介绍了在Android平台上使用TextureView进行RTSP和RTMP视频流渲染的技术背景和实现方法。TextureView相较于SurfaceView具备更高性能、更强功能性和更灵活的绘制方式等优势,但也有必须在硬件加速环境下运行和较高内存占用等局限。文中详细展示了如何在SmartPlayerV2工程中创建和配置TextureView,并通过代码示例解释了如何根据视频分辨率信息调整显示比例,以及处理TextureView的各种生命周期回调。此外,还列举了该播放器SDK支持的多项高级功能,如多实例播放、多种编码格式支持、硬解码能力等,旨在帮助开发者更好地理解和实现高性能的直播播放器。
|
4月前
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件
|
4月前
|
算法 数据处理 开发工具
Android平台RTSP|RTMP播放器如何回调YUV或RGB数据
在开发Android平台上的RTSP或RTMP播放器时,开发者不仅追求低延迟播放,还希望获取解码后的视频数据(如YUV或RGB格式),以便进行视觉算法分析。使用大牛直播SDK中的SmartPlayer,可在确保播放流畅的同时,通过设置外部渲染器(`SmartPlayerSetExternalRender`)来高效地回调原始视频数据。例如,对于RGBA数据,需实现`NTExternalRender`接口,并重写相关方法以处理数据和尺寸变化。同样地,对于I420(YUV)数据,也需要相应地实现接口以满足需求。这种方式使得开发者能在不影响常规播放功能的情况下,进行定制化的视频处理任务。