您的位置:首页 > 理论基础 > 计算机网络

[置顶] 基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像

2017-10-12 15:37 1201 查看


下载

GitHub:

client 端:https://github.com/AmoAmoAmo/Smart_Device_Client

server端:https://github.com/AmoAmoAmo/Smart_Device_Server

另还写了一份macOS版的server,但是目前还有一些问题,有兴趣的去看看吧:https://github.com/AmoAmoAmo/Server_Mac

AudioQueue播放音频

在上一篇中写了解码H264,不过AAC可以省略解码的步骤

因为AudioQueue函数提供的接口可以直接播放AAC音频,估计解码的操作它内部自己帮我们做了,

AudioQueue的使用主要就是几个函数,还有就是它是偏C的函数,所以ARC管不了,我们自己要注意内存的管理。

具体可以参考下面的123篇文章

当时我是直接在官方的代码上找的示例,然后一试就可以了。下面是引用官方指南的部分信息:



用于播放的音频队列

播放音频队列的结构如下所示。



播放过程如下所示:



1、给buffer填充数据,并把buffer放入就绪的buffer queue;

2、应用通知队列开始播放;

3、队列播放第一个填充的buffer;

4、队列返回已经播放完毕的buffer,并开始播放下面一个填充好的buffer;

5、队列调用之前设置的回调函数,填充播放完毕的buffer;

6、回调函数中把buffer填充完毕,并放入buffer queue中。



OpenGL渲染显示图像

这里视频渲染使用的是 OpenGL ES。关于这部分,网上的教程还是挺多的。

OpenGL是一个非常庞大而又专业的知识,如果想完全撑握它需要花不少时间。而视频渲染只用到了其中的一小部分知识,

建议参考文末的参考文章

初始化OpenGL ES上下文

// 初始化EAGLContext时指定ES版本号
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

if (!_context || ![EAGLContext setCurrentContext:_context] || ![self loadShaders]) {
return nil;
}

设置帧缓冲区

- (void)setupBuffers
{
glDisable(GL_DEPTH_TEST);

glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);

glEnableVertexAttribArray(ATTRIB_TEXCOORD);
glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);

glGenFramebuffers(1, &_frameBufferHandle);              // 创建帧缓冲区
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);  // 绑定帧缓冲区到渲染管线

glGenRenderbuffers(1, &_colorBufferHandle);             // 创建绘制缓冲区
glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);// 绑定绘制缓冲区到渲染管线

// 为绘制缓冲区分配存储区,此处将CAEAGLLayer的绘制存储区作为绘制缓冲区的存储区
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);   // 获取绘制缓冲区的像素宽度
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight); // ...

// 绑定绘制缓冲区到帧缓冲区
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBufferHandle);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
}

着色器编译

- (BOOL)loadShaders
{
GLuint vertShader, fragShader;
NSURL *vertShaderURL, *fragShaderURL;

self.program = glCreateProgram();

// 创建并编译顶点着色器。
vertShaderURL = [[NSBundle mainBundle] URLForResource:@"Shader" withExtension:@"vsh"];
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER URL:vertShaderURL]) {
NSLog(@"Failed to compile vertex shader");
return NO;
}

// 创建和编译帧着色器(fragment shader)。
fragShaderURL = [[NSBundle mainBundle] URLForResource:@"Shader" withExtension:@"fsh"];
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER URL:fragShaderURL]) {
NSLog(@"Failed to compile fragment shader");
return NO;
}

// Attach vertex shader to program.   附上顶点着色器
glAttachShader(self.program, vertShader);

// Attach fragment shader to program. 附上帧着色器
glAttachShader(self.program, fragShader);

// Bind attribute locations. This needs to be done prior to linking.
glBindAttribLocation(self.program, ATTRIB_VERTEX, "position");
glBindAttribLocation(self.program, ATTRIB_TEXCOORD, "texCoord");

// Link the program.
if (![self linkProgram:self.program]) {
NSLog(@"Failed to link program: %d", self.program);

if (vertShader) {
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader) {
glDeleteShader(fragShader);
fragShader = 0;
}
if (self.program) {
glDeleteProgram(self.program);
self.program = 0;
}

return NO;
}
// 等等等等,代码省略
......
return YES;
}


传入视频帧,开始绘制

使用像素缓冲区的颜色附件来确定适当的颜色转换矩阵。

CFTypeRef colorAttachments = CVBufferGetAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, NULL);
_preferredConversion = kColorConversion601FullRange; //         YCbCr->RGB


从像素缓冲区创建y和UV纹理
这些纹理将被绘制在帧缓冲Y平面。
glActiveTexture(GL_TEXTURE0);
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
_videoTextureCache,
pixelBuffer,
NULL,
GL_TEXTURE_2D,
GL_LUMINANCE,
frameWidth,
frameHeight,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
0,
&_lumaTexture);


使用着色器
glUseProgram(self.program);
glUniformMatrix3fv(uniforms[UNIFORM_COLOR_CONVERSION_MATRIX], 1, GL_FALSE, _preferredConversion);

设置四个顶点

四顶点数据定义了我们绘制像素缓冲区的二维平面区域。

顶点数据分别用(-1,-1)和(1,1)作为左下角和右上角坐标,覆盖整个屏幕。

GLfloat quadVertexData [] = {
-1 , -1 ,
1, -1,
-1 , 1,
1, 1,
};

// 更新顶点数据
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, quadVertexData);
glEnableVertexAttribArray(ATTRIB_VERTEX);


参考文章

1.  http://www.cnblogs.com/perryxiong/p/3790008.html

2.  http://www.jianshu.com/p/279a9e5b36b5

3.  https://developer.apple.com/documentation/audiotoolbox

4.  http://www.jianshu.com/p/750fde1d8b6a

5.  http://blog.csdn.net/hejjunlin/article/details/62976457


相关文章

基于iOS的网络音视频实时传输系统(一)- 前言

基于iOS的网络音视频实时传输系统(二)- 捕获音视频数据

基于iOS的网络音视频实时传输系统(三)- VideoToolbox编码音视频数据为H264、AAC

基于iOS的网络音视频实时传输系统(四)- 自定义socket协议(TCP、UDP)

基于iOS的网络音视频实时传输系统(五)- 使用VideoToolbox硬解码H264

基于iOS的网络音视频实时传输系统(六)- AudioQueue播放音频,OpenGL渲染显示图像
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐