下載下傳
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渲染顯示圖像