Commit ee79fdb6 authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Kateryna Kostiuk

video: add bridging, service, etc.

- builds the daemon with video support
- adds a bridging layer to expose daemon video functionality
- adds camera usage to the Info.plist and will request permission
  at app wizard screen(manual setting of camera permission is required
  if refused initially)
- sets hardware decoding to false
- sets video enabled to true for the current account

Change-Id: Id80fa8e439679dcccc08006d68af7d7b3350722d
Reviewed-by: Kateryna Kostiuk's avatarKateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
parent 7cd6e685
...@@ -238,6 +238,10 @@ ...@@ -238,6 +238,10 @@
62A88D371F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A88D361F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift */; }; 62A88D371F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A88D361F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift */; };
62A88D391F6C323500F8AB18 /* PresenceAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62A88D381F6C323500F8AB18 /* PresenceAdapter.mm */; }; 62A88D391F6C323500F8AB18 /* PresenceAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62A88D381F6C323500F8AB18 /* PresenceAdapter.mm */; };
62A88D3B1F6C3ACC00F8AB18 /* PresenceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A88D3A1F6C3ACC00F8AB18 /* PresenceService.swift */; }; 62A88D3B1F6C3ACC00F8AB18 /* PresenceService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A88D3A1F6C3ACC00F8AB18 /* PresenceService.swift */; };
62AA15B21FF422810064A063 /* src in Resources */ = {isa = PBXBuildFile; fileRef = 62AA15B11FF422810064A063 /* src */; };
62AA15BF1FFC36840064A063 /* VideoAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 62AA15BE1FFC36840064A063 /* VideoAdapter.mm */; };
62AA15C31FFC39C80064A063 /* VideoAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */; };
62AA15CA1FFD3D7E0064A063 /* VideoService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62AA15C91FFD3D7E0064A063 /* VideoService.swift */; };
62DFAB2C1F9FF030002D6F9C /* Reachability.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62DFAB2B1F9FF030002D6F9C /* Reachability.framework */; }; 62DFAB2C1F9FF030002D6F9C /* Reachability.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 62DFAB2B1F9FF030002D6F9C /* Reachability.framework */; };
62DFAB2E1F9FF0D0002D6F9C /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DFAB2D1F9FF0D0002D6F9C /* NetworkService.swift */; }; 62DFAB2E1F9FF0D0002D6F9C /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DFAB2D1F9FF0D0002D6F9C /* NetworkService.swift */; };
62E55B6D1F758E6F00D3FEF4 /* String+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E55B6C1F758E6F00D3FEF4 /* String+Helpers.swift */; }; 62E55B6D1F758E6F00D3FEF4 /* String+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E55B6C1F758E6F00D3FEF4 /* String+Helpers.swift */; };
...@@ -508,6 +512,11 @@ ...@@ -508,6 +512,11 @@
62A88D361F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenceAdapterDelegate.swift; sourceTree = "<group>"; }; 62A88D361F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenceAdapterDelegate.swift; sourceTree = "<group>"; };
62A88D381F6C323500F8AB18 /* PresenceAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PresenceAdapter.mm; sourceTree = "<group>"; }; 62A88D381F6C323500F8AB18 /* PresenceAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PresenceAdapter.mm; sourceTree = "<group>"; };
62A88D3A1F6C3ACC00F8AB18 /* PresenceService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenceService.swift; sourceTree = "<group>"; }; 62A88D3A1F6C3ACC00F8AB18 /* PresenceService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresenceService.swift; sourceTree = "<group>"; };
62AA15B11FF422810064A063 /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; name = src; path = ../../daemon/src; sourceTree = "<group>"; };
62AA15BD1FFC366D0064A063 /* VideoAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VideoAdapter.h; sourceTree = "<group>"; };
62AA15BE1FFC36840064A063 /* VideoAdapter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VideoAdapter.mm; sourceTree = "<group>"; };
62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoAdapterDelegate.swift; sourceTree = "<group>"; };
62AA15C91FFD3D7E0064A063 /* VideoService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoService.swift; sourceTree = "<group>"; };
62AD0BFB1FE037BF00BEA1F6 /* ru-RU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ru-RU"; path = "ru-RU.lproj/Localizable.strings"; sourceTree = "<group>"; }; 62AD0BFB1FE037BF00BEA1F6 /* ru-RU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ru-RU"; path = "ru-RU.lproj/Localizable.strings"; sourceTree = "<group>"; };
62AD0BFC1FE0384000BEA1F6 /* sq-AL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sq-AL"; path = "sq-AL.lproj/Localizable.strings"; sourceTree = "<group>"; }; 62AD0BFC1FE0384000BEA1F6 /* sq-AL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sq-AL"; path = "sq-AL.lproj/Localizable.strings"; sourceTree = "<group>"; };
62AD0BFF1FE0387F00BEA1F6 /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/Localizable.strings; sourceTree = "<group>"; }; 62AD0BFF1FE0387F00BEA1F6 /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/Localizable.strings; sourceTree = "<group>"; };
...@@ -721,6 +730,8 @@ ...@@ -721,6 +730,8 @@
0E48F9D21FDF150700D6CC08 /* ContactRequestManager.swift */, 0E48F9D21FDF150700D6CC08 /* ContactRequestManager.swift */,
0E49096B1FEAB225005CAA50 /* CallsAdapterDelegate.swift */, 0E49096B1FEAB225005CAA50 /* CallsAdapterDelegate.swift */,
0E49096D1FEAC0DE005CAA50 /* CallsService.swift */, 0E49096D1FEAC0DE005CAA50 /* CallsService.swift */,
62AA15C21FFC39C80064A063 /* VideoAdapterDelegate.swift */,
62AA15C91FFD3D7E0064A063 /* VideoService.swift */,
); );
path = Services; path = Services;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -756,6 +767,8 @@ ...@@ -756,6 +767,8 @@
62A88D381F6C323500F8AB18 /* PresenceAdapter.mm */, 62A88D381F6C323500F8AB18 /* PresenceAdapter.mm */,
0E4909681FEAB156005CAA50 /* CallsAdapter.h */, 0E4909681FEAB156005CAA50 /* CallsAdapter.h */,
0E4909691FEAB156005CAA50 /* CallsAdapter.mm */, 0E4909691FEAB156005CAA50 /* CallsAdapter.mm */,
62AA15BD1FFC366D0064A063 /* VideoAdapter.h */,
62AA15BE1FFC36840064A063 /* VideoAdapter.mm */,
); );
path = Bridging; path = Bridging;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1640,10 +1653,12 @@ ...@@ -1640,10 +1653,12 @@
1A5DC02E1F3565640075E8EF /* ConversationViewModel.swift in Sources */, 1A5DC02E1F3565640075E8EF /* ConversationViewModel.swift in Sources */,
1A2D189C1F264AD900B2C785 /* UIViewController+Ring.swift in Sources */, 1A2D189C1F264AD900B2C785 /* UIViewController+Ring.swift in Sources */,
02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */, 02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */,
62AA15BF1FFC36840064A063 /* VideoAdapter.mm in Sources */,
1A2D18C11F29180700B2C785 /* AccountConfigModel.swift in Sources */, 1A2D18C11F29180700B2C785 /* AccountConfigModel.swift in Sources */,
1A3D28A71F0EB9DB00B524EE /* Bool+String.swift in Sources */, 1A3D28A71F0EB9DB00B524EE /* Bool+String.swift in Sources */,
5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */, 5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */,
1ABE07D31F0D8FE800D36361 /* Storyboards.swift in Sources */, 1ABE07D31F0D8FE800D36361 /* Storyboards.swift in Sources */,
62AA15CA1FFD3D7E0064A063 /* VideoService.swift in Sources */,
0EDE34C71F868E1200FFA15C /* EditProfileViewController.swift in Sources */, 0EDE34C71F868E1200FFA15C /* EditProfileViewController.swift in Sources */,
62A88D3B1F6C3ACC00F8AB18 /* PresenceService.swift in Sources */, 62A88D3B1F6C3ACC00F8AB18 /* PresenceService.swift in Sources */,
0E0FF1B51FC3947B003898C2 /* DBManager.swift in Sources */, 0E0FF1B51FC3947B003898C2 /* DBManager.swift in Sources */,
...@@ -1677,6 +1692,7 @@ ...@@ -1677,6 +1692,7 @@
1A2D18FF1F29352D00B2C785 /* MeViewModel.swift in Sources */, 1A2D18FF1F29352D00B2C785 /* MeViewModel.swift in Sources */,
62A88D391F6C323500F8AB18 /* PresenceAdapter.mm in Sources */, 62A88D391F6C323500F8AB18 /* PresenceAdapter.mm in Sources */,
1A2D18B71F29164700B2C785 /* SmartlistViewModel.swift in Sources */, 1A2D18B71F29164700B2C785 /* SmartlistViewModel.swift in Sources */,
62AA15C31FFC39C80064A063 /* VideoAdapterDelegate.swift in Sources */,
04399AAE1D1C304300E99CD9 /* Utils.mm in Sources */, 04399AAE1D1C304300E99CD9 /* Utils.mm in Sources */,
56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */, 56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */,
0E0FF1AF1FC38CBC003898C2 /* ProfileDataHelper.swift in Sources */, 0E0FF1AF1FC38CBC003898C2 /* ProfileDataHelper.swift in Sources */,
......
...@@ -37,6 +37,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -37,6 +37,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private let contactsService = ContactsService(withContactsAdapter: ContactsAdapter()) private let contactsService = ContactsService(withContactsAdapter: ContactsAdapter())
private let presenceService = PresenceService(withPresenceAdapter: PresenceAdapter()) private let presenceService = PresenceService(withPresenceAdapter: PresenceAdapter())
private let callService = CallsService(withCallsAdapter: CallsAdapter()) private let callService = CallsService(withCallsAdapter: CallsAdapter())
private let videoService = VideoService(withVideoAdapter: VideoAdapter())
private let networkService = NetworkService() private let networkService = NetworkService()
private var conversationManager: ConversationsManager? private var conversationManager: ConversationsManager?
private var contactRequestManager: ContactRequestManager? private var contactRequestManager: ContactRequestManager?
...@@ -49,7 +50,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -49,7 +50,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
withContactsService: self.contactsService, withContactsService: self.contactsService,
withPresenceService: self.presenceService, withPresenceService: self.presenceService,
withNetworkService: self.networkService, withNetworkService: self.networkService,
withCallService: self.callService) withCallService: self.callService,
withVideoService: self.videoService)
}() }()
private lazy var appCoordinator: AppCoordinator = { private lazy var appCoordinator: AppCoordinator = {
return AppCoordinator(with: self.injectionBag) return AppCoordinator(with: self.injectionBag)
...@@ -74,6 +76,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -74,6 +76,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
SystemAdapter().registerConfigurationHandler() SystemAdapter().registerConfigurationHandler()
self.startDaemon() self.startDaemon()
// disables hardware decoding
self.videoService.setDecodingAccelerated(withState: false)
// requests permission to use the camera
// will enumerate and add devices once permission has been granted
self.videoService.setupInputs()
// start monitoring for network changes
self.networkService.monitorNetworkType() self.networkService.monitorNetworkType()
// themetize the app // themetize the app
...@@ -87,15 +97,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -87,15 +97,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
self.conversationManager = ConversationsManager(with: self.conversationsService, accountsService: self.accountService) self.conversationManager = ConversationsManager(with: self.conversationsService, accountsService: self.accountService)
self.startDB() self.startDB()
self.accountService.loadAccounts().subscribe { [unowned self] (_) in self.accountService.loadAccounts().subscribe { [unowned self] (_) in
if let currentAccount = self.accountService.currentAccount { guard let currentAccount = self.accountService.currentAccount else {
self.contactsService.loadContacts(withAccount: currentAccount) self.log.error("Can't get current account!")
self.contactsService.loadContactRequests(withAccount: currentAccount) return
self.presenceService.subscribeBuddies(withAccount: currentAccount, withContacts: self.contactsService.contacts.value) }
if let ringID = AccountModelHelper(withAccount: currentAccount).ringId { self.contactsService.loadContacts(withAccount: currentAccount)
self.conversationManager? self.contactsService.loadContactRequests(withAccount: currentAccount)
.prepareConversationsForAccount(accountId: currentAccount.id, accountUri: ringID) self.presenceService.subscribeBuddies(withAccount: currentAccount, withContacts: self.contactsService.contacts.value)
} if let ringID = AccountModelHelper(withAccount: currentAccount).ringId {
self.conversationManager?
.prepareConversationsForAccount(accountId: currentAccount.id, accountUri: ringID)
} }
// make sure video is enabled
let accountDetails = self.accountService.getAccountDetails(fromAccountId: currentAccount.id)
accountDetails.set(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.videoEnabled), withValue: "true")
self.accountService.setAccountDetails(forAccountId: currentAccount.id, withDetails: accountDetails)
}.disposed(by: self.disposeBag) }.disposed(by: self.disposeBag)
self.window?.rootViewController = self.appCoordinator.rootViewController self.window?.rootViewController = self.appCoordinator.rootViewController
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#import "Chameleon/Chameleon.h" #import "Chameleon/Chameleon.h"
#import "ContactsAdapter.h" #import "ContactsAdapter.h"
#import "PresenceAdapter.h" #import "PresenceAdapter.h"
#import "VideoAdapter.h"
#import <CommonCrypto/CommonCrypto.h> #import <CommonCrypto/CommonCrypto.h>
#import <Contacts/Contacts.h> #import <Contacts/Contacts.h>
#import "CallsAdapter.h" #import "CallsAdapter.h"
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import <Foundation/Foundation.h>
#import <AVFoundation/AVCaptureOutput.h>
@protocol VideoAdapterDelegate;
@interface VideoAdapter : NSObject
@property (class, nonatomic, weak) id <VideoAdapterDelegate> delegate;
- (void)addVideoDeviceWithName:(NSString*)deviceName withDevInfo:(NSDictionary*)deviceInfoDict;
- (void)registerSinkTargetWithSinkId:sinkId withWidth:(NSInteger)w withHeight:(NSInteger)h;
- (void)removeSinkTargetWithSinkId:(NSString*)sinkId;
- (void)writeOutgoingFrameWithImage:(UIImage*)image;
- (void)setDecodingAccelerated:(BOOL)state;
- (void)switchInput:(NSString*)deviceName;
@end
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "VideoAdapter.h"
#import "Utils.h"
#import "dring/videomanager_interface.h"
#import "Ring-Swift.h"
#include <pthread.h>
#include <functional>
#include <AVFoundation/AVFoundation.h>
#include <mutex>
using namespace DRing;
struct Renderer
{
std::mutex frameMutex;
std::condition_variable frameCv;
bool isRendering;
std::mutex renderMutex;
SinkTarget target;
SinkTarget::FrameBufferPtr daemonFramePtr_;
int width;
int height;
void bindSinkFunctions() {
target.pull = [this](std::size_t bytes) {
std::lock_guard<std::mutex> lk(renderMutex);
if (!daemonFramePtr_)
daemonFramePtr_.reset(new DRing::FrameBuffer);
daemonFramePtr_->storage.resize(bytes);
daemonFramePtr_->ptr = daemonFramePtr_->storage.data();
daemonFramePtr_->ptrSize = bytes;
return std::move(daemonFramePtr_);
};
target.push = [this](DRing::SinkTarget::FrameBufferPtr buf) {
std::lock_guard<std::mutex> lk(renderMutex);
daemonFramePtr_ = std::move(buf);
if(VideoAdapter.delegate) {
@autoreleasepool {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bitmapContext = CGBitmapContextCreate((void *)daemonFramePtr_->ptr,
daemonFramePtr_->width,
daemonFramePtr_->height,
8,
4 * width,
colorSpace,
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
CFRelease(colorSpace);
CGImageRef cgImage=CGBitmapContextCreateImage(bitmapContext);
CGContextRelease(bitmapContext);
UIImage* image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
isRendering = true;
[VideoAdapter.delegate writeFrameWithImage: image];
isRendering = false;
}
}
};
}
};
@implementation VideoAdapter {
std::map<std::string, std::shared_ptr<Renderer>> renderers;
}
// Static delegate that will receive the propagated daemon events
static id <VideoAdapterDelegate> _delegate;
#pragma mark Init
- (id)init {
if (self = [super init]) {
[self registerVideoHandlers];
}
return self;
}
#pragma mark -
#pragma mark Callbacks registration
- (void)registerVideoHandlers {
std::map<std::string, std::shared_ptr<CallbackWrapperBase>> videoHandlers;
videoHandlers.insert(exportable_callback<VideoSignal::DecodingStarted>([&](const std::string& renderer_id,
const std::string& shm_path,
int w,
int h,
bool is_mixer) {
if(VideoAdapter.delegate) {
NSString* rendererId = [NSString stringWithUTF8String:renderer_id.c_str()];;
[VideoAdapter.delegate decodingStartedWithRendererId:rendererId withWidth:(NSInteger)w withHeight:(NSInteger)h];
}
}));
videoHandlers.insert(exportable_callback<VideoSignal::DecodingStopped>([&](const std::string& renderer_id,
const std::string& shm_path,
bool is_mixer) {
if(VideoAdapter.delegate) {
NSString* rendererId = [NSString stringWithUTF8String:renderer_id.c_str()];
[VideoAdapter.delegate decodingStoppedWithRendererId:rendererId];
}
}));
videoHandlers.insert(exportable_callback<VideoSignal::StartCapture>([&](const std::string& device) {
if(VideoAdapter.delegate) {
NSString* deviceString = [NSString stringWithUTF8String:device.c_str()];
[VideoAdapter.delegate startCaptureWithDevice:deviceString];
}
}));
videoHandlers.insert(exportable_callback<VideoSignal::StopCapture>([&]() {
if(VideoAdapter.delegate) {
[VideoAdapter.delegate stopCapture];
}
}));
registerVideoHandlers(videoHandlers);
}
#pragma mark -
- (void)registerSinkTargetWithSinkId:sinkId withWidth:(NSInteger)w withHeight:(NSInteger)h {
auto _sinkId = std::string([sinkId UTF8String]);
auto renderer = std::make_shared<Renderer>();
renderer->width = static_cast<int>(w);
renderer->height = static_cast<int>(h);
renderer->bindSinkFunctions();
DRing::registerSinkTarget(_sinkId, renderer->target);
renderers.insert(std::make_pair(_sinkId, renderer));
}
- (void)removeSinkTargetWithSinkId:(NSString*)sinkId {
auto renderer = renderers.find(std::string([sinkId UTF8String]));
if (renderer != renderers.end()) {
std::unique_lock<std::mutex> lk(renderer->second->renderMutex);
renderer->second->frameCv.wait(lk, [=] {
return !renderer->second->isRendering;
});
renderers.erase(renderer);
}
}
- (void)writeOutgoingFrameWithImage:(UIImage*)image {
unsigned capacity = 4 * image.size.width * image.size.height;
uint8_t* buf = (uint8_t*)DRing::obtainFrame(capacity);
if (buf){
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGImageRef imageRef = [image CGImage];
CGContextRef bitmap = CGBitmapContextCreate(buf,
image.size.width,
image.size.height,
8,
image.size.width * 4,
colorSpace,
kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextDrawImage( bitmap, CGRectMake(0, 0, image.size.width, image.size.height), imageRef);
CGContextRelease(bitmap);
CGColorSpaceRelease(colorSpace);
}
DRing::releaseFrame((void*)buf);
}
- (void)addVideoDeviceWithName:(NSString*)deviceName withDevInfo:(NSDictionary*)deviceInfoDict {
std::vector<std::map<std::string, std::string>> devInfo;
auto setting = [Utils dictionnaryToMap:deviceInfoDict];
devInfo.emplace_back(setting);
DRing::addVideoDevice(std::string([deviceName UTF8String]), &devInfo);
DRing::setDefaultDevice(std::string([deviceName UTF8String]));
}
- (void)setDecodingAccelerated:(BOOL)state {
DRing::setDecodingAccelerated(state);
}
- (void)switchInput:(NSString*)deviceName {
DRing::switchInput(std::string([deviceName UTF8String]));
}
#pragma mark PresenceAdapterDelegate
+ (id <VideoAdapterDelegate>)delegate {
return _delegate;
}
+ (void) setDelegate:(id<VideoAdapterDelegate>)delegate {
_delegate = delegate;
}
#pragma mark -
@end
...@@ -26,7 +26,21 @@ ...@@ -26,7 +26,21 @@
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="cOr-ft-BIO"> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="cOr-ft-BIO">
<rect key="frame" x="0.0" y="0.0" width="375" height="618"/> <rect key="frame" x="0.0" y="0.0" width="375" height="618"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DMu-Or-dd7">
<rect key="frame" x="0.0" y="309" width="375" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" id="GKH-fc-lLl"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="DMu-Or-dd7" secondAttribute="trailing" id="5Aa-Zq-Wcw"/>
<constraint firstItem="DMu-Or-dd7" firstAttribute="centerY" secondItem="cOr-ft-BIO" secondAttribute="centerY" id="YVb-yp-TQG"/>
<constraint firstItem="DMu-Or-dd7" firstAttribute="leading" secondItem="cOr-ft-BIO" secondAttribute="leading" id="hFx-0M-Kk5"/>
</constraints>
</view> </view>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<blurEffect style="light"/> <blurEffect style="light"/>
...@@ -107,6 +121,7 @@ ...@@ -107,6 +121,7 @@
<connections> <connections>
<outlet property="cancelButton" destination="B3b-V0-rjx" id="dU9-MG-0y3"/> <outlet property="cancelButton" destination="B3b-V0-rjx" id="dU9-MG-0y3"/>
<outlet property="durationLabel" destination="zMN-6z-uXT" id="Uuf-ph-lrC"/> <outlet property="durationLabel" destination="zMN-6z-uXT" id="Uuf-ph-lrC"/>
<outlet property="incomingVideo" destination="DMu-Or-dd7" id="ogh-ft-54u"/>
<outlet property="infoBottomLabel" destination="SdV-jx-Mla" id="yX9-em-p4w"/> <outlet property="infoBottomLabel" destination="SdV-jx-Mla" id="yX9-em-p4w"/>
<outlet property="nameLabel" destination="73Y-N1-Yga" id="XcQ-V6-ZrF"/> <outlet property="nameLabel" destination="73Y-N1-Yga" id="XcQ-V6-ZrF"/>
<outlet property="profileImageView" destination="fnt-PQ-Q6P" id="MgB-Ev-bTc"/> <outlet property="profileImageView" destination="fnt-PQ-Q6P" id="MgB-Ev-bTc"/>
......
...@@ -23,6 +23,7 @@ import UIKit ...@@ -23,6 +23,7 @@ import UIKit
import Chameleon import Chameleon
import RxSwift import RxSwift
import Reusable import Reusable
import SwiftyBeaver
class CallViewController: UIViewController, StoryboardBased, ViewModelBased { class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
...@@ -31,11 +32,15 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased { ...@@ -31,11 +32,15 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
@IBOutlet weak var durationLabel: UILabel! @IBOutlet weak var durationLabel: UILabel!
@IBOutlet weak var infoBottomLabel: UILabel! @IBOutlet weak var infoBottomLabel: UILabel!
@IBOutlet weak var cancelButton: UIButton! @IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var incomingVideo: UIImageView!
@IBOutlet weak var capturedVideo: UIImageView!
var viewModel: CallViewModel! var viewModel: CallViewModel!
fileprivate let disposeBag = DisposeBag() fileprivate let disposeBag = DisposeBag()
private let log = SwiftyBeaver.self
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.setupUI() self.setupUI()
......
...@@ -34,9 +34,21 @@ class CallViewModel: Stateable, ViewModel { ...@@ -34,9 +34,21 @@ class CallViewModel: Stateable, ViewModel {
fileprivate let callService: CallsService fileprivate let callService: CallsService
fileprivate let contactsService: ContactsService fileprivate let contactsService: ContactsService
fileprivate let accountService: AccountsService fileprivate let accountService: AccountsService
fileprivate let videoService: VideoService
private let disposeBag = DisposeBag() private let disposeBag = DisposeBag()
fileprivate let log = SwiftyBeaver.self fileprivate let log = SwiftyBeaver.self
lazy var incomingFrame: Observable<UIImage?> = {
return videoService.incomingVideoFrame.asObservable().map({ frame in
return frame
})
}()
lazy var capturedFrame: Observable<UIImage?> = {
return videoService.capturedVideoFrame.asObservable().map({ frame in
return frame
})
}()
var call: CallModel? { var call: CallModel? {
didSet { didSet {
guard let call = self.call else { guard let call = self.call else {
...@@ -127,6 +139,7 @@ class CallViewModel: Stateable, ViewModel { ...@@ -127,6 +139,7 @@ class CallViewModel: Stateable, ViewModel {
self.callService = injectionBag.callService self.callService = injectionBag.callService
self.contactsService = injectionBag.contactsService self.contactsService = injectionBag.contactsService
self.accountService = injectionBag.accountService self.accountService = injectionBag.accountService
self.videoService = injectionBag.videoService
} }
static func formattedDurationFrom(interval: Int) -> String { static func formattedDurationFrom(interval: Int) -> String {
let seconds = interval % 60 let seconds = interval % 60
......
...@@ -31,6 +31,7 @@ class InjectionBag { ...@@ -31,6 +31,7 @@ class InjectionBag {
let presenceService: PresenceService let presenceService: PresenceService
let networkService: NetworkService let networkService: NetworkService
let callService: CallsService let callService: CallsService
let videoService: VideoService
init (withDaemonService daemonService: DaemonService, init (withDaemonService daemonService: DaemonService,
withAccountService accountService: AccountsService, withAccountService accountService: AccountsService,
...@@ -39,7 +40,8 @@ class InjectionBag { ...@@ -39,7 +40,8 @@ class InjectionBag {
withContactsService contactsService: ContactsService, withContactsService contactsService: ContactsService,
withPresenceService presenceService: PresenceService, withPresenceService presenceService: PresenceService,
withNetworkService networkService: NetworkService, withNetworkService networkService: NetworkService,
withCallService callService: CallsService) { withCallService callService: CallsService,
withVideoService videoService: VideoService) {
self.daemonService = daemonService self.daemonService = daemonService
self.accountService = accountService self.accountService = accountService
self.nameService = nameService self.nameService = nameService
...@@ -48,6 +50,7 @@ class InjectionBag { ...@@ -48,6 +50,7 @@ class InjectionBag {
self.presenceService = presenceService self.presenceService = presenceService
self.networkService = networkService self.networkService = networkService
self.callService = callService self.callService = callService
self.videoService = videoService
} }
} }
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSCameraUsageDescription</key> <key>NSCameraUsageDescription</key>
<string>Used to take user profile picture</string> <string>Used to take user profile picture and record video</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Used to let the user choose a profile picture</string> <string>Used to let the user choose a profile picture</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
......
...@@ -121,4 +121,26 @@ class AccountConfigModel: Object { ...@@ -121,4 +121,26 @@ class AccountConfigModel: Object {
let value: String? = self.configValues[configKeyModel] let value: String? = self.configValues[configKeyModel]
return value != nil ? value! : "" return value != nil ? value! : ""
} }
/**
Getter for the configuration elements.
- Parameter configKeyModel: the ConfigKeyModel identifying the configuration element to get
- Parameter value: the string value to set at the key
*/
func set(withConfigKeyModel configKeyModel: ConfigKeyModel, withValue value: String) {
self.configValues[configKeyModel] = value
}
}
extension AccountConfigModel {
func toDetails() -> [String: String]? {
var details = [String: String]()
for (key, value) in self.configValues {
details[key.key.rawValue] = value
}
return details
}
} }
...@@ -65,7 +65,9 @@ class AccountsService: AccountAdapterDelegate { ...@@ -65,7 +65,9 @@ class AccountsService: AccountAdapterDelegate {
- SeeAlso: `sharedResponseStream` - SeeAlso: `sharedResponseStream`
*/ */
fileprivate let responseStream = PublishSubject<ServiceEvent>() fileprivate let responseStream = PublishSubject<ServiceEvent>()
let dbManager = DBManager(profileHepler: ProfileDataHelper(),conversationHelper: ConversationDataHelper(),interactionHepler: InteractionDataHelper()) let dbManager = DBManager(profileHepler: ProfileDataHelper(),
conversationHelper: ConversationDataHelper(),
interactionHepler: InteractionDataHelper())
// MARK: - Public members // MARK: - Public members
/** /**
...@@ -120,6 +122,7 @@ class AccountsService: AccountAdapterDelegate { ...@@ -120,6 +122,7 @@ class AccountsService: AccountAdapterDelegate {
} }
} }
} }
init(withAccountAdapter accountAdapter: AccountAdapter) { init(withAccountAdapter accountAdapter: AccountAdapter) {
self.accountList = [] self.accountList = []
...@@ -201,11 +204,10 @@ class AccountsService: AccountAdapterDelegate { ...@@ -201,11 +204,10 @@ class AccountsService: AccountAdapterDelegate {
let devices = getKnownRingDevices(fromAccountId: accountId!) let devices = getKnownRingDevices(fromAccountId: accountId!)
account = try AccountModel(withAccountId: accountId!, account = try AccountModel(withAccountId: accountId!,
details: details, details: details,
volatileDetails: volatileDetails, volatileDetails: volatileDetails,
credentials: credentials, credentials: credentials,
devices: devices) devices: devices)
//TODO: set registration state as ready for a SIP account
let accountModelHelper = AccountModelHelper(withAccount: account!) let accountModelHelper = AccountModelHelper(withAccount: account!)
var accountAddedEvent = ServiceEvent(withEventType: .accountAdded) var accountAddedEvent = ServiceEvent(withEventType: .accountAdded)
...@@ -286,7 +288,7 @@ class AccountsService: AccountAdapterDelegate { ...@@ -286,7 +288,7 @@ class AccountsService: AccountAdapterDelegate {
- Parameter id: the id of the account. - Parameter id: the id of the account.
- Returns: the details of the accounts. - Returns: the details of the account.
*/ */
func getAccountDetails(fromAccountId id: String) -> AccountConfigModel { func getAccountDetails(fromAccountId id: String) -> AccountConfigModel {
let details: NSDictionary = accountAdapter.getAccountDetails(id) as NSDictionary let details: NSDictionary = accountAdapter.getAccountDetails(id) as NSDictionary
...@@ -295,12 +297,22 @@ class AccountsService: AccountAdapterDelegate { ...@@ -295,12 +297,22 @@ class AccountsService: AccountAdapterDelegate {
return accountDetails return accountDetails