请选择 进入手机版 | 继续访问电脑版

[Android] Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别

[复制链接]
查看185 | 回复50 | 2021-9-14 07:10:48 | 显示全部楼层 |阅读模式
目次

SurfaceView, GLSurfaceView, SurfaceTexture以及TextureView是Android当中名字比较绕,关系又比较密切的几个类。本文基于Android 5.0(Lollipop)的代码理一下它们的基本原理,接洽 与区别。

SurfaceView

SurfaceView从Android 1.0(API level 1)时就有 。它继承自类View,因此它本质上是一个View。但与平凡 View不同的是,它有本身 的Surface。我们知道,一样平常 的Activity包含的多个View会构成 View hierachy的树形布局 ,只有最顶层的DecorView,也就是根结点视图,才是对WMS可见的。这个DecorView在WMS中有一个对应的WindowState。相应地,在SF中对应的Layer。而SurfaceView自带一个Surface,这个Surface在WMS中有本身 对应的WindowState,在SF中也会有本身 的Layer。如下图所示:

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别

也就是说,固然 在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它与宿主窗口是分离的。如许 的好处是对这个Surface的渲染可以放到单独线程去做,渲染时可以有本身 的GL context。这对于一些游戏、视频等性能相干 的应用非常有益,由于 它不会影响主线程对变乱 的相应 。但它也有缺点,由于 这个Surface不在View hierachy中,它的表现 也不受View的属性控制,以是 不能举行 平移,缩放等变换,也不能放在别的 ViewGroup中,一些View中的特性也无法使用 。

GLSurfaceView

GLSurfaceView从Android 1.5(API level 3)开始加入,作为SurfaceView的补充。它可以看作是SurfaceView的一种典型使用 模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程。别的 它定义了用户必要 实现的Render接口,提供了用Strategy pattern更改详细 Render举动 的机动 性。作为GLSurfaceView的Client,只必要 将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。如:

  1. public class TriangleActivity extends Activity {
  2. protected void onCreate(Bundle savedInstanceState) {
  3. mGLView = new GLSurfaceView(this);
  4. mGLView.setRenderer(new RendererImpl(this));
复制代码

相干 类图如下。此中 SurfaceView中的SurfaceHolder重要 是提供了一坨操作Surface的接口。GLSurfaceView中的EglHelper和GLThread分别实现了上面提到的管理EGL环境和渲染线程的工作。GLSurfaceView的使用 者必要 实现Renderer接口。

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别
 

SurfaceTexture

SurfaceTexture从Android 3.0(API level 11)加入。和SurfaceView不同的是,它对图像流的处理并不直接表现 ,而是转为GL外部纹理,因此可用于图像流数据的二次处理(如Camera滤镜,桌面殊效 等)。比如Camera的预览数据,变成纹理后可以交给GLSurfaceView直接表现 ,也可以通过SurfaceTexture交给TextureView作为View heirachy中的一个硬件加速层来表现 。起首 ,SurfaceTexture从图像流(来自Camera预览,视频解码,GL绘制场景等)中获得帧数据,当调用updateTexImage()时,根据内容流中近来 的图像更新SurfaceTexture对应的GL纹理对象,接下来,就可以像操作平凡 GL纹理一样操作它了。从下面的类图中可以看出,它核心管理着一个BufferQueue的Consumer和Producer两头 。Producer端用于内容流的源输出数据,Consumer端用于拿GraphicBuffer并天生 纹理。SurfaceTexture.OnFrameAvailableListener用于让SurfaceTexture的使用 者知道有新数据到来。JNISurfaceTextureContext是OnFrameAvailableListener从Native到Java的JNI跳板。此中 SurfaceTexture中的attachToGLContext()和detachToGLContext()可以让多个GL context共享同一个内容源。

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别
 

Android 5.0中将BufferQueue的核心部分分离出来,放在BufferQueueCore这个类中。BufferQueueProducer和BufferQueueConsumer分别是它的生产者和消耗 者实现基类(分别实现了IGraphicBufferProducer和IGraphicBufferConsumer接口)。它们都是由BufferQueue的静态函数createBufferQueue()来创建的。Surface是生产者端的实现类,提供dequeueBuffer/queueBuffer等硬件渲染接口,和lockCanvas/unlockCanvasAndPost等软件渲染接口,使内容流的源可以往BufferQueue中填graphic buffer。GLConsumer继承自ConsumerBase,是消耗 者端的实现类。它在基类的基础上添加了GL相干 的操作,如将graphic buffer中的内容转为GL纹理等操作。到此,以SurfaceTexture为中央 的一个pipeline大体是如许 的:

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别
 

TextureView

TextureView在4.0(API level 14)中引入。它可以将内容流直接投影到View中,可以用于实现Live preview等功能。和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为View hierachy中的一个平凡 View,因此可以和别的 平凡 View一样举行 移动,旋转,缩放,动画等变化。值得留意 的是TextureView必须在硬件加速的窗口中。它表现 的内容流数据可以来自App历程 或是远端历程 。从类图中可以看到,TextureView继承自View,它与别的 的View一样在View hierachy中管理与绘制。TextureView重载了draw()方法,此中 重要 把SurfaceTexture中收到的图像数据作为纹理更新到对应的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用于关照 TextureView内容流有新图像到来。SurfaceTextureListener接口用于让TextureView的使用 者知道SurfaceTexture已准备 好,如许 就可以把SurfaceTexture交给相应的内容源。Surface为BufferQueue的Producer接口实现类,使生产者可以通过它的软件或硬件渲染接口为SurfaceTexture内部的BufferQueue提供graphic buffer。

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别
 

实例解读

下面以VideoDumpView.java(位于/frameworks/base/media/tests/MediaDump/src/com/android/mediadump/)为例分析下SurfaceTexture的使用 。这个例子的效果 是从MediaPlayer中拿到视频帧,然后表现 在屏幕上,接着把屏幕上的内容dump到指定文件中。由于 SurfaceTexture本身只产生纹理,以是 这里还必要 GLSurfaceView共同 来做末了 的渲染输出。

起首 ,VideoDumpView是GLSurfaceView的继承类。在构造函数VideoDumpView()中会创建VideoDumpRenderer,也就是GLSurfaceView.Renderer的实例,然后调setRenderer()将之设成GLSurfaceView的Renderer。

  1. 109 public VideoDumpView(Context context) {
  2. ...
  3. 116 mRenderer = new VideoDumpRenderer(context);
  4. 117 setRenderer(mRenderer);
  5. 118 }
复制代码

随后,GLSurfaceView中的GLThread启动,创建EGL环境后回调VideoDumpRenderer中的onSurfaceCreated()。

  1. 519 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
  2. ...
  3. 551 // Create our texture. This has to be done each time the surface is created.
  4. 552 int[] textures = new int[1];
  5. 553 GLES20.glGenTextures(1, textures, 0);
  6. 554
  7. 555 mTextureID = textures[0];
  8. 556 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
  9. ...
  10. 575 mSurface = new SurfaceTexture(mTextureID);
  11. 576 mSurface.setOnFrameAvailableListener(this);
  12. 577
  13. 578 Surface surface = new Surface(mSurface);
  14. 579 mMediaPlayer.setSurface(surface);
复制代码

这里,起首 通过GLES创建GL的外部纹理。外部纹理阐明 它的真正内容是放在ion分配出来的体系 物理内存中,而不是GPU中,GPU中只是维护了其元数据。接着根据前面创建的GL纹理对象创建SurfaceTexture。流程如下:

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别

SurfaceTexture的参数为GLES接口函数glGenTexture()得到的纹理对象id。在初始化函数SurfaceTexture_init()中,先创建GLConsumer和相应的BufferQueue,再将它们的指针通过JNI放到SurfaceTexture的Java层对象成员中。

  1. 230static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
  2. 231 jint texName, jboolean singleBufferMode, jobject weakThiz)
  3. 232{
  4. ...
  5. 235 BufferQueue::createBufferQueue(&producer, &consumer);
  6. ...
  7. 242 sp<GLConsumer> surfaceTexture;
  8. 243 if (isDetached) {
  9. 244 surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
  10. 245 true, true);
  11. 246 } else {
  12. 247 surfaceTexture = new GLConsumer(consumer, texName,
  13. 248 GL_TEXTURE_EXTERNAL_OES, true, true);
  14. 249 }
  15. ...
  16. 256 SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
  17. 257 SurfaceTexture_setProducer(env, thiz, producer);
  18. ...
  19. 266 sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz,
  20. 267 clazz));
  21. 268 surfaceTexture->setFrameAvailableListener(ctx);
  22. 269 SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
复制代码

由于直接的Listener在Java层,而触发者在Native层,因此必要 从Native层回调到Java层。这里通过JNISurfaceTextureContext当了跳板。JNISurfaceTextureContext的onFrameAvailable()起到了Native和Java的桥接作用:

  1. 180void JNISurfaceTextureContext::onFrameAvailable()
  2. ...
  3. 184 env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
复制代码

此中 的fields.postEvent早在SurfaceTexture_classInit()中被初始化为SurfaceTexture的postEventFromNative()函数。这个函数往地点 线程的消息队列中放入消息,异步调用VideoDumpRenderer的onFrameAvailable()函数,关照 VideoDumpRenderer有新的数据到来。 回到onSurfaceCreated(),接下来创建供外部生产者使用 的Surface类。Surface的构造函数之一带有参数SurfaceTexture。

  1. 133 public Surface(SurfaceTexture surfaceTexture) {
  2. ...
  3. 140 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
复制代码

它实际 上是把SurfaceTexture中创建的BufferQueue的Producer接口实现类拿出来后创建了相应的Surface类。

  1. 135static jlong nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz,
  2. 136 jobject surfaceTextureObj) {
  3. 137 sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, surfaceTextureObj));
  4. ...
  5. 144 sp<Surface> surface(new Surface(producer, true));
复制代码

如许 ,Surface为BufferQueue的Producer端,SurfaceTexture中的GLConsumer为BufferQueue的Consumer端。当通过Surface绘制时,SurfaceTexture可以通过updateTexImage()来将绘制效果 绑定到GL的纹理中。 回到onSurfaceCreated()函数,接下来调用setOnFrameAvailableListener()函数将VideoDumpRenderer(实现SurfaceTexture.OnFrameAvailableListener接口)作为SurfaceTexture的Listener,由于 它要监听内容流上是否有新数据。接着将SurfaceTexture传给MediaPlayer,由于 这里MediaPlayer是生产者,SurfaceTexture是消耗 者。后者要吸收 前者输出的Video frame。如许 ,就通过Observer pattern建立起了一条关照 链:MediaPlayer -> SurfaceTexture -> VideDumpRenderer。在onFrameAvailable()回调函数中,将updateSurface标志设为true,表示有新的图像到来,必要 更新Surface了。为毛不在这儿立刻 更新纹理呢,由于 当前大概 不在渲染线程。SurfaceTexture对象可以在恣意 线程被创建(回调也会在该线程被调用),但updateTexImage()只能在含有纹理对象的GL context地点 线程中被调用。因此一样平常 环境 下回调中不能直接调用updateTexImage()。 与此同时,GLSurfaceView中的GLThread也在运行,它会调用到VideoDumpRenderer的绘制函数onDrawFrame()。

  1. 372 public void onDrawFrame(GL10 glUnused) {
  2. ...
  3. 377 if (updateSurface) {
  4. ...
  5. 380 mSurface.updateTexImage();
  6. 381 mSurface.getTransformMatrix(mSTMatrix);
  7. 382 updateSurface = false;
  8. ...
  9. 394 // Activate the texture.
  10. 395 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  11. 396 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
  12. ...
  13. 421 // Draw a rectangle and render the video frame as a texture on it.
  14. 422 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
  15. ...
  16. 429 DumpToFile(frameNumber);
复制代码

这里,通过SurfaceTexture的updateTexImage()将内容流中的新图像转成GL中的纹理,再举行 坐标转换。绑定刚天生 的纹理,画到屏幕上。整个流程如下:

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别

末了 onDrawFrame()调用DumpToFile()将屏幕上的内容倒到文件中。在DumpToFile()中,先用glReadPixels()从屏幕中把像素数据存到Buffer中,然后用FileOutputStream输出到文件。 上面讲了SurfaceTexture,下面看看TextureView是怎样 工作的。还是从例子动手 ,Android的关于TextureView的官方文档(http://developer.android.com/reference/android/view/TextureView.html)给了一个简洁的例子LiveCameraActivity。它它可以将Camera中的内容放在View中举行 表现 。在onCreate()函数中起首 创建TextureView,再将Activity(实现了TextureView.SurfaceTextureListener接口)传给TextureView,用于监听SurfaceTexture准备 好的信号。

  1. protected void onCreate(Bundle savedInstanceState) {
  2. ...
  3. mTextureView = new TextureView(this);
  4. mTextureView.setSurfaceTextureListener(this);
  5. ...
  6. }
复制代码

TextureView的构造函数并不做重要 的初始化工作。重要 的初始化工作是在getHardwareLayer()中,而这个函数是在其基类View的draw()中调用。TextureView重载了这个函数:

  1. 348 HardwareLayer getHardwareLayer() {
  2. ...
  3. 358 mLayer = mAttachInfo.mHardwareRenderer.createTextureLayer();
  4. 359 if (!mUpdateSurface) {
  5. 360 // Create a new SurfaceTexture for the layer.
  6. 361 mSurface = new SurfaceTexture(false);
  7. 362 mLayer.setSurfaceTexture(mSurface);
  8. 363 }
  9. 364 mSurface.setDefaultBufferSize(getWidth(), getHeight());
  10. 365 nCreateNativeWindow(mSurface);
  11. 366
  12. 367 mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
  13. 368
  14. 369 if (mListener != null && !mUpdateSurface) {
  15. 370 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
  16. 371 }
  17. ...
  18. 390 applyUpdate();
  19. 391 applyTransformMatrix();
  20. 392
  21. 393 return mLayer;
  22. 394 }
复制代码

由于 TextureView是硬件加速层(范例 为LAYER_TYPE_HARDWARE),它起首 通过HardwareRenderer创建相应的HardwareLayer类,放在mLayer成员中。然后创建SurfaceTexture类,详细 流程见前文。之后将HardwareLayer与SurfaceTexture做绑定。接着调用Native函数nCreateNativeWindow,它通过SurfaceTexture中的BufferQueueProducer创建Surface类。留意 Surface实现了ANativeWindow接口,这意味着它可以作为EGL Surface传给EGL接口从而举行 硬件绘制。然后setOnFrameAvailableListener()将监听者mUpdateListener注册到SurfaceTexture。如许 ,当内容流上有新的图像到来,mUpdateListener的onFrameAvailable()就会被调用。然后必要 调用注册在TextureView中的SurfaceTextureListener的onSurfaceTextureAvailable()回调函数,关照 TextureView的使用 者SurfaceTexture已停当 。整个流程大体如下:

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别

留意 这里这里为TextureView创建了DeferredLayerUpdater,而不是像Android 4.4(Kitkat)中返回GLES20TextureLayer。由于 Android 5.0(Lollipop)中在App端分离出了渲染线程,并将渲染工作放到该线程中。这个线程还能吸收 VSync信号,因此它还能本身 处理动画。究竟 上,这里DeferredLayerUpdater的创建就是通过同步方式在渲染线程中做的。DeferredLayerUpdater,顾名思义,就是将Layer的更新哀求 先记录在这,当渲染线程真正要画的时间 ,再举行 真正的操作。此中 的setSurfaceTexture()会调用HardwareLayer的Native函数nSetSurfaceTexture()将SurfaceTexture中的surfaceTexture成员(范例 为GLConsumer)传给DeferredLayerUpdater,如许 之后要更新纹理时DeferredLayerUpdater就知道从那里 更新了。 前面提到初始化中会调用onSurfaceTextureAvailable()这个回调函数。在它的实现中,TextureView的使用 者就可以将准备 好的SurfaceTexture传给数据源模块,供数据源输出之用。如:

  1. public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
  2. mCamera = Camera.open();
  3. ...
  4. mCamera.setPreviewTexture(surface);
  5. mCamera.startPreview();
  6. ...
  7. }
复制代码

看一下setPreviewTexture()的实现,此中 把SurfaceTexture中初始化时创建的GraphicBufferProducer拿出来传给Camera模块。

  1. 576static void android_hardware_Camera_setPreviewTexture(JNIEnv *env,
  2. 577 jobject thiz, jobject jSurfaceTexture)
  3. ...
  4. 585 producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
  5. ...
  6. 594 if (camera->setPreviewTarget(producer) != NO_ERROR) {
复制代码

到这里,统统 都初始化地差不多了。接下来当内容流有新图像可用,TextureView会被关照 到(通过SurfaceTexture.OnFrameAvailableListener接口)。SurfaceTexture.OnFrameAvailableListener是SurfaceTexture有新内容来时的回调接口。TextureView中的mUpdateListener实现了该接口:

  1. 755 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
  2. 756 updateLayer();
  3. 757 invalidate();
  4. 758 }
复制代码

可以看到此中 会调用updateLayer()函数,然后通过invalidate()函数申请更新UI。updateLayer()会设置mUpdateLayer标志位。如许 ,当下次VSync到来时,Choreographer关照 App通过重绘View hierachy。在UI重绘函数performTranversals()中,作为View hierachy的一分子,TextureView的draw()函数被调用,此中 便会相继调用applyUpdate()和HardwareLayer的updateSurfaceTexture()函数。

  1. 138 public void updateSurfaceTexture() {
  2. 139 nUpdateSurfaceTexture(mFinalizer.get());
  3. 140 mRenderer.pushLayerUpdate(this);
  4. 141 }
复制代码

updateSurfaceTexture()实际 通过JNI调用到android_view_HardwareLayer_updateSurfaceTexture()函数。在此中 会设置相应DeferredLayerUpdater的标志位mUpdateTexImage,它表示在渲染线程中必要 更新该层的纹理。

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别
 

前面提到,Android 5.0引入了渲染线程,它是一个更大的topic,超出本文范围,这里只说相干 的部分。作为背景知识,下面只画出了相干 的类。可以看到,ThreadedRenderer作为新的HardwareRenderer替换 了Android 4.4中的Gl20Renderer。此中 比较关键的是RenderProxy类,必要 让渲染线程干活时就通过这个类往渲染线程发使命 。RenderProxy中指向的RenderThread就是渲染线程的主体了,此中 的threadLoop()函数是主循环,大多数时间它会poll在线程的Looper上等待,当有同步哀求 (或者VSync信号)过来,它会被唤醒,然后处理TaskQueue中的使命 。TaskQueue是RenderTask的队列,RenderTask代表一个渲染线程中的使命 。如DrawFrameTask就是RenderTask的继承类之一,它重要 用于渲染当前帧。而DrawFrameTask中的DeferredLayerUpdater集合就存放着之前对硬件加速层的更新操作申请。

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别

当主线程准备 好渲染数据后,会以同步方式让渲染线程完成渲染工作。此中 会先调用processLayerUpdate()更新全部 硬件加速层中的属性,继而调用到DeferredLayerUpdater的apply()函数,此中 检测到标志位mUpdateTexImage被置位,于是会调用doUpdateTexImage()真正更新GL纹理和转换坐标。

Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别
 

末了 ,总结下这几者的区别和接洽 。简单地说,SurfaceView是一个有本身 Surface的View。它的渲染可以放在单独线程而不是主线程中。其缺点是不能做变形和动画。SurfaceTexture可以用作非直接输出的内容流,如许 就提供二次处理的机会。与SurfaceView直接输出相比,如许 会有多少 帧的耽误 。同时,由于它本身管理BufferQueue,因此内存斲丧 也会轻微 大一些。TextureView是一个可以把内容流作为外部纹理输出在上面的View。它本身必要 是一个硬件加速层。究竟 上TextureView本身也包含了SurfaceTexture。它与SurfaceView+SurfaceTexture组合相比可以完成雷同 的功能(即把内容流上的图像转成纹理,然后输出)。区别在于TextureView是在View hierachy中做绘制,因此一样平常 它是在主线程上做的(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。而SurfaceView+SurfaceTexture在单独的Surface上做绘制,可以是用户提供的线程,而不是体系 的主线程或是渲染线程。别的 ,与TextureView相比,它还有个好处是可以用Hardware overlay举行 表现 。

到此这篇关于Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别的文章就先容 到这了,更多相干 Android SurfaceTexture TextureView SurfaceView GLSurfaceView内容请搜索 脚本之家从前 的文章或继续欣赏 下面的相干 文章渴望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 风来时狂放 | 2021-9-14 09:08:40 | 显示全部楼层
顶一下,收藏了!
回复

使用道具 举报

avatar 恶贯满瘾锥 | 2021-9-21 07:23:46 | 显示全部楼层
admin楼主说的我也略懂!
回复

使用道具 举报

avatar 清荼蘼荼iq | 2021-9-26 23:32:35 | 显示全部楼层
这么版块的帖子越来越有深度了!
回复

使用道具 举报

avatar 天然悠然牌 | 2021-9-28 20:20:18 | 显示全部楼层
admin楼主,我告诉你一个你不知道的的秘密,有一个牛逼的源码论坛他的站点都是商业源码,还是免费下载的那种!特别好用。访问地址:http://www.mxswl.com 猫先森网络
回复

使用道具 举报

avatar hecgdge4 | 2021-10-7 09:18:18 | 显示全部楼层
刚分手,心情不好!
回复

使用道具 举报

avatar 忆神姆原们 | 2021-10-7 21:23:48 | 显示全部楼层
楼上的能详细介绍一下么?
回复

使用道具 举报

avatar V刘晨曦 | 2021-10-8 03:17:28 | 显示全部楼层
突然觉得admin楼主说的很有道理,赞一个!
回复

使用道具 举报

avatar 一心之恋祭 | 2021-10-8 17:30:33 | 显示全部楼层
脑残片admin楼主今天吃了么?
回复

使用道具 举报

avatar aupdbe229 | 2021-10-9 16:37:57 | 显示全部楼层
鸟大了,什么林子都敢进啊!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则