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


下载


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渲染显示图像


猜你喜欢

转载自blog.csdn.net/a997013919/article/details/78215581