很多开发者希望我们的Android平台RTSP/RTMP播放端实现视频窗口的放大缩小功能,为此,我们做了个简单的demo,通过播放端回调RGB数据,直接在上层view操作处理即可,Github:https://github.com/daniulive/SmarterStreaming
无视频无真相:http://www.iqiyi.com/w_19s9sa7epp.html
基本流程如下:
基础的初始化和参数设定
libP = new SmartP JniV2(); myContext = this.getApplicationContext(); sSurfaceView = (SurfaceView) this.findViewById(R.id.surface); surface_renderer = new RGBSurfaceRenderer(sSurfaceView);
private void InitAndSetConfig() {
p Handle = libP .SmartP Open(myContext); if (p Handle == 0) { Log.e(TAG, "surfaceHandle with nil.."); return; } libP .SetSmartP EventCallbackV2(p Handle, new EventHandeV2()); libP .SmartP SetBuffer(p Handle, playBuffer); // set report download speed(默认2秒一次回调 用户可自行调整report间隔) libP .SmartP SetReportDownloadSpeed(p Handle, 1, 2); libP .SmartP SetFastStartup(p Handle, isFastStartup ? 1 : 0); //设置RTSP超时时间 int rtsp_timeout = 10; libP .SmartP SetRTSPTimeout(p Handle, rtsp_timeout); //设置RTSP TCP/UDP模式自动切换 int is_auto_switch_tcp_udp = 1; libP .SmartP SetRTSPAutoSwitchTcpUdp(p Handle, is_auto_switch_tcp_udp); libP .SmartP SaveImageFlag(p Handle, 1); // It only used when playback RTSP stream.. // libP .SmartP SetRTSPTcpMode(p Handle, 1); playbackUrl = "rtmp://202.69.69.180:443/webcast/bshdlive-pc"; //playbackUrl = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"; libP .SmartP SetUrl(p Handle, playbackUrl);}2. 设置External Render,回调RGBA数据
libP .SmartP SetExternalRender(p Handle, new RGBAExternalRender());
Log.i(TAG, "Start playback stream++");
InitAndSetConfig(); // External Render libP .SmartP SetExternalRender(p Handle, new RGBAExternalRender()); libP .SmartP SetAudioOutputType(p Handle, 0); if (isMute) { libP .SmartP SetMute(p Handle, isMute ? 1 : 0); } if (isHardwareDecoder) { int isSupportHevcHwDecoder = libP .SetSmartP VideoHevcHWDecoder(p Handle, 1); int isSupportH264HwDecoder = libP .SetSmartP VideoHWDecoder(p Handle, 1); Log.i(TAG, "isSupportH264HwDecoder: " + isSupportH264HwDecoder + ", isSupportHevcHwDecoder: " + isSupportHevcHwDecoder); } libP .SmartP SetLowLatencyMode(p Handle, isLowLatency ? 1 : 0); libP .SmartP SetFlipVertical(p Handle, is_flip_vertical ? 1 : 0); libP .SmartP SetFlipHorizontal(p Handle, is_flip_horizontal ? 1 : 0); libP .SmartP SetRotation(p Handle, rotate_degrees); int iPlaybackRet = libP .SmartP StartPlay(p Handle); if (iPlaybackRet != 0) { Log.e(TAG, "Call SmartP StartPlay failed.."); return; } surface_renderer.StartRender(); btnStartStopPlayback.setText("停止播放 "); isPlaying = true; Log.i(TAG, "Start playback stream--");- 回调RGBA数据:
class RGBAExternalRender implements NTExternalRender {
// public static final int NT_ _FORMAT_RGBA = 1; // public static final int NT_ _FORMAT_ABGR = 2; // public static final int NT_ _FORMAT_I420 = 3; private int width_ = 0; private int height_ = 0; private int row_bytes_ = 0; private ByteBuffer rgba_buffer_ = null; @Override public int getNT Format() { Log.i(TAG, "RGBAExternalRender::getNT Format return " + NT_ _FORMAT_RGBA); return NT_ _FORMAT_RGBA; } @Override public void onNT SizeChanged(int width, int height) { width_ = width; height_ = height; row_bytes_ = width_ * 4; Log.i(TAG, "RGBAExternalRender::onNT SizeChanged 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 onNTRender (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:onNTRender w=" + width + " h=" // + height + " timestamp=" + timestamp); // Log.i(TAG, "RGBAExternalRender:onNTRender rgba:" + // bytesToHexString(test_buffer)); if ( surface_renderer != null) { surface_renderer.SetRGBImage(width, height, rgba_buffer_); } }}对视频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; }});}
- 关闭播放:
if (isPlaying) {
Log.i(TAG, "Stop playback stream++"); int iRet = libP .SmartP StopPlay(p Handle); if (iRet != 0) { Log.e(TAG, "Call SmartP StopPlay failed.."); return; } surface_renderer.StopRender(); libP .SmartP Close(p Handle); p Handle = 0; isPlaying = false; btnStartStopPlayback.setText("开始播放 "); Log.i(TAG, "Stop playback stream--"); } 继续阅读与本文标签相同的文章
-
【实践】工业用变频器上云 【1】 硬件介绍 与阿里云IOT设备创建
2026-05-20栏目: 教程
-
新技术驱动,新旧动能转换有新篇
2026-05-20栏目: 教程
-
开发部署提速8倍!这款IDE插件了解一下?
2026-05-20栏目: 教程
-
阿里云建站主机3折限时优惠(低至3折+附价格表查询)
2026-05-20栏目: 教程
-
Docker入门-构建第一个Java程序
2026-05-20栏目: 教程
