基于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 ...
随机推荐
- RawCap抓取本地回环接口数据包
RawCap.exe --help ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 D: ...
- Kali之Metasploit Framework环境配置
运行Metasploit Framework 依照Kali Linux网络服务策略,Kali没有自动启动的网络服务,包括数据库服务在内.所以为了让Metasploit以支持数据库的方式运行有些必要的步 ...
- Linux驱动mmap内存映射
mmap在linux哪里? 什么是mmap? 上图说了,mmap是操作这些设备的一种方法,所谓操作设备,比如IO端口(点亮一个LED).LCD控制器.磁盘控制器,实际上就是往设备的物理地址读写数据. ...
- spfile 和用户环境变量 和export
RAC数据库环境变量设置 [oracle@rac1 ~]$ sqlplus / as sysdba SQL*Plus: Release 10.2.0.1.0 - Production on Fri ...
- 约瑟夫环形链表问题、丢手帕问题、剑指offer圆圈中最后一个数问题
public class Solution { // 左神解法,本题本质还是报数为m-1的倍数的人死.求最后一个活着的人是初始时候的哪个人 /* 报数(A) 实际人员编号(B) ...
- OpenCV 4.1 编译和配置
OpenCV 4.0 版本,历时3年半,终于在2018年圣诞节前发布了,该版本增加的新功能如下: 1) 更新代码支持 c++11 特性,需要兼容 c++11 语法的编译器 2)增加 dnn 中的模块功 ...
- Google 收购 Android 十周年 全面解读Android现状
--訪传智播客Android学科教学总监传智·平一指 Android以前是一家创立于旧金山的公司的名字,该公司于2005年8月份被Google收购,并从此踏上了飞速发展的道路.经过十年的发展,它已经发 ...
- 【VBA编程】07.循环结构语句
[FOR...NEXT语句] For counter = start To End [Step step] [statements] [Exit For] [statements] Next [cou ...
- 【VBA编程】01.第一个VBA程序Hello world
[程序1] 所有程序语言的开始都源于Hello world,那么我们也使用Hello world进行第一个VBA编程 新建Excle文件-----文件-------选项-----自定义功能区域---- ...
- spring中的AOP 以及各种通知 配置
理解了前面动态代理对象的原理之后,其实还是有很多不足之处,因为如果在项目中有20多个类,每个类有100多个方法都需要判断是不是要开事务,那么方法调用那里会相当麻烦. spring中的AOP很好地解决了 ...