diff --git a/platform/Apple/source/CMakeLists.txt b/platform/Apple/source/CMakeLists.txt index 2e2bb9c6d..9df33e710 100644 --- a/platform/Apple/source/CMakeLists.txt +++ b/platform/Apple/source/CMakeLists.txt @@ -48,6 +48,12 @@ link_directories(${SRC_LIBRARIES_DIR}) set(SOURCE_LIST CicadaPlayer.mm CicadaPlayer.h + CicadaSyncPlayerServer.h + CicadaSyncPlayerServer.mm + CicadaSyncPlayerClient.h + CicadaSyncPlayerClient.mm + playerMessage.h + playerMessage.cpp CicadaPlayerGlobalSettings.h CicadaPlayerGlobalSettings.mm CicadaPlayerSDK.h @@ -85,6 +91,8 @@ target_include_directories(CicadaPlayerSDK PUBLIC ${PROJECT_SOURCE_DIR}) set_source_files_properties(${XIB_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set(PUBLIC_HEADERS CicadaPlayer.h + CicadaSyncPlayerServer.h + CicadaSyncPlayerClient.h CicadaPlayerGlobalSettings.h CicadaConfig.h CicadaCacheConfig.h diff --git a/platform/Apple/source/CicadaOCHelper.h b/platform/Apple/source/CicadaOCHelper.h index 2936af759..5d01617de 100644 --- a/platform/Apple/source/CicadaOCHelper.h +++ b/platform/Apple/source/CicadaOCHelper.h @@ -10,11 +10,16 @@ class CicadaOCHelper { CicadaOCHelper(CicadaPlayer * player):mPlayer(player){} void getListener(playerListener &listener); + void setDelegate(__weak id innerDelegate) { + mInnerDelegate = innerDelegate; + } + static CicadaTrackInfo* getCicadaTrackInfo(const StreamInfo *info); static CicadaImage * convertBitmapRGBA8ToUIImage(unsigned char *buffer, int width, int height); protected: static CicadaPlayer * getOCPlayer(void *userData); + static id getDelegate(void *userData); static void onError(int64_t code, const void *msg, /*void *extra, */void *userData); @@ -70,6 +75,7 @@ class CicadaOCHelper { private: __weak CicadaPlayer * mPlayer = nullptr; + __weak id mInnerDelegate = nil; }; diff --git a/platform/Apple/source/CicadaOCHelper.mm b/platform/Apple/source/CicadaOCHelper.mm index 01ca0d02a..74bc483a9 100644 --- a/platform/Apple/source/CicadaOCHelper.mm +++ b/platform/Apple/source/CicadaOCHelper.mm @@ -173,10 +173,25 @@ return helper->mPlayer; } +id CicadaOCHelper::getDelegate(void *userData) +{ + CicadaOCHelper *helper = (CicadaOCHelper *)userData; + if (nullptr == helper) { + return nil; + } + return helper->mInnerDelegate; +} + void CicadaOCHelper::onPrepared(void *userData) { __weak CicadaPlayer * player = getOCPlayer(userData); + __weak id theDelegate = getDelegate(userData); + dispatch_async(dispatch_get_main_queue(), ^{ player.duration = 0; // setDuration will overwrite the value + if (theDelegate && [theDelegate respondsToSelector:@selector(onPlayerEvent:eventType:)]) { + [theDelegate onPlayerEvent:player eventType:CicadaEventPrepareDone]; + } + if (player.delegate && [player.delegate respondsToSelector:@selector(onPlayerEvent:eventType:)]){ [player.delegate onPlayerEvent:player eventType:CicadaEventPrepareDone]; } @@ -238,11 +253,15 @@ void CicadaOCHelper::onCurrentPositionUpdate(int64_t position, void *userData) { __weak CicadaPlayer * player = getOCPlayer(userData); + __weak id theDelegate = getDelegate(userData); dispatch_async(dispatch_get_main_queue(), ^{ [player setCurrentPosition:position]; if (player.delegate && [player.delegate respondsToSelector:@selector(onCurrentPositionUpdate:position:)]) { [player.delegate onCurrentPositionUpdate:player position:position]; } + if (theDelegate && [theDelegate respondsToSelector:@selector(onCurrentPositionUpdate:position:)]) { + [theDelegate onCurrentPositionUpdate:player position:position]; + } }); } @@ -410,11 +429,16 @@ void CicadaOCHelper::onPlayerStatusChanged(int64_t oldStatus, int64_t newStatus, void *userData) { __weak CicadaPlayer * player = getOCPlayer(userData); - if (player && player.delegate && [player.delegate respondsToSelector:@selector(onPlayerStatusChanged:oldStatus:newStatus:)]) { - dispatch_async(dispatch_get_main_queue(), ^{ + __weak id theDelegate = getDelegate(userData); + dispatch_async(dispatch_get_main_queue(), ^{ + if (theDelegate && [theDelegate respondsToSelector:@selector(onPlayerStatusChanged:oldStatus:newStatus:)]) { + [theDelegate onPlayerStatusChanged:player oldStatus:mapStatus(oldStatus) newStatus:mapStatus(newStatus)]; + } + + if (player && player.delegate && [player.delegate respondsToSelector:@selector(onPlayerStatusChanged:oldStatus:newStatus:)]) { [player.delegate onPlayerStatusChanged:player oldStatus:mapStatus(oldStatus) newStatus:mapStatus(newStatus)]; - }); - } + } + }); } void CicadaOCHelper::onEvent(int64_t code, const void *msg, void *userData) { diff --git a/platform/Apple/source/CicadaPlayer.h b/platform/Apple/source/CicadaPlayer.h index cf316e868..51e1a5d13 100644 --- a/platform/Apple/source/CicadaPlayer.h +++ b/platform/Apple/source/CicadaPlayer.h @@ -504,6 +504,8 @@ OBJC_EXPORT */ @property (nonatomic, weak) id delegate; +- (void)setInnerDelegate:(id) delegate; + /** @brief 设置AudioSession的Delegate @param delegate Delegate对象 diff --git a/platform/Apple/source/CicadaPlayer.mm b/platform/Apple/source/CicadaPlayer.mm index e98bcb534..369bd326d 100644 --- a/platform/Apple/source/CicadaPlayer.mm +++ b/platform/Apple/source/CicadaPlayer.mm @@ -417,6 +417,11 @@ - (void)stop [self resetProperty]; } +- (void)setInnerDelegate:(id) delegate +{ + mHelper->setDelegate(delegate); +} + -(void)destroy { if (mView) { @@ -870,7 +875,7 @@ -(void) setPlaybackType:(CicadaPlaybackType)type -(int64_t) getPlayingPts { if (self.player) { - self.player->GetMasterClockPts(); + return self.player->GetMasterClockPts(); } return 0; } diff --git a/platform/Apple/source/CicadaPlayerSDK.h b/platform/Apple/source/CicadaPlayerSDK.h index 9aacdc4ce..85a404256 100644 --- a/platform/Apple/source/CicadaPlayerSDK.h +++ b/platform/Apple/source/CicadaPlayerSDK.h @@ -22,3 +22,5 @@ #import #import #import +#import +#import diff --git a/platform/Apple/source/CicadaSyncPlayerClient.h b/platform/Apple/source/CicadaSyncPlayerClient.h new file mode 100644 index 000000000..87a7dd7a1 --- /dev/null +++ b/platform/Apple/source/CicadaSyncPlayerClient.h @@ -0,0 +1,10 @@ + +#import +#import "CicadaPlayer.h" + +@interface CicadaSyncPlayerClient : CicadaPlayer + +- (instancetype)init; + +@end + diff --git a/platform/Apple/source/CicadaSyncPlayerClient.mm b/platform/Apple/source/CicadaSyncPlayerClient.mm new file mode 100644 index 000000000..51f1b8f19 --- /dev/null +++ b/platform/Apple/source/CicadaSyncPlayerClient.mm @@ -0,0 +1,106 @@ + +#import "CicadaSyncPlayerClient.h" +#include +#include "playerMessage.h" +#include +#include + +using namespace Cicada; +using namespace std; +class syncServerListener; + +@interface CicadaSyncPlayerClient() +{ + af_clock masterClock; +} + +@property (nonatomic, assign)bool isConnet; +@property (nonatomic, assign)messageClient *client; +@property (nonatomic, strong)NSTimer *mTimer; +@end + +static string serverIp = "tcp://localhost:8888"; + +@implementation CicadaSyncPlayerClient + +- (void)timerAction +{ + if (!self.isConnet) { + self.isConnet = self.client->connect(serverIp) >= 0; + } else { + string msg = self.client->readMessage(); + if (!msg.empty()) { + AF_LOGE("client msg %s\n", msg.c_str()); + if (msg == playerMessage::start) { + [self start]; + masterClock.start(); + } else if (msg == playerMessage::pause) { + [self pause]; + masterClock.pause(); + } else if (msg == playerMessage::prepare) { + [self prepare]; + } else if (msg == playerMessage::clock) { + int64_t pts = atoll(self.client->readMessage().c_str()); + masterClock.set(pts); + if (llabs(pts - [self getPlayingPts]) > 40000) { + AF_LOGW("delta pts is %lld\n", pts - [self getPlayingPts]); + } + } else if (msg == playerMessage::seekAccurate) { + int64_t ms = atoll(self.client->readMessage().c_str()); + [self seekToTime:ms seekMode:(CICADA_SEEKMODE_ACCURATE)]; + } else if (msg == playerMessage::seek) { + int64_t ms = atoll(self.client->readMessage().c_str()); + [self seekToTime:ms seekMode:(CICADA_SEEKMODE_INACCURATE)]; + } + } + } +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.autoPlay = YES; + [super setInnerDelegate:self]; + + [self setPlaybackType:CicadaPlaybackTypeVideo]; + [self SetClockRefer:^int64_t{ + return masterClock.get(); + }]; + self.client = new messageClient(); + int ret = self.client->connect(serverIp); + if (ret >= 0) { + self.isConnet = true; + } else { + self.isConnet = false; + } + + self.mTimer = [NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(timerAction) userInfo:nil repeats:YES]; + [[NSRunLoop mainRunLoop] addTimer:self.mTimer forMode:NSDefaultRunLoopMode]; + [self.mTimer fire]; + } + + return self; +} + +#pragma mark CicadaDelegate +-(void)onPlayerEvent:(CicadaPlayer*)player eventWithString:(CicadaEventWithString)eventWithString description:(NSString *)description { + switch (eventWithString) { + case CICADA_EVENT_PLAYER_NETWORK_RETRY: + [player reload]; + break; + default: + break; + } +} + +-(void)destroy +{ + if (nil != self.mTimer) { + [self.mTimer invalidate]; + self.mTimer = nil; + } + [super destroy]; +} + +@end diff --git a/platform/Apple/source/CicadaSyncPlayerServer.h b/platform/Apple/source/CicadaSyncPlayerServer.h new file mode 100644 index 000000000..c37e44abb --- /dev/null +++ b/platform/Apple/source/CicadaSyncPlayerServer.h @@ -0,0 +1,10 @@ + +#import +#import "CicadaPlayer.h" + +@interface CicadaSyncPlayerServer : CicadaPlayer + +- (instancetype)init; + +@end + diff --git a/platform/Apple/source/CicadaSyncPlayerServer.mm b/platform/Apple/source/CicadaSyncPlayerServer.mm new file mode 100644 index 000000000..147c47877 --- /dev/null +++ b/platform/Apple/source/CicadaSyncPlayerServer.mm @@ -0,0 +1,135 @@ + +#import "CicadaSyncPlayerServer.h" +#include +#include "playerMessage.h" + +using namespace Cicada; +using namespace std; +class syncServerListener; + +@interface CicadaSyncPlayerServer() + +@property (nonatomic, assign)CicadaStatus playerStatus; +@property (nonatomic, assign)BOOL isLoading; +@property (nonatomic, assign)BOOL isPrepare; +@property (nonatomic, assign)syncServerListener *syncListener; +@property (nonatomic, assign)messageServer *server; + +@end + +static NSString *serverIp = @"tcp://localhost:8888"; +class syncServerListener : public IProtocolServer::Listener { +public: + explicit syncServerListener(CicadaSyncPlayerServer *player) : mPlayer(player) + {} + + void onAccept(IProtocolServer::IClient **client) override + { + if (mPlayer.playerStatus >= CicadaStatusPrepared) { + Cicada::messageServer::write(playerMessage::seekAccurate, *client); + Cicada::messageServer::write(to_string([mPlayer currentPosition]), *client); + Cicada::messageServer::write(playerMessage::prepare, *client); + } + + if (mPlayer.playerStatus == CicadaStatusStarted && !mPlayer.isLoading) { + Cicada::messageServer::write(playerMessage::start, *client); + } + } + +private: + CicadaSyncPlayerServer *mPlayer = nil; +}; + +@implementation CicadaSyncPlayerServer + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.isPrepare = NO; + self.autoPlay = YES; + [super setInnerDelegate:self]; + + self.syncListener = new syncServerListener(self); + self.server = new messageServer(self.syncListener); + self.server->init(); + } + + return self; +} + + +- (void)prepare +{ + [super prepare]; + self.isPrepare = YES; +} + +- (void)stop +{ + [super stop]; + self.isPrepare = NO; +} + +#pragma mark CicadaDelegate + +-(void)onPlayerEvent:(CicadaPlayer*)player eventType:(CicadaEventType)eventType { + switch (eventType) { + case CicadaEventLoadingStart: + self.isLoading = YES; + if (self.server) { + self.server->write(playerMessage::pause); + } + break; + case CicadaEventLoadingEnd: + self.isLoading = NO; + if (self.server) { + self.server->write(playerMessage::start); + } + break; + default: + break; + } +} + +-(void)onPlayerEvent:(CicadaPlayer*)player eventWithString:(CicadaEventWithString)eventWithString description:(NSString *)description { + switch (eventWithString) { + case CICADA_EVENT_PLAYER_NETWORK_RETRY: + [player reload]; + break; + default: + break; + } +} + +- (void)onPlayerStatusChanged:(CicadaPlayer*)player oldStatus:(CicadaStatus)oldStatus newStatus:(CicadaStatus)newStatus { + self.playerStatus = newStatus; + switch (newStatus) { + case CicadaStatusStarted: + if (self.server) { + self.server->write(playerMessage::start); + } + break; + case CicadaStatusPaused: + if (self.server) { + self.server->write(playerMessage::pause); + } + break; + default: + break; + } +} + +- (void)onCurrentPositionUpdate:(CicadaPlayer*)player position:(int64_t)position { + if (self.server) { + self.server->write(playerMessage::clock); + self.server->write(to_string([self getPlayingPts])); + } +} + +-(void)destroy +{ + [super destroy]; +} + +@end diff --git a/platform/Apple/source/config.cmake b/platform/Apple/source/config.cmake index 39fffa91d..dbd770889 100644 --- a/platform/Apple/source/config.cmake +++ b/platform/Apple/source/config.cmake @@ -38,6 +38,7 @@ set(ALI_SRC_LIBRARIES videodec muxer render + communication z iconv resolv diff --git a/platform/Apple/source/playerMessage.cpp b/platform/Apple/source/playerMessage.cpp new file mode 100644 index 000000000..1a522cfa3 --- /dev/null +++ b/platform/Apple/source/playerMessage.cpp @@ -0,0 +1,14 @@ +// +// Created by moqi on 2020/4/23. +// + +#include "playerMessage.h" +using namespace Cicada; +const std::string playerMessage::prepare = "prepare"; +const std::string playerMessage::pause = "pause"; +const std::string playerMessage::start = "start"; +const std::string playerMessage::exit = "exit"; +const std::string playerMessage::fullScreen = "fullScreen"; +const std::string playerMessage::clock = "clock"; +const std::string playerMessage::seek = "seek"; +const std::string playerMessage::seekAccurate = "seekAccurate"; diff --git a/platform/Apple/source/playerMessage.h b/platform/Apple/source/playerMessage.h new file mode 100644 index 000000000..bffc72358 --- /dev/null +++ b/platform/Apple/source/playerMessage.h @@ -0,0 +1,23 @@ +// +// Created by moqi on 2020/4/23. +// + +#ifndef CICADAMEDIA_PLAYERMESSAGE_H +#define CICADAMEDIA_PLAYERMESSAGE_H +#include +namespace Cicada { + class playerMessage { + public: + const static std::string prepare; + const static std::string start; + const static std::string pause; + const static std::string exit; + const static std::string fullScreen; + const static std::string clock; + const static std::string seek; + const static std::string seekAccurate; + }; +}// namespace Cicada + + +#endif//CICADAMEDIA_PLAYERMESSAGE_H