天天看點

基于iOS的網絡音視訊實時傳輸系統(六)- AudioQueue播放音頻,OpenGL渲染顯示圖像 下載下傳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篇文章
當時我是直接在官方的代碼上找的示例,然後一試就可以了。下面是引用官方指南的部分資訊:

用于播放的音頻隊列
播放音頻隊列的結構如下所示。
基于iOS的網絡音視訊實時傳輸系統(六)- AudioQueue播放音頻,OpenGL渲染顯示圖像 下載下傳AudioQueue播放音頻OpenGL渲染顯示圖像參考文章 相關文章
播放過程如下所示:
基于iOS的網絡音視訊實時傳輸系統(六)- AudioQueue播放音頻,OpenGL渲染顯示圖像 下載下傳AudioQueue播放音頻OpenGL渲染顯示圖像參考文章 相關文章
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渲染顯示圖像