iOS – 无法从parsing后端stream式传输video

最近,我用mongoLab创build了自己的分析服务器,用于存储我的数据。

我的问题是我保存一个video作为parsingPFFile ,但我似乎无法保存它能够stream。

这是我确切的步骤。

首先,我保存由UIImagePicker返回的video

 //Get the video URL let videoURL = info[UIImagePickerControllerMediaURL] as? NSURL //Create PFFile with NSData from URL let data = NSData(contentsOfURL: videoURL!) videoFile = PFFile(data: data!, contentType: "video/mp4") //Save PFFile first, then save the PFUser PFUser.currentUser()?.setObject(videoFile!, forKey: "profileVideo") videoFile?.saveInBackgroundWithBlock({ (succeeded, error) -> Void in print("saved video") PFUser.currentUser()?.saveInBackgroundWithBlock({ (succeeded, error) -> Void in if succeeded && error == nil { print("user saved") //Hide progress bar UIView.animateWithDuration(0.5, animations: { () -> Void in self.progressBar.alpha = 0 }, completion: { (bool) -> Void in self.progressBar.removeFromSuperview() }) }else{ //Show error if the save failed let message = error!.localizedDescription let alert = UIAlertController(title: "Uploading profile picture error!", message: message, preferredStyle: UIAlertControllerStyle.Alert) let dismiss = UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil) alert.addAction(dismiss) self.presentViewController(alert, animated: true, completion: nil) } }) }, progressBlock: { (progress) -> Void in self.progressBar.setProgress(Float(progress)/100, animated: true) }) 

这一切运作良好。 问题在于我检索PFFile并尝试stream式传输video。 这是我的代码:

 //Get URL from my current user self.videoFile = PFUser.currentUser()?.objectForKey("profileVideo") as? PFFile self.profileVideoURL = NSURL(string: (self.videoFile?.url)!) //Create AVPlayerController let playerController = AVPlayerViewController() //Set AVPlayer URL to where the file is stored on the sever let avPlayer = AVPlayer(URL: self.profileVideoURL) playerController.player = avPlayer //Present the playerController self.presentViewController(playerController, animated: true, completion: { () -> Void in playerController.player?.play() }) 

当我展示playerController时,发生什么事情是这样的:

在这里输入图像说明

为什么当我尝试播放video时发生这种情况?

任何帮助是极大的赞赏!

UPDATE

我最近尝试使用这行代码播放保存在不同数据库中的video: let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")

这证实这是我保存我的PFFile格式,这是导致错误。

"video/mp4"可能不是正确的格式: videoFile = PFFile(data: data!, contentType: "video/mp4")

更新2

我把mongoLab上的.mp4文件直接链接了下来,发现我可以在谷歌浏览器中播放它,但不能在Safari或iPhone上播放。

更新3

我发现这是parsingapi本身的问题,而且与代码无关,因为在使用原始parsing后端(closures的那个)代替我的定制parsing服务器时,我的代码完美工作。 我目前没有解决办法,但应该随着时间的推移而得到解决。

这段代码适用于我

 let playerController = AVPlayerViewController() self.addChildViewController(playerController) self.view.addSubview(playerController.view) playerController.view.frame = self.view.frame file!.getDataInBackgroundWithBlock({ (movieData: NSData?, error: NSError?) -> Void in if (error == nil) { let filemanager = NSFileManager.defaultManager() let documentsPath : AnyObject = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask,true)[0] let destinationPath:NSString = documentsPath.stringByAppendingString("/file.mov") movieData!.writeToFile ( destinationPath as String, atomically:true) let playerItem = AVPlayerItem(asset: AVAsset(URL: NSURL(fileURLWithPath: destinationPath as String))) let player = AVPlayer(playerItem: playerItem) playerController.player = player player.play() } else { print ("error on getting movie data \(error?.localizedDescription)") } }) 

parsing服务器似乎不支持Safari / iOS中的stream媒体,解决scheme是使用Express&GridStore启用它如下,

parsing-服务器-例如\ node_modules \parsing服务器\ lib中\路由器\ FilesRouter

  { key: 'getHandler', value: function getHandler(req, res, content) { var config = new _Config2.default(req.params.appId); var filesController = config.filesController; var filename = req.params.filename; var video = '.mp4' var lastFourCharacters = video.substr(video.length - 4); if (lastFourCharacters == '.mp4') { filesController.handleVideoStream(req, res, filename).then(function (data) { }).catch(function (err) { console.log('404FilesRouter'); res.status(404); res.set('Content-Type', 'text/plain'); res.end('File not found.'); }); }else{ filesController.getFileData(config, filename).then(function (data) { res.status(200); res.end(data); }).catch(function (err) { res.status(404); res.set('Content-Type', 'text/plain'); res.end('File not found.'); }); } } } , ... 

parsing-服务器-例如\ node_modules \parsing服务器\ lib中\控制器\ FilesController

 _createClass(FilesController, [{ key: 'getFileData', value: function getFileData(config, filename) { return this.adapter.getFileData(filename); } },{ key: 'handleVideoStream', value: function handleVideoStream(req, res, filename) { return this.adapter.handleVideoStream(req, res, filename); } }, ... 

parsing服务器-例如\ node_modules \parsing服务器\ LIB \适配器\文件\ GridStoreAdapter

 ... , { key: 'handleVideoStream', value: function handleVideoStream(req, res, filename) { return this._connect().then(function (database) { return _mongodb.GridStore.exist(database, filename).then(function () { var gridStore = new _mongodb.GridStore(database, filename, 'r'); gridStore.open(function(err, GridFile) { if(!GridFile) { res.send(404,'Not Found'); return; } console.log('filename'); StreamGridFile(GridFile, req, res); }); }); }) } }, ... 

GridStore适配器的底部

 function StreamGridFile(GridFile, req, res) { var buffer_size = 1024 * 1024;//1024Kb if (req.get('Range') != null) { //was: if(req.headers['range']) // Range request, partialle stream the file console.log('Range Request'); var parts = req.get('Range').replace(/bytes=/, "").split("-"); var partialstart = parts[0]; var partialend = parts[1]; var start = partialstart ? parseInt(partialstart, 10) : 0; var end = partialend ? parseInt(partialend, 10) : GridFile.length - 1; var chunksize = (end - start) + 1; if(chunksize == 1){ start = 0; partialend = false; } if(!partialend){ if(((GridFile.length-1) - start) < (buffer_size) ){ end = GridFile.length - 1; }else{ end = start + (buffer_size); } chunksize = (end - start) + 1; } if(start == 0 && end == 2){ chunksize = 1; } res.writeHead(206, { 'Cache-Control': 'no-cache', 'Content-Range': 'bytes ' + start + '-' + end + '/' + GridFile.length, 'Accept-Ranges': 'bytes', 'Content-Length': chunksize, 'Content-Type': 'video/mp4', }); GridFile.seek(start, function () { // get GridFile stream var stream = GridFile.stream(true); var ended = false; var bufferIdx = 0; var bufferAvail = 0; var range = (end - start) + 1; var totalbyteswanted = (end - start) + 1; var totalbyteswritten = 0; // write to response stream.on('data', function (buff) { bufferAvail += buff.length; //Ok check if we have enough to cover our range if(bufferAvail < range) { //Not enough bytes to satisfy our full range if(bufferAvail > 0) { //Write full buffer res.write(buff); totalbyteswritten += buff.length; range -= buff.length; bufferIdx += buff.length; bufferAvail -= buff.length; } } else{ //Enough bytes to satisfy our full range! if(bufferAvail > 0) { var buffer = buff.slice(0,range); res.write(buffer); totalbyteswritten += buffer.length; bufferIdx += range; bufferAvail -= range; } } if(totalbyteswritten >= totalbyteswanted) { // totalbytes = 0; GridFile.close(); res.end(); this.destroy(); } }); }); }else{ // res.end(GridFile); // stream back whole file res.header('Cache-Control', 'no-cache'); res.header('Connection', 'keep-alive'); res.header("Accept-Ranges", "bytes"); res.header('Content-Type', 'video/mp4'); res.header('Content-Length', GridFile.length); var stream = GridFile.stream(true).pipe(res); } }; 

PS原来的答案由@Bragegs给出 – https://github.com/ParsePlatform/parse-server/issues/1440#issuecomment-212815625 。 User @ Stav1在这个post中也提到过,但不幸的是他被低估了?

对于那些仍然在这里的人来说,看看新的Parse-Server-update更新Parse-server现在可以识别stream媒体; 不过,您应该使用parsingiOS sdk方法来检索video。 如果您正在推出自定义分析服务器,则服务器代码位于下方。 我遵循一些stream式方法的列表。

服务器代码更改位于:

parsing-服务器-例如\ node_modules \parsing服务器\ lib中\路由器\ FilesRouter

 { key: 'getHandler', value: function getHandler(req, res) { var config = new _Config2.default(req.params.appId); var filesController = config.filesController; var filename = req.params.filename; var contentType = _mime2.default.lookup(filename); if (isFileStreamable(req, filesController)) { filesController.getFileStream(config, filename).then(function (stream) { handleFileStream(stream, req, res, contentType); }).catch(function () { res.status(404); res.set('Content-Type', 'text/plain'); res.end('File not found.'); }); } else { filesController.getFileData(config, filename).then(function (data) { res.status(200); res.set('Content-Type', contentType); res.set('Content-Length', data.length); res.end(data); }).catch(function () { res.status(404); res.set('Content-Type', 'text/plain'); res.end('File not found.'); }); } }}, 

使用iOSparsingsdk(swift)的stream方法示例:

 vidObject.video.getDataStreamInBackground(block: <#T##PFDataStreamResultBlock?##PFDataStreamResultBlock?##(InputStream?, Error?) -> Void#>) 

我有一个与parse-server的本地mongodb,需要这个使其工作:

从PFFile.urlparsing服务器streamvideo到IOS

不知道是否与非本地数据库相同。