Commit 5e46e54e authored by Tobias's avatar Tobias

first very hacked tvOS UPnP playback POC

parent 3b4d6b13
---
BUNDLE_PATH: vendor/bundle
BUNDLE_DISABLE_SHARED_GEMS: '1'
......@@ -9,3 +9,4 @@ External
ImportedSources
DerivedData
Pods
vendor/bundle
source 'https://rubygems.org'
gem 'cocoapods', :git => 'https://github.com/CocoaPods/CocoaPods.git', :branch => 'tvos'
gem 'cocoapods-core', :git => 'https://github.com/CocoaPods/Core.git', :branch => 'tvos-support'
gem 'xcodeproj', :git => 'https://github.com/CocoaPods/Xcodeproj.git', :branch => 'tvos-support'
GIT
remote: https://github.com/CocoaPods/CocoaPods.git
revision: 25bcc14b04260939144496cc08f091061f5500de
branch: tvos
specs:
cocoapods (0.39.0.beta.4)
activesupport (>= 3.2.15)
claide (~> 0.9.1)
cocoapods-core (= 0.39.0.beta.4)
cocoapods-downloader (~> 0.9.3)
cocoapods-plugins (~> 0.4.2)
cocoapods-search (~> 0.1.0)
cocoapods-stats (~> 0.6.1)
cocoapods-trunk (~> 0.6.4)
cocoapods-try (~> 0.5.1)
colored (~> 1.2)
escape (~> 0.0.4)
molinillo (~> 0.3.1)
nap (~> 1.0)
xcodeproj (~> 0.27.2)
GIT
remote: https://github.com/CocoaPods/Core.git
revision: f60d8b4f28223ea2e02316efa52a612153d7fe2e
branch: tvos-support
specs:
cocoapods-core (0.39.0.beta.4)
activesupport (>= 3.2.15)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
GIT
remote: https://github.com/CocoaPods/Xcodeproj.git
revision: b208640ffd04ffb1ec1becc085e8b3f3759ff8f1
branch: tvos-support
specs:
xcodeproj (0.27.2)
activesupport (>= 3)
claide (~> 0.9.1)
colored (~> 1.2)
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.4)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
claide (0.9.1)
cocoapods-downloader (0.9.3)
cocoapods-plugins (0.4.2)
nap
cocoapods-search (0.1.0)
cocoapods-stats (0.6.1)
cocoapods-trunk (0.6.4)
nap (>= 0.8, < 2.0)
netrc (= 0.7.8)
cocoapods-try (0.5.1)
colored (1.2)
escape (0.0.4)
fuzzy_match (2.0.4)
i18n (0.7.0)
json (1.8.3)
minitest (5.8.0)
molinillo (0.3.1)
nap (1.0.0)
netrc (0.7.8)
thread_safe (0.3.5)
tzinfo (1.2.2)
thread_safe (~> 0.1)
PLATFORMS
ruby
DEPENDENCIES
cocoapods!
cocoapods-core!
xcodeproj!
BUNDLED WITH
1.10.6
......@@ -9,13 +9,20 @@ target 'vlc-ios' do
pod 'OBSlider', '1.1.0'
pod 'GHSidebarNav', '1.0.0'
pod 'InAppSettingsKit', '2.2.2'
pod 'upnpx', '~>1.3.4'
pod 'upnpx', :podspec => 'vendor/upnpx.podspec'
pod 'HockeySDK', '~>3.6.4'
pod 'SSKeychain', '~>1.2.2'
pod 'box-ios-sdk-v2', :git => 'git://github.com/carolanitz/box-ios-sdk-v2.git', :commit => 'd2df30aa5f76d30910e06f3ef5aff49025de3cf1' #has a logout function added
pod 'RESideMenu', '~>4.0.7'
end
target 'VLC for Apple TV' do
platform :tvos, '9.0'
pod 'upnpx', :podspec => 'vendor/upnpx.podspec'
end
post_install do |installer_representation|
installer_representation.pods_project.build_configurations.each do |config|
config.build_settings['SKIP_INSTALL'] = 'YES'
......
......@@ -19,12 +19,14 @@ DEPENDENCIES:
- OBSlider (= 1.1.0)
- RESideMenu (~> 4.0.7)
- SSKeychain (~> 1.2.2)
- upnpx (~> 1.3.4)
- upnpx (from `vendor/upnpx.podspec`)
EXTERNAL SOURCES:
box-ios-sdk-v2:
:commit: d2df30aa5f76d30910e06f3ef5aff49025de3cf1
:git: git://github.com/carolanitz/box-ios-sdk-v2.git
upnpx:
:podspec: vendor/upnpx.podspec
CHECKOUT OPTIONS:
box-ios-sdk-v2:
......@@ -39,6 +41,6 @@ SPEC CHECKSUMS:
OBSlider: 490f108007bfdd5414a38650b211fe403a95b8a0
RESideMenu: f24c508404b49c667344c54aba7e590883533958
SSKeychain: 88767e903ee8d274ed380e364d96b7a101235286
upnpx: 790dcf9598818385d881ca39a5b77b184b7d291a
upnpx: a480f282159960290fc2a620c7937a785b3e4b82
COCOAPODS: 0.38.2
COCOAPODS: 0.39.0.beta.4
This diff is collapsed.
......@@ -12,7 +12,7 @@
#import <UIKit/UIKit.h>
@interface FirstViewController : UIViewController
@property (nonatomic, strong) NSURL *url;
@end
......@@ -19,7 +19,10 @@
@end
@implementation FirstViewController
- (void)awakeFromNib {
[super awakeFromNib];
self.url = [NSURL URLWithString:@"http://streams.videolan.org/streams/mp4/Mr_MrsSmith-h264_aac.mp4"];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
......@@ -34,7 +37,7 @@
_mediaplayer.drawable = self.view;
/* create a media object and give it to the player */
_mediaplayer.media = [VLCMedia mediaWithURL:[NSURL URLWithString:@"http://streams.videolan.org/streams/mp4/Mr_MrsSmith-h264_aac.mp4"]];
_mediaplayer.media = [VLCMedia mediaWithURL:self.url];
[_mediaplayer play];
}
......
......@@ -28,5 +28,10 @@
<array>
<string>arm64</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>
/*****************************************************************************
* VLCUPnPServerListViewController.h
* VLC for iOS
*****************************************************************************
* Copyright (c) 2015 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Felix Paul Kühne <fkuehne # videolan.org>
* Author: Felix Paul Kühne <fkuehne # videolan.org>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import "SecondViewController.h"
#import "VLCNetworkListViewController.h"
@interface SecondViewController ()
@class MediaServer1Device;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
@interface VLCUPnPServerListViewController : UITableViewController
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)configureWithUPNPDevice:(MediaServer1Device *)device header:(NSString *)header andRootID:(NSString *)rootID;
@end
/*****************************************************************************
* VLCUPnPServerListViewController.m
* VLC for iOS
*****************************************************************************
* Copyright (c) 2013-2015 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Felix Paul Kühne <fkuehne # videolan.org>
* Marc Etcheverry <marc@taplightsoftware.com>
* Pierre SAGASPE <pierre.sagaspe # me.com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import "VLCUPnPServerListViewController.h"
#import "NSString+SupportedMedia.h"
#import "MediaServerBasicObjectParser.h"
#import "MediaServer1ItemObject.h"
#import "MediaServer1ContainerObject.h"
#import "MediaServer1Device.h"
#import "BasicUPnPDevice+VLC.h"
#import <upnpx/UPnPManager.h>
#import "FirstViewController.h"
@interface VLCUPnPServerListViewController () <UITableViewDataSource, UITableViewDelegate>
{
MediaServer1Device *_UPNPdevice;
NSString *_UPNProotID;
NSMutableArray *_mutableObjectList;
NSMutableArray *_searchData;
MediaServer1ItemObject *_lastSelectedMediaItem;
UIView *_resourceSelectionActionSheetAnchorView;
}
@end
@implementation VLCUPnPServerListViewController
- (void)configureWithUPNPDevice:(MediaServer1Device*)device header:(NSString*)header andRootID:(NSString*)rootID
{
_UPNPdevice = device;
self.title = header;
_UPNProotID = rootID;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_mutableObjectList = [[NSMutableArray alloc] init];
NSString *sortCriteria = @"";
NSMutableString *outSortCaps = [[NSMutableString alloc] init];
[[_UPNPdevice contentDirectory] GetSortCapabilitiesWithOutSortCaps:outSortCaps];
if ([outSortCaps rangeOfString:@"dc:title"].location != NSNotFound)
{
sortCriteria = @"+dc:title";
}
NSMutableString *outResult = [[NSMutableString alloc] init];
NSMutableString *outNumberReturned = [[NSMutableString alloc] init];
NSMutableString *outTotalMatches = [[NSMutableString alloc] init];
NSMutableString *outUpdateID = [[NSMutableString alloc] init];
[[_UPNPdevice contentDirectory] BrowseWithObjectID:_UPNProotID BrowseFlag:@"BrowseDirectChildren" Filter:@"*" StartingIndex:@"0" RequestedCount:@"0" SortCriteria:sortCriteria OutResult:outResult OutNumberReturned:outNumberReturned OutTotalMatches:outTotalMatches OutUpdateID:outUpdateID];
[_mutableObjectList removeAllObjects];
NSData *didl = [outResult dataUsingEncoding:NSUTF8StringEncoding];
MediaServerBasicObjectParser *parser;
@synchronized(self) {
parser = [[MediaServerBasicObjectParser alloc] initWithMediaObjectArray:_mutableObjectList itemsOnly:NO];
}
[parser parseFromData:didl];
}
#pragma mark - table view data source, for more see super
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _mutableObjectList.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
MediaServer1BasicObject *item;
item = _mutableObjectList[indexPath.row];
if (![item isContainer]) {
MediaServer1ItemObject *mediaItem;
long long mediaSize = 0;
unsigned int durationInSeconds = 0;
unsigned int bitrate = 0;
MediaServer1ItemRes *resource = nil;
NSEnumerator *e = [[mediaItem resources] objectEnumerator];
while((resource = (MediaServer1ItemRes*)[e nextObject])){
if (resource.bitrate > 0 && resource.durationInSeconds > 0) {
mediaSize = resource.size;
durationInSeconds = resource.durationInSeconds;
bitrate = resource.bitrate;
}
}
if (mediaSize < 1)
mediaSize = [mediaItem.size longLongValue];
if (mediaSize < 1)
mediaSize = (bitrate * durationInSeconds);
// object.item.videoItem.videoBroadcast items (like the HDHomeRun) may not have this information. Center the title (this makes channel names look better for the HDHomeRun)
if (mediaSize > 0 && durationInSeconds > 0) {
[cell.detailTextLabel setText: [NSString stringWithFormat:@"%@ (%@)", [NSByteCountFormatter stringFromByteCount:mediaSize countStyle:NSByteCountFormatterCountStyleFile], [VLCTime timeWithInt:durationInSeconds * 1000].stringValue]];
}
// Custom TV icon for video broadcasts
if ([[mediaItem objectClass] isEqualToString:@"object.item.videoItem.videoBroadcast"]) {
UIImage *broadcastImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
broadcastImage = [UIImage imageNamed:@"TVBroadcastIcon"];
} else {
broadcastImage = [UIImage imageNamed:@"TVBroadcastIcon~ipad"];
}
cell.imageView.image = broadcastImage;
} else {
cell.imageView.image = [UIImage imageNamed:@"blank"];
}
} else {
cell.imageView.image = [UIImage imageNamed:@"folder"];
}
cell.textLabel.text = [item title];
return cell;
}
#pragma mark - table view delegate, for more see super
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MediaServer1BasicObject *item;
item = _mutableObjectList[indexPath.row];
if ([item isContainer]) {
MediaServer1ContainerObject *container;
container = _mutableObjectList[indexPath.row];
[self performSegueWithIdentifier:@"showUPnP" sender:container];
} else {
MediaServer1ItemObject *mediaItem;
mediaItem = _mutableObjectList[indexPath.row];
NSURL *itemURL;
NSArray *uriCollectionKeys = [[mediaItem uriCollection] allKeys];
NSUInteger count = uriCollectionKeys.count;
NSRange position;
NSUInteger correctIndex = 0;
NSUInteger numberOfDownloadableResources = 0;
for (NSUInteger i = 0; i < count; i++) {
position = [uriCollectionKeys[i] rangeOfString:@"http-get:*:video/"];
if (position.location != NSNotFound) {
correctIndex = i;
numberOfDownloadableResources++;
}
}
NSArray *uriCollectionObjects = [[mediaItem uriCollection] allValues];
// Present an action sheet for the user to choose which URI to download. Do not deselect the cell to provide visual feedback to the user
if (numberOfDownloadableResources > 1) {
_resourceSelectionActionSheetAnchorView = [tableView cellForRowAtIndexPath:indexPath];
// [self presentResourceSelectionActionSheetForUPnPMediaItem:mediaItem forDownloading:NO];
} else {
if (uriCollectionObjects.count > 0) {
itemURL = [NSURL URLWithString:uriCollectionObjects[correctIndex]];
}
if (itemURL) {
[self performSegueWithIdentifier:@"showFromURL" sender:itemURL];
}
}
}
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showUPnP"]) {
MediaServer1ContainerObject *container = sender;
[(VLCUPnPServerListViewController *)segue.destinationViewController configureWithUPNPDevice:_UPNPdevice header:[container title] andRootID:[container objectID]];
} else if ([segue.identifier isEqualToString:@"showFromURL"]) {
[(FirstViewController *)segue.destinationViewController setUrl:sender];
}
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([sender isKindOfClass:[UITableViewCell class]]) {
return false;
}
return true;
}
@end
......@@ -4,15 +4,13 @@
* Copyright (c) 2015 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Felix Paul Kühne <fkuehne # videolan.org>
* Authors: Tobias Conradi <videolan@tobias-conradi.de>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import <UIKit/UIKit.h>
@interface SecondViewController : UIViewController
@interface VLCUPnPTableViewController : UITableViewController
@end
/*****************************************************************************
* VLC for iOS
*****************************************************************************
* Copyright (c) 2015 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Tobias Conradi <videolan@tobias-conradi.de>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import "VLCUPnPTableViewController.h"
#import <upnpx/UPnPManager.h>
#import "VLCUPnPServerListViewController.h"
@interface VLCUPnPTableViewController () <UPnPDBObserver>
{
NSArray *_filteredUPNPDevices;
NSArray *_UPNPdevices;
BOOL _udnpDiscoveryRunning;
NSTimer *_searchTimer;
BOOL _setup;
}
@end
@implementation VLCUPnPTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self _startUPNPDiscovery];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self _stopUPNPDiscovery];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _filteredUPNPDevices.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"networkCell" forIndexPath:indexPath];
BasicUPnPDevice *device = _filteredUPNPDevices[indexPath.row];
cell.textLabel.text = [device friendlyName];
cell.imageView.image = [device smallIcon];
return cell;
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showUPnP"]) {
NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
if (!indexPath) {
return;
}
BasicUPnPDevice *device = _filteredUPNPDevices[indexPath.row];
if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"]) {
MediaServer1Device *server = (MediaServer1Device*)device;
[(VLCUPnPServerListViewController *)segue.destinationViewController configureWithUPNPDevice:server header:[device friendlyName] andRootID:@"0"];
}
}
}
#pragma mark - UPNP discovery
- (void)_startUPNPDiscovery
{
UPnPManager *managerInstance = [UPnPManager GetInstance];
_UPNPdevices = [[managerInstance DB] rootDevices];
if (_UPNPdevices.count > 0)
[self UPnPDBUpdated:nil];
[[managerInstance DB] addObserver:self];
//Optional; set User Agent
if (!_setup) {
[[managerInstance SSDP] setUserAgentProduct:[NSString stringWithFormat:@"VLCforiOS/%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]] andOS:[NSString stringWithFormat:@"iOS/%@", [[UIDevice currentDevice] systemVersion]]];
_setup = YES;
}
//Search for UPnP Devices
[[managerInstance SSDP] startSSDP];
[[managerInstance SSDP] notifySSDPAlive];
_searchTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1.0] interval:10.0 target:self selector:@selector(_performSSDPSearch) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:_searchTimer forMode:NSRunLoopCommonModes];
_udnpDiscoveryRunning = YES;
}
- (void)_performSSDPSearch
{
UPnPManager *managerInstance = [UPnPManager GetInstance];
[[managerInstance SSDP] searchSSDP];
[[managerInstance SSDP] searchForMediaServer];
[[managerInstance SSDP] performSelectorInBackground:@selector(SSDPDBUpdate) withObject:nil];
}
- (void)_stopUPNPDiscovery
{
if (_udnpDiscoveryRunning) {
UPnPManager *managerInstance = [UPnPManager GetInstance];
[[managerInstance SSDP] notifySSDPByeBye];
[_searchTimer invalidate];
_searchTimer = nil;
[[managerInstance DB] removeObserver:self];
[[managerInstance SSDP] stopSSDP];
_udnpDiscoveryRunning = NO;
}
}
//protocol UPnPDBObserver
- (void)UPnPDBWillUpdate:(UPnPDB*)sender
{
}
- (void)UPnPDBUpdated:(UPnPDB*)sender
{
NSUInteger count = _UPNPdevices.count;
BasicUPnPDevice *device;
NSMutableArray *mutArray = [[NSMutableArray alloc] init];
for (NSUInteger x = 0; x < count; x++) {
device = _UPNPdevices[x];
if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"])
[mutArray addObject:device];
else
NSLog(@"found device '%@' with unsupported urn '%@'", [device friendlyName], [device urn]);
}
_filteredUPNPDevices = nil;
_filteredUPNPDevices = [NSArray arrayWithArray:mutArray];
[self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];
}
@end
This diff is collapsed.
Pod::Spec.new do |s|
s.name = 'upnpx'
s.version = '1.3.4'
s.license = 'BSD'
s.ios.deployment_target = "6.1"
s.osx.deployment_target = "10.6"
s.tvos.deployment_target = "9.0"
s.summary = 'Open Source Mac OS X / iOS Cocoa UPnP Stack.'
s.homepage = 'https://github.com/fkuehne/upnpx'
s.authors = { 'Felix Paul Kühne' => 'fkuehne@videolan.org', 'Bruno Keymolen' => 'bruno.keymolen@gmail.com', 'Paul Williamson' => 'squarefrog@gmail.com' }
s.source = { :git => 'https://github.com/fkuehne/upnpx.git', :tag => s.version.to_s }
s.description = 'Static OS X & iOS UPnP library written in Cocoa (UPnP) and C++ (SSDP).' \
'The Current implementation has support for control point/client only.'
s.ios.source_files = 'src/{api,common,eventserver,ssdp,upnp}/*.{h,m,mm,c,cpp}', 'src/port/ios/*.{h,m}'
s.osx.source_files = 'src/{api,common,eventserver,ssdp,upnp}/*.{h,m,mm,c,cpp}', 'src/port/macos/*.{h,m}'
s.tvos.source_files = 'src/{api,common,eventserver,ssdp,upnp}/*.{h,m,mm,c,cpp}', 'src/port/ios/*.{h,m}'
s.tvos.public_header_files = 'src/api/*.h', 'src/port/ios/*.h'
s.ios.public_header_files = 'src/api/*.h', 'src/port/ios/*.h'
s.osx.public_header_files = 'src/api/*.h', 'src/port/macos/*.h'
s.library = 'stdc++'
s.xcconfig = {
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++0x',
'CLANG_CXX_LIBRARY' => 'libstdc++',
'OTHER_LDFLAGS' => '-weak_library /usr/lib/libstdc++.dylib'
}
s.requires_arc = false
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment