基于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 ...
随机推荐
- centOS6.5 Hadoop1.0.4安装
前段时间去培训,按照教程装了一遍Hadoop.回来又重新装一次,捋下思路,加深理解. 基本配置如下,三个节点,一个namenode,两个datanode. Namenode 192.168.59.14 ...
- SEO之巴莱多定律
SEO这个词在2007.2008年好像特别的火,尤其在2007年下半年至今,众多SEO爱好者发起了最大的一轮搜索引擎优化研究风暴,甚至最终逼迫百度等大型搜索引擎公司改变算法. SEO,确实能带来一些经 ...
- 理解Vue的状态管理模式Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 状态管理模式.集中式存储管理,一听就很高大 ...
- 简单的混淆ID
public class TestfuzzId { public static void main(String[] args) { int id = 123456; int p = id^Integ ...
- TreeView 类 事件
名称 说明 AfterCheck 在选中树节点复选框后发生. AfterCollapse 在折叠树节点后发生. AfterExpand 在展开树节点后发生. AfterLabelEdit 在编辑树节点 ...
- 修改ASPCMS升级扩展功能
修改 inc/aspcms_templateFun.asp 查找"content=decodeHtml(rsObj("Content"))" 替换为”conte ...
- 倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-如何声明定时器,使用定时器TON模块 TC3
TON功能块功能: 当输入为高电平时,计时器开始计时,CV表示计时器计时的当前值,而PV则是计时的目标值,当CV的值等于PV的值时,输出置1. 在主程序接下去的地方按下F2并添加TON功能块. ...
- 各种字符编码方式详解及由来(ANSI,UNICODE,UTF-8,GB2312,GBK)
一直对字符的各种编码方式懵懵懂懂,什么ANSI UNICODE UTF-8 GB2312 GBK DBCS UCS……是不是看的很晕,假如您细细的阅读本文你一定可以清晰的理解他们.Let's go! ...
- Windows开机出现提示“nwsvc.exe”错误怎么办
开机时出现错误提示: Microsoft Visual C++ Runtime Library Runtime Error! Program: C:\Windows\system32\nwsvc.ex ...
- C++11 可变参数模板构造string列表
#include <iostream> #include <cstdint> #include <list> #include <string> tem ...