基于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 ...
随机推荐
- all-oone-data-structure(好)
哈哈,我用了HashMap, 双向链表,还有了HashSet来保存key的集合. 现在这道题目还只有 9.3%的AC率,难度为HardTotal Accepted: 9 Total Submissio ...
- 【转载】【Todo】银弹与我们的职业
看到一段文字,不得不单独拎出来. 然后再借用一下g9老大的<银弹和我们的职业>中的话: 银弹和我们的职业发展有什么相干?很简单:我们得把时间用于学习解决本质困难.新技术给高手带来方便.菜鸟 ...
- CF(D. Fibonacci Sums)dp计数
题目链接:http://codeforces.com/contest/126/problem/D 题意:一个数能够有多种由互不同样的斐波那契数组成的情况: 解法:dp,easy证明:每一个数通过贪心能 ...
- windows 服务器不能使用剪贴板解决办法
您可以在系统资源中先结束rdpclip.exe进程,然后重新打开c:\windows\system32\rdpclip.exe即可
- 转:关于android webview实践的文章集合
http://blog.csdn.net/jiangqq781931404/article/category/2681765
- [HTML5] Add an SVG Image to a Webpage and Get a Reference to the Internal Elements in JavaScript
We want to show an SVG avatar of the patio11bot, so we'll do that in three ways: Using an img tag - ...
- 如何使用angularjs实现文本框设置值
<!DOCTYPE html> <html ng-app="myApp"> <head> <title>angularjs-setV ...
- Flutter常用布局组件
Flutter控件本身通常由许多小型.单用途的控件组成,结合起来产生强大的效果,例如,Container是一种常用的控件,由负责布局.绘画.定位和大小调整的几个控件组成,具体来说,Container是 ...
- django2自动发现项目中的url
根据路飞学城luffycity.com 的crm项目修改的 1 url入口:rbac/urls.py urlpatterns = [ ... # 批量操作权限 re_path(r'^multi/per ...
- Java 变长參数Varargs
Varargs (variable arguments)可变长參数是Java 1.5引入的特性. 方法的形參如print(String ... s),实參为随意数目的值. public class V ...