一对一直播app源码开发,推流技术的实现

在说一对一直播app源码推流技术之前,先说一对一直播app源码推流的过程:采集——>前处理——>编码-—>推流———>流分发———>播放。
1.采集:音视频采集 pc段屏幕摄像头采集 iOS和安卓端的摄像头和屏幕采集
2.前处理:主要包括美颜,模糊效果,水印。iOS端一般会用到GPUImage处理图像,安卓端一般使用Google的grafika(图形处理库)
3.编码:不经过编码的视频体积会比较庞大。一对一直播app源码中的音视频必须经过压缩编辑才能进行存储和传输。
编码方式:硬编码(通过非CPU,如显卡GPU)和软编码(使用CPU),最好使用硬编码。
编码标准:视频:H.265 H.264 VP8 VP9 音频:AAC Opus
4.传输:从推流端到服务器。 常见的传输协议:RTMP RTSP HLS
5.流分发:在服务器端做一些处理。比如鉴黄 转码成不同的格式支持不同协议,以适应各个平台
6.播放 (解协议—解封装—音视频解码—音视频同步—音视频播放)
解协议:取出网络传输过程中一些无用的信息
解封装:获取到的是音频和视频在一起的封装文件

==========================================================
接下来步入正题,这里一对一直播app源码推流技术的实现我们主要基于bilibili的ijkPlayer第三方开源框架,这个开源框架已经帮我们集成好了FFmpeg。苹果的播放器也是基于FFMpeg实现的,但是不能播放直播类视频。这个第三方开源框架十分强大,安卓移动端的直播很多也是基于这个框架。

==========================================================
先来看一下ijkPlayer的运行效果,在这之前我们要做一些准备操作,才能看到官方提供的demo的运行效果。
1.Github上搜索ijkPlayer,并下载下来。
2.打开终端,输入命令: cd 第一步下载的ijkplayer这个文件名
3./init-ios.sh (下载ffmpeg的过程,可能很漫长)
4.第三步骤执行完成后, cd ios
5./compile-ffmpeg.sh clean (编译过程,可能比较耗时间)
6./compile-ffmpeg.sh all (编译过程,可能比较耗时间)
执行以上步骤后,就可以运行ijkplayer —> ios —>IJKMediaDemo这个Demo了。

==========================================================
已经运行了demo,看了项目,接下来看看如何集成到一对一直播app源码开发。
主要有两种方法:
1.工程中集成工程 。
2.把ijkplayer源码打包成framework,然后将这个framework集成到我们的项目中。
第一种方法相对而言比较麻烦,所以这里我们采用生成framework的方式。
生成framework的步骤如下:
1.打开ijkplayer —> ios 目录下的IJKMediaPlayer这个工程。
2.调成release版本。(默认是debug版本),操作步骤见下图。
3.然后分别选择模拟器和Generic iOS Device,分别编译一下。
4.合并模拟器和Generic iOS Device编译生成的framework文件。注意:这里合并的内容并不是Bundle文件,而是Bundle文件下的IJKMediaFramework。如果是上线的话,我们可以不合并framework文件,而是直接使用Generic iOS Device生成的framework,这样可以减小项目资源文件大小。之所以执行合并这个步骤,是因为在实际开发中,不仅仅要在真机上测试,还要在模拟器上运行。
5.将上面生成的IJKMediaFramework.framework包文件,直接拉到工程的文件中,然后导入如下依赖框架:
AudioToolbox.framework、AVFoundation.framework、CoreGraphics.framework、CoreMedia.framework、CoreVideo.framework、libbz2.tbd、libz.tbd、MediaPlayer.framework、MobileCoreServices.framework、OpenGLES.framework、QuartzCore.framework、UIKit.framework、VideoToolbox.framework。此时编译一下,应该接没有问题了。

==========================================================
接下来就可以开始一对一直播app源码推流技术代码实现部分了,注释会在代码中说明。以下代码是我之前写过的代码,所以在这里就说明一些事项。创建一个PlayerViewController类,在.h文件中添加一个属性@property (nonatomic, strong) Live * live;其中Live是上一界面传入的一个模型对象,live.streamAddr属性是播放的地址,live.creator.portrait是毛玻璃特效显示模糊处理的图片地址。另外以下代码中有些代码会和界面布局有关系,有刚进入播放界面的毛玻璃特效。另外,还添加了一个子控制器,并将子控制器的view显示到PlayerViewController控制器的view上。这个控制器主要是起到控制面板的作用,类似直播界面中的一些发送小礼物,聊天等控能,都在这个控制器面板中实现。

#import "PlayerViewController.h"
//导入头文件
#import <IJKMediaFramework/IJKMediaFramework.h>
#import "AppDelegate.h"
#import "SXTLiveChatViewController.h"

@interface PlayerViewController ()
//注意这里是atomic**************************
@property(atomic, retain) id<IJKMediaPlayback> player;

@property(nonatomic,strong)UIImageView *blurImageView;
@property (nonatomic, strong) UIButton * closeBtn;
//添加直播控制面板
@property (nonatomic, strong) SXTLiveChatViewController * liveChatVC;

@end

@implementation SXTPlayerViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self initPlayer];
    [self initUI];
    [self addChildVC];
}

- (void)initUI{
    //这和毛玻璃效果有关系
    self.view.backgroundColor = [UIColor blackColor];
    self.blurImageView = [[UIImageView alloc]initWithFrame:self.view.bounds];
    [self.blurImageView downloadImage:[NSString stringWithFormat:@"%@%@",IMAGE_HOST,self.live.creator.portrait] placeholder:@"default_room"];
    [self.view addSubview:self.blurImageView];
    //  创建需要的毛玻璃特效类型
    UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
    // 创建毛玻璃view 视图
    UIVisualEffectView *effectView = [[UIVisualEffectView alloc]initWithEffect:blurEffect];
    effectView.frame = self.blurImageView.bounds;
    //添加到要有毛玻璃特效的控件中
    [self.blurImageView addSubview:effectView];
    //[self.view addSubview:self.closeBtn];
}

- (void)initPlayer {
    IJKFFOptions *options = [IJKFFOptions optionsByDefault];
    self.player = [[IJKFFMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:self.live.streamAddr] withOptions:options];
    self.player.view.frame = self.view.bounds;
    //设置自动播放
    self.player.shouldAutoplay = YES;
    
    /******************************************/
    //因为本视图控制器添加了视图,为了退出按钮可以点击,应该添加到window上
    //添加player的view到self.view上
    [self.view addSubview:self.player.view];
    
}
//添加控制面板
- (void)addChildVC{
    //添加子控制器
    [self addChildViewController:self.liveChatVC];
    //添加子控制器视图
    [self.view addSubview:self.liveChatVC.view];
    [self.liveChatVC.view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.view);
    }];
    self.liveChatVC.live = self.live;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.navigationController.navigationBarHidden = YES;
    //注册直播需要的通知
    [self installMovieNotificationObservers];
    //准备播放
     [self.player prepareToPlay];
    
    /******************************************/
    //因为本视图控制器添加了视图,为了退出按钮可以点击,应该添加到window上。但是要注意:视图离开时要移除退出按钮
    UIWindow * window = [(AppDelegate *)[UIApplication sharedApplication].delegate window];
    [window addSubview:self.closeBtn];
}

- (UIButton *)closeBtn {
    if (!_closeBtn) {
        UIImage * image = [UIImage imageNamed:@"mg_room_btn_guan_h"];
        _closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_closeBtn setImage:image forState:UIControlStateNormal];
        _closeBtn.frame = CGRectMake(SCREEN_WIDTH - image.size.width - 10, SCREEN_HEIGHT - image.size.height - 10, image.size.width, image.size.height);
        [_closeBtn addTarget:self action:@selector(closeLive:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _closeBtn;
}
- (void)closeLive:(UIButton *)button {
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
     self.navigationController.navigationBarHidden = NO;
    //关闭直播
    [self.player shutdown];
    //移除直播通知
    [self removeMovieNotificationObservers];
    /******************************************/
    //因为本视图控制器添加了视图,为了退出按钮可以点击,应该添加到window上。但是要注意:视图离开时要移除退出按钮
    [self.closeBtn removeFromSuperview];
}


#pragma mark -通知要实现的四个方法
- (void)loadStateDidChange:(NSNotification*)notification
{
    //    MPMovieLoadStateUnknown        = 0,  未知
    //    MPMovieLoadStatePlayable       = 1 << 0, 缓冲结束可以播放
    //    MPMovieLoadStatePlaythroughOK  = 1 << 1, // Playback will be automatically started in this state when shouldAutoplay is YES 缓冲结束自动播放
    //    MPMovieLoadStateStalled        = 1 << 2, // Playback will be automatically paused in this state, if started  暂停
    
    IJKMPMovieLoadState loadState = _player.loadState;
    
    if ((loadState & IJKMPMovieLoadStatePlaythroughOK) != 0) {
        NSLog(@"loadStateDidChange: IJKMPMovieLoadStatePlaythroughOK: %d\n", (int)loadState);
    } else if ((loadState & IJKMPMovieLoadStateStalled) != 0) {
        NSLog(@"loadStateDidChange: IJKMPMovieLoadStateStalled: %d\n", (int)loadState);
    } else {
        NSLog(@"loadStateDidChange: ???: %d\n", (int)loadState);
    }
}

- (void)moviePlayBackDidFinish:(NSNotification*)notification
{
    //    MPMovieFinishReasonPlaybackEnded,  直播结束
    //    MPMovieFinishReasonPlaybackError,  直播错误
    //    MPMovieFinishReasonUserExited   用户退出
    int reason = [[[notification userInfo] valueForKey:IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] intValue];
    
    switch (reason)
    {
            case IJKMPMovieFinishReasonPlaybackEnded:
            NSLog(@"playbackStateDidChange: IJKMPMovieFinishReasonPlaybackEnded: %d\n", reason);
            break;
            
            case IJKMPMovieFinishReasonUserExited:
            NSLog(@"playbackStateDidChange: IJKMPMovieFinishReasonUserExited: %d\n", reason);
            break;
            
            case IJKMPMovieFinishReasonPlaybackError:
            NSLog(@"playbackStateDidChange: IJKMPMovieFinishReasonPlaybackError: %d\n", reason);
            break;
            
        default:
            NSLog(@"playbackPlayBackDidFinish: ???: %d\n", reason);
            break;
    }
}

- (void)mediaIsPreparedToPlayDidChange:(NSNotification*)notification
{
    NSLog(@"mediaIsPreparedToPlayDidChange\n");
}

- (void)moviePlayBackStateDidChange:(NSNotification*)notification
{
    
    //    MPMoviePlaybackStateStopped,  停止
    //    MPMoviePlaybackStatePlaying, 播放
    //    MPMoviePlaybackStatePaused, 暂停
    //    MPMoviePlaybackStateInterrupted,
    //    MPMoviePlaybackStateSeekingForward, 前进
    //    MPMoviePlaybackStateSeekingBackward 后退
    
    switch (_player.playbackState)
    {
            case IJKMPMoviePlaybackStateStopped: {
                NSLog(@"IJKMPMoviePlayBackStateDidChange %d: stoped", (int)_player.playbackState);
                break;
            }
            case IJKMPMoviePlaybackStatePlaying: {
                NSLog(@"IJKMPMoviePlayBackStateDidChange %d: playing", (int)_player.playbackState);
                break;
            }
            case IJKMPMoviePlaybackStatePaused: {
                NSLog(@"IJKMPMoviePlayBackStateDidChange %d: paused", (int)_player.playbackState);
                break;
            }
            case IJKMPMoviePlaybackStateInterrupted: {
                NSLog(@"IJKMPMoviePlayBackStateDidChange %d: interrupted", (int)_player.playbackState);
                break;
            }
            case IJKMPMoviePlaybackStateSeekingForward:
            case IJKMPMoviePlaybackStateSeekingBackward: {
                NSLog(@"IJKMPMoviePlayBackStateDidChange %d: seeking", (int)_player.playbackState);
                break;
            }
        default: {
            NSLog(@"IJKMPMoviePlayBackStateDidChange %d: unknown", (int)_player.playbackState);
            break;
        }
    }
    /*****************************************/
    //只要有变化就移除掉毛玻璃效果
    //开始播放直播的时候要移除毛玻璃效果
    self.blurImageView.hidden = YES;
    [self.blurImageView removeFromSuperview];
}


#pragma mark Install Movie Notifications

/* Register observers for the various movie object notifications. */
-(void)installMovieNotificationObservers
{
    //监听网络环境,监听缓冲方法
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(loadStateDidChange:)
                                                 name:IJKMPMoviePlayerLoadStateDidChangeNotification
                                               object:_player];
    //监听直播完成回调
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(moviePlayBackDidFinish:)
                                                 name:IJKMPMoviePlayerPlaybackDidFinishNotification
                                               object:_player];
    //
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(mediaIsPreparedToPlayDidChange:)
                                                 name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification
                                               object:_player];
    //监听用户的主动操作
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(moviePlayBackStateDidChange:)
                                                 name:IJKMPMoviePlayerPlaybackStateDidChangeNotification
                                               object:_player];
}

#pragma mark Remove Movie Notification Handlers

/* Remove the movie notification observers from the movie object. */
-(void)removeMovieNotificationObservers
{
    [[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerLoadStateDidChangeNotification object:_player];
    [[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerPlaybackDidFinishNotification object:_player];
    [[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification object:_player];
    [[NSNotificationCenter defaultCenter]removeObserver:self name:IJKMPMoviePlayerPlaybackStateDidChangeNotification object:_player];
}


- (SXTLiveChatViewController *)liveChatVC {
    if (!_liveChatVC) {
        _liveChatVC = [[SXTLiveChatViewController alloc] init];
    }
    return _liveChatVC;
}

@end

以上就是一对一直播app源码开发,推流技术的实现的全部内容了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧农业是一种结合了现代信息技术,包括物联网、大数据、云计算等,对农业生产过程进行智能化管理和监控的新模式。它通过各种传感器和设备采集农业生产中的关键数据,如大气、土壤和水质参数,以及生物生长状态等,实现远程诊断和精准调控。智慧农业的核心价值在于提高农业生产效率,保障食品安全,实现资源的可持续利用,并为农业产业的转型升级提供支持。 智慧农业的实现依赖于多个子系统,包括但不限于设施蔬菜精细化种植管理系统、农业技术资料库、数据采集系统、防伪防串货系统、食品安全与质量追溯系统、应急追溯系统、灾情疫情防控系统、农业工作管理系统、远程诊断系统、监控中心、环境监测系统、智能环境控制系统等。这些系统共同构成了一个综合的信息管理和服务平台,使得农业生产者能够基于数据做出更加科学的决策。 数据采集是智慧农业的基础。通过手工录入、传感器自动采集、移动端录入、条码/RFID扫描录入、拍照录入以及GPS和遥感技术等多种方式,智慧农业系统能够全面收集农业生产过程中的各种数据。这些数据不仅包括环境参数,还涵盖了生长状态、加工保存、检验检疫等环节,为农业生产提供了全面的数据支持。 智慧农业的应用前景广阔,它不仅能够提升农业生产的管理水平,还能够通过各种应用系统,如库房管理、无公害监控、物资管理、成本控制等,为农业生产者提供全面的服务。此外,智慧农业还能够支持政府监管,通过发病报告、投入品报告、死亡报告等,加强农业产品的安全管理和质量控制。 面对智慧农业的建设和发展,存在一些挑战,如投资成本高、生产过程标准化难度大、数据采集和监测的技术难题等。为了克服这些挑战,需要政府、企业和相关机构的共同努力,通过政策支持、技术创新和教育培训等手段,推动智慧农业的健康发展。智慧农业的建设需要明确建设目的,选择合适的系统模块,并制定合理的设备布署方案,以实现农业生产的智能化、精准化和高效化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值