美文网首页视频OpenGL+MetalOpenGL ES 学习资料
iOS开发-OpenGL ES实践教程(一)

iOS开发-OpenGL ES实践教程(一)

作者: 落影loyinglin | 来源:发表于2016-06-11 14:17 被阅读9714次

    教程

    入门教程和进阶教程,介绍的是OpenGL ES基础,学习图形学基本概念,了解OpenGL ES的特性。
    实践教程是OpenGL ES在实际开发中的应用,demo的来源主要是apple官网github
    这一次的内容是用OpenGL ES绘制YUV视频:获取到视频的每帧图像信息,用OpenGL ES绘制出来

    效果展示

    核心思路

    通过APLImagePickerController选择本地的视频文件,用AVPlayer播放音频,用OpenGL ES绘制视频。

    具体细节

    1、AVPlayer

    AVAsset:用于获取多媒体信息。
    AVPlayerItem:管理视频的基本信息和状态。
    AVPlayer:用来读取本地或者远程的多媒体文件。
    AVPlayer的使用实例

        AVAsset *movieAsset = [AVURLAsset URLAssetWithURL:sourceMovieURL options:nil];
        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:movieAsset];
        AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
        AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
        playerLayer.frame = self.view.layer.bounds;
        playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
        [self.view.layer addSublayer:playerLayer];
        [player play];
    

    可以使用KVO,观察playerItem的status和loadedTimeRange属性
    status有三种状态:
    AVPlayerStatusUnknown
    AVPlayerStatusReadyToPlay:视频可以播放
    AVPlayerStatusFailed
    loadedTimeRange属性代表已经缓冲的进度。

    在demo中,还用到一个AVPlayerItemVideoOutput类,用于协调输出的CoreVideo像素缓存,配置AVPlayerItem。

    CADisplayLink帧显示的定时器
    通过 CADisplayLink的timestamp 和 duration,计算下一帧显示的时间
    从videoOutput中取出像素数据copyPixelBufferForItemTime
    再通过displayPixelBuffer把数据交给OpenGL ES绘制。

    2、CGAffineTransform

    CGAffineTransform是一个矩阵,结构如下
    | a, b, 0 |
    | c, d, 0 |
    | tx, ty, 1 |
    demo中会通过videoTrack的preferredTransform来获取拍摄开始时候的旋转角度,从而修正显示的角度。即使录制的时候是反着,显示也会是正的。

    3、CMTime

    CMTimeMake(a,b) a当前第几帧, b每秒钟多少帧.当前播放时间a/b
    CMTimeMakeWithSeconds(a,b) a当前时间,b每秒钟多少帧

    demo中的addPeriodicTimeObserverForInterval 方法
    添加回调CMTimeMake(1, 2)每秒回调两次

    4、APLEAGLView

    自定义的UIView子类,用OpenGL ES绘制视频。
    preferredRotation 旋转的弧度
    presentationRect 显示视频的大小
    chromaThreshold 色度大小
    lumaThreshold 亮度大小

    kColorConversion601kColorConversion709是两个YUV颜色空间到RGB颜色空间的转换矩阵。
    OpenGL ES的基础不再赘述,入门教程和进阶教程这里有详细的介绍,这次着重介绍如何把YUV的视频显示绘制到屏幕上。
    CVOpenGLESTextureCacheRef :缓存和管理CVOpenGLESTextureRef。
    CVOpenGLESTextureRef、CVImageBufferRef、CVBufferRef这三个实际上是一样的,是CV纹理的缓存。

    demo中的pixelBuffer是从AVPlayerItemVideoOutput获取到图像数据,再用CVOpenGLESTextureCacheCreateTextureFromImage从buffer中读取数据并创建chromaTexture和lumaTexture。

    AVMakeRectWithAspectRatioInsideRect会计算得出合适的视频宽高,不超过layer的bounds,再与bounds相除,以此作为顶点坐标的位置数据。

    5、shader

    • 顶点着色器。通过preferredRotation计算出rotationMatrix,再对position进行操作。
    • 片元着色器。从SamplerY和SamplerUV中取出颜色,再与lumaThreshold和chromaThreshold相乘得出最后的颜色。

    总结

    从iOS设备中获取到每一帧的视频信息,可以使用AV框架。
    使用OpenGL ES绘制视频部分的逻辑与之前教程介绍相差不多,增加了CVOpenGLESTextureCacheRef的使用。

    附上代码

    相关文章

      网友评论

      • Everdinner:大神,看了你的基础和进阶系列教程,到这篇实践教程里面有一个低级的数学问题不太理解,这篇demo里顶点着色器的那个gl_Position = position * rotationMatrix;这个计算向量是四行一列的矩阵,而后面是一个四行四列的矩阵,他们是怎么直接相乘的?还是我理解有误
      • GeniusWong:你好。 这个APLEAGLView, 我在一个界面里加了两个, 因为我的需求是同时播放两个视频。 现在问题是,第二个加上去的APLEAGLView可以播放, 第一个就不会播放。
      • hhjdk:大神,我用kxmovie 获得了一个本地视频 每一帧(KxVideoFrameRGB) ,再把每一帧用OpenGL渲染,但是渲染以后,视频播放会有杂音,你知道这是什么问题吗?先谢谢了,你的文章很棒
        落影loyinglin:@hhjdk 你看是不是处理不过来了
        hhjdk:@落影lying-in 大神, 再向你请教一个问题,从kxmovie 获得了一个本地视频 每一帧(KxVideoFrameRGB),用OpenGL渲染之后,视频播放也会好卡,而且有杂音,这是什么原因,但是我不去调用渲染视频的demo,声音播放就不会有杂音。这到底是什么情况你
        落影loyinglin:@hhjdk 杂音和opengl没关系,得看你的音频编解码和播放怎么弄的
      • 无声的叹息:AVPlayer好像不能拿到音频轨道的sampler buffer,我尝试了下面两种方法,没有找到合适的方法对非本地的URL视频进行重新编码?
        (1)对于一个HTTP的视频链接,我通过AVPlayerItemVideoOutput获取到它的视频输出(无法获取音频的buffer),因此只能进行播放,而不能对OpenGL绘制后的视频帧和音频(音频不需要处理)一起进行编码。
        (2)通过AVAssetReader无法通过非本地的URL初始化,也就拿不到音频了;
        落影loyinglin:@无声的叹息 MPMoviePlayerController是支持在线播放的。
        无声的叹息:@落影loyinglin mp4格式的,用AVAssetReader初始化的时候出现
        "Cannot initialize an instance of AVAssetReader with an asset at non-local URL 'https://mvvideo5.meitudata.com/571090934cea5517.mp4'"
        落影loyinglin:@无声的叹息 http的视频链接是什么格式的
      • Mikesong:请问一下,上面说的需要在那些地方改,能不能大概说说思路和方法
      • 陈_某_某:CVPixelBufferRef可以用GLKBaseEffect绘制纹理吗?
        落影loyinglin:@_iOSer 刚刚试了下,可以调用
        self.mEffect.texture2d0.name = CVOpenGLESTextureGetName(renderTexture);

      本文标题:iOS开发-OpenGL ES实践教程(一)

      本文链接:https://www.haomeiwen.com/subject/xdysdttx.html