投屏问题备忘

此处仅会提及遇到的具体问题及处理方式,相关SDK及控件具体的使用方式不做介绍。
背景需求:iOS端和TV端,播放、暂停、进度调节 互控且同步
有点乱,个人备忘

AirPlay

iOS下调取AirPlay Picker 的相关控件,关于这些苹果相关的介绍极少。
MPVolumeView

乐播投屏 【乐联、DLNA、公网】

乐播投屏SDK


【AirPlay问题】

  • AppleTV投屏后没有声音。
    原因:AVPlayer muted 被设置为YES

  • 投屏视频播放完,电视会自动断开,此时会存在两种令人费解的情况
    a. currentItem 释放
    b. currentItem 未被释放
    在此情形下,尝试调用play 方法无效。

    原因:后续追查相关机制
    解决方案:重置 AVPlayer及相关资源,注意相关观察者移除及释放。
    这里有个有意思的地方是,AVPlayer 一开始并未释放,只是处理了相关资源(item及观察者),在(播放到视频结尾处)重复播放的时候 前两次都没有问题,但是第三次开始 addPeriodicTimeObserverForInterval的block回调就不在执行了。
    最终处理方式是连同 AVPlayer 均销毁并重新生成实例,费解。

  • 视频播放完成后的诡异状态变更
    在使用iOS10.0 新增的 AVPlayerTimeControlStatus做播放暂停状态判断时,发现正常的播放 和 暂停 都是没有问题的。 但是在视频播放到结尾处时,观察者方法中 会获得如下状态变更:
    --> AVPlayerTimeControlStatusPaused --> AVPlayerTimeControlStatusPlaying
    这里疑惑,为什么播放结束后 还会有个播放中的状态回调?
    为了防止该状态对相关逻辑造成干扰,过滤了如下状态:

//注册结束播放通知 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(airPlayDidPlayEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.airPlayPlayer.currentItem];

#pragma mark - AirPlay
- (void)airPlayDidPlayEnd:(NSNotification *)notification{
    self.status = LBLelinkPlayStatusCommpleted;

//   以下为错误尝试    
//    [self.airPlayPlayer pause];
//    if (self.strUrlCache) {
//        //重置播放资源 AVplayer 播放完成后资源会被销毁
//        AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:self.strUrlCache]];
//        AVPlayerItem *item = [[AVPlayerItem alloc] initWithAsset:asset];
//        [self.airPlayPlayer replaceCurrentItemWithPlayerItem:item];
//    }


    //replaceCurrentItemWithPlayerItem: 特别注意!!! 该方法如在非主线程使用会引起崩溃
    //逻辑变更...
   ....
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    ...相关逻辑 略
    
    if (object == self.airPlayPlayer && [keyPath isEqualToString:@"timeControlStatus"]) {
        
        if (self.status == LBLelinkPlayStatusCommpleted) {
            //播放完成状态 过滤
            return;
        }

        if (@available(iOS 10.0, *)) {
            AVPlayerTimeControlStatus status = [[change objectForKey:NSKeyValueChangeNewKey]integerValue];
            if (status == AVPlayerTimeControlStatusPaused) {
                // do something
                SYLog(@"AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate");
                self.status = LBLelinkPlayStatusPause;
            }
            
            if (status == AVPlayerTimeControlStatusPlaying) {
                self.status = LBLelinkPlayStatusPlaying;
            }
            
        } else {
            // Fallback on earlier versions
            // ios10.0之后才能够监听到暂停后继续播放的状态,ios10.0之前监测不到这个状态
            //但是可以监听到开始播放的状态 AVPlayerStatus  status监听这个属性。
            SYLog(@"another ....");
        }
        
        ...相关逻辑 略
        return;
    }

 ...略
    
}
  • 播放进度记录的处理
    self.airPlayTimeObserve = [self.airPlayPlayer addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.25, 10) queue:nil usingBlock:^(CMTime time){
        @strongify(self)
        if(!self){
            return;
        }
        if(!self.isAirPlay){
            return;
        }
        if(self.airPlayPlayer.status != AVPlayerStatusReadyToPlay){
            return;
        }
        AVPlayerItem *currentItem = self.airPlayPlayer.currentItem;
        if(currentItem.duration.timescale != 0){
            NSInteger currentTime = (NSInteger)CMTimeGetSeconds([currentItem currentTime]);

            NSInteger duration  = (NSInteger)CMTimeGetSeconds([currentItem duration]);
            
            //这里相当于一个定时器,视频播放完成后,播放进度会一直累加。
            if (currentTime > duration) {
                currentTime = duration;
            }
            //状态过滤  播放完成及暂停状态下,跳过处理
            if (self.status == LBLelinkPlayStatusCommpleted ||self.status == LBLelinkPlayStatusPause) {
                return;
            }
            
            self.currentTime = currentTime;
            ...略
        }
    }];

【乐播相关问题】

  • 投屏设备搜索回调 过慢或无回调
//搜索设备
- (void)searchDevices:(searchBlock)searchBlock{
    self.searchBlock = searchBlock;
    
    //如果已有搜索设备则使用之前的搜索结果先行回调 (WIFI 处于链接情况下)
    if (self.arrLelinkServices.count > 0 && SparkReachabilityIsWIFI) {
        self.searchBlock(self.arrLelinkServices, nil);
    }
    //搜索状态标记,用于尝试重启搜索
    if (!self.isSearchStart) {
        self.isSearchStart = YES;
    }
    //启动搜索
    [self.lelinkBrowser searchForLelinkService];
}


//停止搜索
- (void)stopSearchDevices{
    [self.lelinkBrowser stop];
    self.isSearchStart = NO;
   //重置尝试次数
    self.intRetrySearchLimit = 0;
}

// 搜索到服务时,会调用此代理方法,将设备列表在此方法中回调出来
// 注意:如果不调用stop,则当有服务信息和状态更新以及新服务加入网络或服务退出网络时,会调用此代理将新的设备列表回调出来
- (void)lelinkBrowser:(LBLelinkBrowser *)browser didFindLelinkServices:(NSArray<LBLelinkService *> *)services {
    SYLog(@"搜索到设备数 %zd", services.count);
    //本地保存 设备数组
    self.arrLelinkServices = services;
    // 更新UI
    //    ...
    self.searchBlock(services, nil);
    
    //如果搜索设备为空 则手动重启搜索服务
    if (services.count == 0 && self.isSearchStart) {
        SYLog(@"%@",@"[Info]: 搜索服务,进行尝试模式...");
        //设置尝试次数 2次
        if (self.intRetrySearchLimit >= 2) {
            return;
        }
        self.intRetrySearchLimit += 1;
        //搜索 刷新
        [browser searchForLelinkService];
    }
    
}

  • 设备连接 多设备投屏连接时,防止回调错乱
- (void)connectDeviceLinkService:(LBLelinkService *)linkService connectBlock:(connectBlock)connectBlock{
    self.connectBlock = connectBlock;

    //服务连接 检查服务是否可用
    if (!linkService.isLelinkServiceAvailable) {
        NSLog(@"service name : %@",linkService.lelinkServiceName);
        NSString *strDomain = @"com.feng.car.ErrorDomain";
        NSString *desc = NSLocalizedString(@"lelinkServiceAvailable  state no...", @"");
        NSDictionary *userInfo = @{NSLocalizedDescriptionKey: desc};
        NSError *error = [NSError errorWithDomain:strDomain code:-20002 userInfo:userInfo];
        self.connectBlock(nil, NO, error);
        
        SYLog(@"%@",@"[Info]: 服务不可用。。。。");
        return;
    }
   
    //新增逻辑 如果播放器 存在播放资源或播放中,则停止播放
    if(self.lelinkPlayer.lelinkConnection.isConnected){
        [self.lelinkPlayer stop];
        [self.lelinkPlayer.lelinkConnection disConnect];
    }

    self.lelinkConnection.lelinkService = linkService;
    [self.lelinkConnection connect];
    
}
  • 播放状态回调 关联UI变更逻辑
#pragma mark - LBLelinkPlayerDelegate
// 播放错误代理回调,根据错误信息进行相关的处理
- (void)lelinkPlayer:(LBLelinkPlayer *)player onError:(NSError *)error {
    if (error) {
        SYLog(@"%@",error);
        self.castBlock(LBLelinkPlayStatusError, nil, error);
    }
}

// 播放状态代理回调
- (void)lelinkPlayer:(LBLelinkPlayer *)player playStatus:(LBLelinkPlayStatus)playStatus {

    //4G下 断开链接后 回调延迟... 过滤方法  【可能会触发云投屏功能】
    if (!self.linkService || !self.lelinkConnection.isConnected) {
        return;
    }
    
    SYLog(@"%lu",(unsigned long)playStatus);
    self.status = playStatus;
    self.castBlock(playStatus, nil, nil);
   ...略
}

// 播放进度信息回调
- (void)lelinkPlayer:(LBLelinkPlayer *)player progressInfo:(LBLelinkProgressInfo *)progressInfo {
   
    //4G下 断开链接后 回调延迟... 过滤方法
    if (!self.linkService || !self.lelinkConnection.isConnected) {
        return;
    }
    self.castBlock(self.status, progressInfo, nil);
    
    SYLog(@"current time = %ld, duration = %ld",(long)progressInfo.currentTime,(long)progressInfo.duration);
    
    //本地记录进度
    self.currentTime = progressInfo.currentTime;
    
   ... 略
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345