基于ffmpeg 直播推流和播放rtmp (IOS源码)
ios直播推流每秒能达到30帧,比安卓要强,视频采用软编码的话手机会发烫,得采用码编码,播放视频采用opengl渲染。
ffmpeg初始化代码如下:
int init_Code(int width, int height, const char *out_path) {
av_log_set_callback(custom_log);
//avcodec_register_all();
av_register_all();
avformat_network_init();
avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_path);
//Open output URL,set before avformat_write_header() for muxing
AVOutputFormat * ofmt = ofmt_ctx->oformat;
if (!(ofmt->flags & AVFMT_NOFILE)) { //Open output URL
if (avio_open(&ofmt_ctx->pb, out_path, AVIO_FLAG_READ_WRITE) < ) {
return -;
}
}
if (isAudio == ) {
if (init_audio_Code() != )//初始化 音频参数
return -;
}
if (isVideo == ) {
if (init_video_code(width, height) != )//初始化 视频参数
return -;
}
av_dump_format(ofmt_ctx, , out_path, );
if (avformat_write_header(ofmt_ctx, NULL) < ) { //Write file header
//LOGE("Error occurred when opening output file\n");
return -;
}
start_thread_encode(); //开始编码
return ;
}
视频硬编码:
/* 视频 硬编码**/
int encodeVideo_h264(uint8_t* in, int64_t time, int size, int keyframe) {
int ret;
// 定义AVPacket对象后,请使用av_init_packet进行初始化
av_init_packet(&video_pkt);
//av_new_packet(&video_pkt,size);
video_pkt.stream_index = video_st->index;
video_pkt.data = in; video_pkt.size = size;
video_pkt.pos = -;
ptsPacket(video_st, &video_pkt, time);
if (video_pkt.buf != NULL) {
video_pkt.buf->data = in;
video_pkt.buf->size = size;
}
video_pkt.flags = keyframe;
if (keyframe == ) {
//LOGE("硬编码-关键帧: %lld", time);
} ret = av_interleaved_write_frame(ofmt_ctx, &video_pkt);
if (ret != ) {
printf("----encodeVideo--encodeVideo -ret: %d ", ret);
//LOGE("----encodeVideo--encodeVideo -ret: %d ", ret);
}
av_free_packet(&video_pkt); return ;
}
音频硬编码:
/* 音频 硬编码**/
int encodeAudio_AAC(uint8_t* in, int64_t time, int size) {
if (isAudio == )
return ;
av_init_packet(&audio_pkt);
int got_frame = ;
audio_pkt.stream_index = audio_st->index; //标识该AVPacket所属的视频/音频流。
audio_pkt.data = in;
audio_pkt.size = size;
audio_pkt.pts = time;
audio_pkt.dts = time;
//audio_pkt.pos = -1;
audio_pkt.flags = ;
//audio_pkt.duration = 10;
int ret = av_interleaved_write_frame(ofmt_ctx, &audio_pkt);
if (ret != ) {
//LOGE("----encodeAudio---ret: %d size:%d ,time:%lld ",
// ret, size, time);
}
return ;
}
初始化相机:
- (void) initCamera:(BOOL)type
{
NSError *deviceError;
AVCaptureDeviceInput *inputCameraDevice;
if (type==false)
{
inputCameraDevice = [AVCaptureDeviceInput deviceInputWithDevice:cameraDeviceB error:&deviceError];
}
else
{
inputCameraDevice = [AVCaptureDeviceInput deviceInputWithDevice:cameraDeviceF error:&deviceError];
}
AVCaptureVideoDataOutput *outputVideoDevice = [[AVCaptureVideoDataOutput alloc] init]; NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
NSNumber* val = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange];
NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:val forKey:key];
outputVideoDevice.videoSettings = videoSettings;
[outputVideoDevice setSampleBufferDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, )];
captureSession = [[AVCaptureSession alloc] init];
[captureSession addInput:inputCameraDevice];
[captureSession addOutput:outputVideoDevice];
[captureSession beginConfiguration]; [captureSession setSessionPreset:[NSString stringWithString:AVCaptureSessionPreset352x288]];
connectionVideo = [outputVideoDevice connectionWithMediaType:AVMediaTypeVideo];
#if TARGET_OS_IPHONE
[self setRelativeVideoOrientation]; NSNotificationCenter* notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self
selector:@selector(statusBarOrientationDidChange:)
name:@"StatusBarOrientationDidChange"
object:nil];
#endif [captureSession commitConfiguration];
recordLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
[recordLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
}
设置音频参数
- (void)setupAudioFormat:(UInt32) inFormatID SampleRate:(int)sampeleRate
{
//重置下
memset(&_recordFormat, , sizeof(_recordFormat));
//设置采样率,这里先获取系统默认的测试下 //TODO:
//采样率的意思是每秒需要采集的帧数
_recordFormat.mSampleRate = sampeleRate;//[[AVAudioSession sharedInstance] sampleRate];
UInt32 size = sizeof(_recordFormat.mSampleRate);
//AudioSessionGetProperty( kAudioSessionProperty_CurrentHardwareSampleRate,
// &size,
// &_recordFormat.mSampleRate);
size = sizeof(_recordFormat.mChannelsPerFrame);
// AudioSessionGetProperty( kAudioSessionProperty_CurrentHardwareInputNumberChannels,
// &size,
// &_recordFormat.mChannelsPerFrame); _recordFormat.mFormatID = inFormatID;
if (inFormatID == kAudioFormatLinearPCM){
//这个屌属性不知道干啥的。,//要看看是不是这里属性设置问题
//结果分析: 8bit为1byte,即为1个通道里1帧需要采集2byte数据,再*通道数,即为所有通道采集的byte数目。
//所以这里结果赋值给每帧需要采集的byte数目,然后这里的packet也等于一帧的数据。 _recordFormat.mFramesPerPacket = ;
_recordFormat.mSampleRate =sampeleRate;// 16000.0;
//每个通道里,一帧采集的bit数目 语音每采样点占用位数
_recordFormat.mBitsPerChannel = ;
_recordFormat.mChannelsPerFrame = ;// 1:单声道;2:立体声
_recordFormat.mFramesPerPacket = ;
_recordFormat.mBytesPerFrame = (_recordFormat.mBitsPerChannel / ) * _recordFormat.mChannelsPerFrame;
_recordFormat.mBytesPerPacket = _recordFormat.mBytesPerFrame * _recordFormat.mFramesPerPacket;
//_recordFormat.mBytesPerPacket = _recordFormat.mBytesPerFrame = (_recordFormat.mBitsPerChannel / 8) * _recordFormat.mChannelsPerFrame;
_recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
//_recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
}
}
开始录音:
-(void)startRecording
{
UInt32 size;
NSError *error = nil;
//设置audio session的category
BOOL ret = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:&error];//注意,这里选的是AVAudioSessionCategoryPlayAndRecord参数,如果只需要录音,就选择Record就可以了,如果需要录音和播放,则选择PlayAndRecord,这个很重要
if (!ret) {
NSLog(@"设置声音环境失败");
return;
}
//启用audio session
ret = [[AVAudioSession sharedInstance] setActive:YES error:&error];
if (!ret)
{
NSLog(@"启动失败");
return;
}
//初始化音频输入队列
AudioQueueNewInput(&_recordFormat, inputBufferHandler, (__bridge void *)(self), NULL, kCFRunLoopCommonModes, , &_audioQueue);//inputBufferHandler这个是回调函数名
size = sizeof(_recordFormat);
//AudioQueueGetProperty(_audioQueue, kAudioQueueProperty_StreamDescription,
// &_recordFormat, &size);
//计算估算的缓存区大小
//int frames = (int)ceil(kDefaultBufferDurationSeconds * _recordFormat.mSampleRate);//返回大于或者等于指定表达式的最小整数
int bufferByteSize =;// frames * _recordFormat.mBytesPerFrame;//缓冲区大小在这里设置,这个很重要,在这里设置的缓冲区有多大,那么在回调函数的时候得到的inbuffer的大小就是多大。
//bufferByteSize=[self ComputeRecordBufferSize:&_recordFormat sss:kDefaultBufferDurationSeconds];
NSLog(@"缓冲区大小:%d",bufferByteSize);
AudioQueueBufferRef _audioBuffers[];
//创建缓冲器
for (int i = ; i < kNumberAudioQueueBuffers; i++){
AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_audioBuffers[i]);
AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[i], , NULL);//将 _audioBuffers[i]添加到队列中
}
// 开始录音
AudioQueueStart(_audioQueue, NULL); }
源码地址:http://pan.baidu.com/s/1qXOSznA
基于ffmpeg 直播推流和播放rtmp (IOS源码)的更多相关文章
- 最简单的基于FFmpeg的移动端例子:IOS 推流器
转至:http://blog.csdn.net/leixiaohua1020/article/details/47072519 ================================== ...
- 最简单的基于FFmpeg的推流器(以推送RTMP为例)
===================================================== 最简单的基于FFmpeg的推流器系列文章列表: <最简单的基于FFmpeg的推流器(以 ...
- day122:MoFang:OSSRS流媒体直播服务器&基于APICloud的acLive直播推流模块实现RTMP直播推流
目录 1.docker安装OSSRS流媒体直播服务器 2.基于APICloud的acLive直播推流模块实现RTMP直播推流 3.直播流管理 1.docker安装OSSRS流媒体直播服务器 1.OSS ...
- 最简单的基于FFmpeg的移动端例子:IOS 视频解码器-保存
===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...
- 最简单的基于FFmpeg的移动端例子:IOS 视频转码器
===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (采用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- 最简单的基于FFMPEG+SDL的音频播放器 ver2 (採用SDL2.0)
===================================================== 最简单的基于FFmpeg的音频播放器系列文章列表: <最简单的基于FFMPEG+SDL ...
- 一个功能齐全的IOS音乐播放器应用源码
该源码是在ios教程网拿过来的,一个不错的IOS音乐播放器应用源码,这个是我当时进公司时 我用了一晚上写的 图片都是在别的地方扒的,主要是歌词同步,及上一曲,下一曲,功能齐全了 ,大家可以学习一下吧 ...
- ios源码-ios游戏源码-ios源码下载
游戏源码 一款休闲类的音乐小游戏源码 该源码实现了一款休闲类的音乐小游戏源码,该游戏的源码很简单,而且游戏的玩法也很容易学会,只要我们点击视图中的grid,就可以 人气:2943运行环境:/Xco ...
随机推荐
- 完美解决“find: 路径必须在表达式之前:”
使用find命令查找文件的时候会有如题的提示,只需略作修改即可.两种方法: ①可cd到其它目录再查找(当前目录下存在要查找的目标文件时会出现此类错误,换到其它目录下再执行相同的命令即可): ②如果目标 ...
- jQuery匹配各种条件的选择器用法
:hidden匹配所有的不可见元素,input 元素的 type 属性为 "hidden" 的话也会被匹配到Matches all elements that are hidden ...
- Razor语法(三)
1.定义变量 定义变量或声明常量必须在代码体内,代码体以'@{'开头,以'}'结束,其中定义变量以'var'进行声明.代码体内每行以';'做为结束标识. @{ var i = 10; ...
- 编译安装mysql-5.6.40
编译安装mysql-5.6.40 环境说明 系统版本 CentOS 7.2 x86_64 软件版本 mysql-5.6.40 [root@db01 ~]# mkdir -p /serv ...
- 一个.net Cookie组件的bug引发的题外话
在.net里,做过Http模拟发送请求的朋友们应该遇到过,有个时候无论怎么努力,都没办法让Cookie跟网页用浏览器所收集的一样,其中原因除了有些Cookie大概是ReadOnly之外,似乎另有隐情: ...
- LoadRunner+Android模所器实现抓包并调试本地服务端
步骤就是 1:新建LR脚本.协议选择Mobile Application - HTTP/HTML 2:在record里选择第三个:Record Emulator........ 3: 选择下一步后, ...
- CSS3Transition添加多个过渡效果
本篇文章由:http://xinpure.com/css3transition-to-add-multiple-transition-effects/ 通过监听动画的结束事件,可以为一个元素添加多个动 ...
- 详解Android中那些酷炫返回方式的实现
Android手机都会有返回键,不管是实体键,还是虚拟键.Android用户主要也都是通过这个返回键操控页面返回方式的,不比IOS逼格甚高的只保留一个操作键.这种方式是最普遍的返回方式,还有一种也是比 ...
- canvas学习笔记(中篇) -- canvas入门教程-- 颜色/透明度/渐变色/线宽/线条样式/虚线/文本/阴影/图片/像素处理
[中篇] -- 建议学习时间4小时 课程共(上中下)三篇 此笔记是我初次接触canvas的时候的学习笔记,这次特意整理为博客供大家入门学习,几乎涵盖了canvas所有的基础知识,并且有众多练习案例, ...
- 在Ubuntu下安装mongodb
一. 在Ubuntu下最傻瓜的步骤(以下都在root用户下进行操作): 1.运行"apt-get install mongo" 如果遇到找不到安装包的话运行"apt-ge ...