赞
踩
自2015年我们发布Android平台RTSP、RTMP直播播放模块以来,渲染这块,支持SurfaceView或GlSurfaceView,当然如果开发者需要TextureView渲染,可以把RTSP、RTMP流数据解码回调YUV或RGB数据上来,上层自己渲染。本文主要介绍,如何实现RTSP、RTMP播放器TextureView渲染。在此之前,我们先看看TextureView优缺点:
先说优点:
再说缺点:
本文以大牛直播SDK的Android平台SmartPlayerV2工程demo为例:
开始播放之前,CreateView()实现如下:
- /*
- * SmartPlayer.java
- * Author: daniusdk.com
- * Create rendering with different type
- */
- private boolean CreateView() {
- if (sSurfaceView != null)
- return true;
-
- Log.i(TAG, "CreateView");
-
- if (SURFACE_TYPE_NULL == surface_type_) {
- String manufacturer = Build.MANUFACTURER;
- Log.i(TAG, "CreateView, current manufacturer: " + manufacturer);
-
- if (is_enable_hardware_render_mode) {
- //hardware render模式,第二个参数设置为false
- sSurfaceView = NTRenderer.CreateRenderer(this, false);
- } else {
- //这个字符串可以自己定义,例如判断华为就填写huawei,魅族就填写meizu
- if ("huawei".equalsIgnoreCase(manufacturer)) {
- sSurfaceView = NTRenderer.CreateRenderer(this, true);
- } else {
- /*
- * useOpenGLES2: If with true: Check if system supports openGLES, if
- * supported, it will choose openGLES. If with false: it will set
- * with default surfaceView;
- */
- sSurfaceView = NTRenderer.CreateRenderer(this, true);
- }
- }
- } else {
- if (SURFACE_TYPE_TEXTURE_VIEW == surface_type_) {
- TextureView texture_view = new TextureView(this);
- texture_view.setSurfaceTextureListener(this);
- sSurfaceView = texture_view;
- } else
- sSurfaceView= new SurfaceView(this);
- }
-
- if (sSurfaceView == null) {
- Log.i(TAG, "Create render failed..");
- return false;
- }
-
- if (is_enable_hardware_render_mode || SURFACE_TYPE_SURFACE_VIEW == surface_type_) {
- if (sSurfaceView instanceof SurfaceView) {
- SurfaceHolder surfaceHolder = ((SurfaceView)sSurfaceView).getHolder();
- if (surfaceHolder == null)
- Log.e(TAG, "CreateView, surfaceHolder with null..");
- else
- surfaceHolder.addCallback(this);
- }
- }
-
- return true;
- }

视频流开始播放后,我们会把视频宽高信息回调上来(EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO),然后,根据获取到的宽高信息,调用adjustTextureViewAspectRatio()按比例显示窗口,如果需要铺满显示,不调用比例显示即可。
- class EventHandeV2 implements NTSmartEventCallbackV2 {
- @Override
- public void onNTSmartEventCallbackV2(long handle, int id, long param1,
- long param2, String param3, String param4, Object param5) {
-
- //Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);
-
- String player_event = "";
-
- switch (id) {
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STARTED:
- player_event = "开始..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTING:
- player_event = "连接中..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTION_FAILED:
- player_event = "连接失败..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CONNECTED:
- player_event = "连接成功..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_DISCONNECTED:
- player_event = "连接断开..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STOP:
- player_event = "停止播放..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RESOLUTION_INFO:
- player_event = "分辨率信息: width: " + param1 + ", height: " + param2;
- handler.post(new OnNTPlayerVideoSize((int)param1, (int)param2));
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_NO_MEDIADATA_RECEIVED:
- player_event = "收不到媒体数据,可能是url错误..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_SWITCH_URL:
- player_event = "切换播放URL..";
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_CAPTURE_IMAGE:
- player_event = "快照: " + param1 + " 路径:" + param3;
-
- if (param1 == 0)
- player_event = player_event + ", 截取快照成功";
- else
- player_event = player_event + ", 截取快照失败";
-
- if (param4 != null && !param4.isEmpty())
- player_event += (", user data:" + param4);
-
- break;
-
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RECORDER_START_NEW_FILE:
- player_event = "[record]开始一个新的录像文件 : " + param3;
- break;
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_ONE_RECORDER_FILE_FINISHED:
- player_event = "[record]已生成一个录像文件 : " + param3;
- break;
-
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_START_BUFFERING:
- Log.i(TAG, "Start Buffering");
- break;
-
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_BUFFERING:
- Log.i(TAG, "Buffering:" + param1 + "%");
- break;
-
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_STOP_BUFFERING:
- Log.i(TAG, "Stop Buffering");
- break;
-
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_DOWNLOAD_SPEED:
- player_event = "download_speed:" + param1 + "Byte/s" + ", "
- + (param1 * 8 / 1000) + "kbps" + ", " + (param1 / 1024)
- + "KB/s";
- break;
-
- case NTSmartEventID.EVENT_DANIULIVE_ERC_PLAYER_RTSP_STATUS_CODE:
- Log.e(TAG, "RTSP error code received, please make sure username/password is correct, error code:" + param1);
- player_event = "RTSP error code:" + param1;
- break;
- ....
- }
-
- if (player_event.length() > 0) {
- Log.i(TAG, player_event);
- Message message = new Message();
- message.what = PLAYER_EVENT_MSG;
- message.obj = player_event;
- handler.sendMessage(message);
- }
- }
- }

OnNTPlayerVideoSize实现如下:
- class OnNTPlayerVideoSize implements Runnable{
- int width_;
- int height_;
-
- OnNTPlayerVideoSize(int w, int h ) {
- this.width_ = w;
- this.height_ = h;
- }
-
- public void run() {
- if (this.width_ < 1 || this.height_ < 1)
- return;
-
- video_width_ = this.width_;
- video_height_ = this.height_;
-
- if (null == sSurfaceView)
- return;
-
- if (SURFACE_TYPE_TEXTURE_VIEW == surface_type_ && sSurfaceView instanceof TextureView)
- adjustTextureViewAspectRatio((TextureView)sSurfaceView, this.width_, this.height_);
- else if (((isHardwareDecoder&&is_enable_hardware_render_mode) || SURFACE_TYPE_SURFACE_VIEW == surface_type_)
- && sSurfaceView instanceof SurfaceView )
- adjustSurfaceViewAspectRatio((SurfaceView)sSurfaceView, this.width_, this.height_);
- }
- }

针对TextureView处理如下:
- /**
- * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
- *
- * @param surface The surface returned by
- * {@link android.view.TextureView#getSurfaceTexture()}
- * @param width The width of the surface
- * @param height The height of the surface
- */
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- Log.i(TAG, "TextureView onSurfaceTextureAvailable w:" + width + ", h:" + height);
-
- if (texture_view_surface_ != null) {
- texture_view_surface_.release();
- texture_view_surface_ = null;
- }
-
- texture_view_surface_ = new Surface(surface);
-
- if (isPlaying && SURFACE_TYPE_TEXTURE_VIEW == surface_type_) {
- libPlayer.SetSurface(playerHandle, texture_view_surface_, 0, disable_codec_render_surface_, disable_sdk_render_surface_);
-
- if (video_width_ > 0 && video_height_ > 0 && sSurfaceView != null && (sSurfaceView instanceof TextureView))
- adjustTextureViewAspectRatio((TextureView)sSurfaceView, video_width_, video_height_);
- }
- }
-
- /**
- * Invoked when the {@link SurfaceTexture}'s buffers size changed.
- *
- * @param surface The surface returned by
- * {@link android.view.TextureView#getSurfaceTexture()}
- * @param width The new width of the surface
- * @param height The new height of the surface
- */
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- Log.i(TAG, "TextureView onSurfaceTextureSizeChanged w:" + width + ", h:" + height);
-
- if(isPlaying && SURFACE_TYPE_TEXTURE_VIEW == surface_type_) {
- if (sSurfaceView != null && (sSurfaceView instanceof TextureView) && video_width_ > 0 && video_height_ > 0)
- adjustTextureViewAspectRatio((TextureView) sSurfaceView, video_width_, video_height_);
- }
- }
-
- /**
- * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
- * If returns true, no rendering should happen inside the surface texture after this method
- * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
- * Most applications should return true.
- *
- * @param surface The surface about to be destroyed
- */
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- Log.i(TAG, "TextureView onSurfaceTextureDestroyed");
-
- if(isPlaying && SURFACE_TYPE_TEXTURE_VIEW == surface_type_)
- libPlayer.SetSurface(playerHandle, null, 0, 0, 0);
-
- if (texture_view_surface_ != null) {
- texture_view_surface_.release();
- texture_view_surface_ = null;
- }
-
- return true;
- }
-
- /**
- * Invoked when the specified {@link SurfaceTexture} is updated through
- * {@link SurfaceTexture#updateTexImage()}.
- *
- * @param surface The surface just updated
- */
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- // Log.i(TAG, "TextureView onSurfaceTextureUpdated");
- }

至此,RTSP|RTMP播放,我们是实现的功能如下(如不特别说明,代表Windows、Linux、Android、iOS平台均支持):
做播放器不难,做高稳定低延迟低资源占用的RTMP|RTSP直播播放器还是有点儿难度,以上是大牛直播SDK针对Android平台RTMP|RTSP播放器TextureView渲染相关的技术交流,感兴趣的开发者,也可以找我单独沟通。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。