MobileVLCKit是開源播放器VLC的iOS平臺框架近尚,在Mac OS上也有對應(yīng)的VLCKit,搞直播的同學(xué)應(yīng)該不陌生,不過它其實還是一款強大的本地播放器体捏,支持幾乎所有的主流媒體格式塔插。最近在研究app如何瀏覽電腦上文件梗摇,然后直接做到播放視頻的功能。用過iOS上的VLC播放器的童鞋應(yīng)該知道想许,他能做到掃描本地端口伶授,然后通過輸入用戶名和密碼瀏覽電腦的文件,點擊視頻和音頻還能直接播放流纹。
通過Google知道這里用到一個叫SMB的協(xié)議糜烹,不光是Mac OS上,Windows和Linux都支持這種協(xié)議漱凝。只要本地開啟SMB的文件共享服務(wù)景图,同一個局域網(wǎng)內(nèi)的設(shè)備就能通過它訪問電腦上的文件了。
上上gayhub發(fā)現(xiàn)了一個SMB的iOS框架碉哑,叫TOSMBClient挚币,它將一個C語言的框架封裝成了OC的框架。還支持CocoaPods扣典,使用起來非常方便妆毕。而MobileVLCKit原生就支持SMB協(xié)議的在線播放。所以解決方案是通過TOSMBClient獲取文件列表贮尖,VLC播放笛粘,想法很美好,但是實際實現(xiàn)還是踩了不少坑。
先說說SMB的格式薪前,長這樣:smb://{hostname}:{password}@{ip}/path
比如桌面上的一個mp4文件就應(yīng)該長這樣:smb://xiaoming:123456@192.168.1.100/xiaoming/Desktop/233.mp4
hostname是域名润努,一般創(chuàng)建SMB共享協(xié)議的時候,就需要指定示括。password是密碼铺浇,ip是服務(wù)器的ip。
TOSMBClient提供了一個登錄的類叫TOSMBSession
垛膝,常用屬性是這幾個
//服務(wù)器域名
@property (nonatomic, copy) NSString *hostName;
//服務(wù)器ip
@property (nonatomic, copy) NSString *ipAddress;
//登錄的用戶名
@property (nonatomic, copy) NSString *userName;
//登錄密碼
@property (nonatomic, copy) NSString *password;
其中域名和ip可以都設(shè)置鳍侣,也可以只設(shè)置其中一個,框架會自動查找吼拥。
然后通過TOSMBSession
提供的方法
- (void)requestContentsOfDirectoryAtFilePath:(NSString *)path
success:(void (^)(NSArray *files))successHandler
error:(void (^)(NSError *))errorHandler;
獲取文件列表倚聚,很簡單。返回的files是TOSMBSessionFile
類型凿可。它包含基本的文件信息惑折,比如路徑,名稱枯跑,大小惨驶。想法挺美好,有URL全肮,直接給VLC播放不就行了,然后就碰到了第一個坑棘捣。
TOSMBSessionFile
提供的路徑只是smb格式的一部分辜腺,也就只有path部分,所以需要播放還得自己拼接成完整路徑乍恐。
拼接好了之后嘗試下播放個文件评疗,沒帶中文的,成功了茵烈,高興之余本著嚴謹?shù)膽B(tài)度試了下中文路徑百匆,結(jié)果失敗了。NSURL初始化如果包含標準ASCALL以外的字符呜投,會返回nil加匈,這是第二個坑。
作為程序員仑荐,很自然會想到雕拼,URL如果帶中文,瀏覽器會自動做URL轉(zhuǎn)碼粘招。所以嘗試下轉(zhuǎn)碼啥寇,發(fā)現(xiàn)還是播放失敗。這就讓我懷疑人生了,怎么肥事辑甜?而且連iOS上的VLC的app都有這個問題衰絮,這是第三個坑。
通過查找API我發(fā)現(xiàn)播放器除了通過NSURL初始化磷醋,還可以通過NSString初始化猫牡,我想,NSURL不讓包含中文子檀,NSString總可以吧镊掖。結(jié)果還是播放失敗,神奇的是即使不包含中文褂痰,通過NSString初始化還是失敗亩进,但是NSURL就可以,這是第四個坑缩歪。
既然一定要實現(xiàn)功能归薛,那就得搞明白為什么,這時候開源的好處就體現(xiàn)出來了匪蝙,我看了下MobileVLCKit的源碼主籍,它的媒體類VLCMedia是這么寫的:
- (instancetype)initWithPath:(NSString *)aPath
{
return [self initWithURL:[NSURL fileURLWithPath:aPath isDirectory:NO]];
}
- (instancetype)initWithURL:(NSURL *)anURL
{
if (self = [super init]) {
const char *url;
VLCLibrary *library = [VLCLibrary sharedLibrary];
NSAssert(library.instance, @"no library instance when creating media");
if (([[anURL absoluteString] hasPrefix:@"sftp://"]) ||
([[anURL absoluteString] hasPrefix:@"smb://"])) {
url = [[[anURL absoluteString] stringByRemovingPercentEncoding] UTF8String];
} else {
url = [[anURL absoluteString] UTF8String];
}
p_md = libvlc_media_new_location(library.instance, url);
_metaDictionary = [[NSMutableDictionary alloc] initWithCapacity:3];
[self initInternalMediaDescriptor];
}
return self;
}
可以看到,initWithPath:
方法把字符串通過fileURLWithPath:isDirectory:
方法初始化成URL了逛球,所以smb格式的字符串路徑通過這個方法初始化得到的路徑肯定是錯的千元,因為它不是標準的本地路徑,自然會出現(xiàn)上面神奇的情況颤绕。
然后看下initWithURL:
方法幸海,if語句判斷如果包含smb前綴,則做URL解碼操作奥务。說明我們的想法是正確的物独,確實應(yīng)該對URL進行編碼。但是氯葬,經(jīng)過測試編碼還是不行挡篓,這種情況就很費解了。到現(xiàn)在我還不知道什么原因帚称,因為它自己的APP都有這個問題官研。不過之后偶然發(fā)現(xiàn)了解決方法,很簡單
**
把URL編碼兩次即可4扯谩阀参!
把URL編碼兩次即可!瞻坝!
把URL編碼兩次即可V肟恰杏瞻!
**
重要的事情說三遍,編碼兩次之后框架會對URL解碼一次衙荐,所以得到的URL實際是編碼了一次的內(nèi)容捞挥,這樣就能播放了,非常神奇忧吟,這是第五個坑砌函。在這里分享一下給需要的童鞋。
注意事項
應(yīng)該只對path
溜族、hostname
讹俊、password
部分做URL兩次編碼,smb://
前綴不需要煌抒,否則播放器會無法識別仍劈。
2017.10.16日更新 如果發(fā)現(xiàn)拼接之后還是沒法播放,大部分去因為VLC的版本比較舊的關(guān)系寡壮,cocoapods里有最新的unstable版本贩疙,用這個,不過這個版本也是最不穩(wěn)定的况既。
2018.7.8日更新 文章Demo