在本文中,您将会了解到关于如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘的新资讯,同时我们还将为您解释avi是不是流媒体格式的相关在本文中,我们将带你探索如何使用AVURLAsset
在本文中,您将会了解到关于如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘的新资讯,同时我们还将为您解释avi是不是流媒体格式的相关在本文中,我们将带你探索如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘的奥秘,分析avi是不是流媒体格式的特点,并给出一些关于asp.net-core-mvc – 如何考虑请求和响应范围标头流式传输视频或文件?、c# – 如何从.NET将数据保存到磁盘?、c# – 如何在asp.net中流式传输视频内容?、Golang web 开发实战之 session 缓存:如何使用 redigo 将一个结构体数据保存到 redis?的实用技巧。
本文目录一览:- 如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘(avi是不是流媒体格式)
- asp.net-core-mvc – 如何考虑请求和响应范围标头流式传输视频或文件?
- c# – 如何从.NET将数据保存到磁盘?
- c# – 如何在asp.net中流式传输视频内容?
- Golang web 开发实战之 session 缓存:如何使用 redigo 将一个结构体数据保存到 redis?
如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘(avi是不是流媒体格式)
几天前,我被要求检查从互联网下载视频时播放视频的难度。我知道这很容易,因为有人在不久前告诉我。所以,我检查了一下,这非常容易。
问题是我想将视频保存到磁盘上,而不是强迫用户一次又一次地下载它。
问题是访问缓冲区并将其存储到磁盘。
Stackoverflow中的许多答案都说不可能。特别是视频。
我播放视频的原始代码:
import AVFoundation....//MARK: - Accessorslazy var player: AVPlayer = { var player: AVPlayer = AVPlayer(playerItem: self.playerItem) player.actionAtItemEnd = AVPlayerActionAtItemEnd.None return player}()lazy var playerItem: AVPlayerItem = { var playerItem: AVPlayerItem = AVPlayerItem(asset: self.asset) return playerItem}()lazy var asset: AVURLAsset = { var asset: AVURLAsset = AVURLAsset(URL: self.url) return asset}()lazy var playerLayer: AVPlayerLayer = { var playerLayer: AVPlayerLayer = AVPlayerLayer(player: self.player) playerLayer.frame = UIScreen.mainScreen().bounds playerLayer.backgroundColor = UIColor.clearColor().CGColor return playerLayer}()var url: NSURL = { var url = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4") return url!}()//MARK: - ViewLifeCycleoverride func viewDidLoad() { super.viewDidLoad() view.layer.addSublayer(playerLayer) player.play()}
答案1
小编典典解决此问题的方法是使用AVAssetExportSession
和AVAssetResourceLoaderDelegate
:
第一步是添加一条通知,以了解视频何时结束。然后,我们可以开始将其保存到磁盘。
override func viewDidLoad() { super.viewDidLoad() NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(playerItemDidReachEnd(_:)), name: AVPlayerItemDidPlayToEndTimeNotification, object: nil) ...}deinit { NSNotificationCenter.defaultCenter().removeObserver(self)}
执行我们的功能:
func playerItemDidReachEnd(notification: NSNotification) { if notification.object as? AVPlayerItem == player.currentItem { let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) let filename = "filename.mp4" let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last! let outputURL = documentsDirectory.URLByAppendingPathComponent(filename) exporter?.outputURL = outputURL exporter?.outputFileType = AVFileTypeMPEG4 exporter?.exportAsynchronouslyWithCompletionHandler({ print(exporter?.status.rawValue) print(exporter?.error) }) }}
最后,我们需要使我们AVURLAsset
的代理AVAssetResourceLoaderDelegate:
lazy var asset: AVURLAsset = { var asset: AVURLAsset = AVURLAsset(URL: self.url) asset.resourceLoader.setDelegate(self, queue: dispatch_get_main_queue()) return asset}()
和:
extension ViewController : AVAssetResourceLoaderDelegate {}
我在GitHub中使用此代码创建了一个小型演示。
asp.net-core-mvc – 如何考虑请求和响应范围标头流式传输视频或文件?
我正在使用ByterangeStreamContent,但它似乎不再可用于dnxcore50.
那怎么办?
我是否需要手动解析请求范围标头并编写自定义FileResult来设置响应Content-Range和其余标头,并将缓冲区范围写入响应主体,或者是否已经实现了某些内容并且我错过了它?
解决方法
public class VideoStreamResult : FileStreamResult { // default buffer size as defined in BufferedStream type private const int BufferSize = 0x1000; private string MultipartBoundary = "<qwe123>"; public VideoStreamResult(Stream fileStream,string contentType) : base(fileStream,contentType) { } public VideoStreamResult(Stream fileStream,MediaTypeHeaderValue contentType) : base(fileStream,contentType) { } private bool IsMultipartRequest(RangeHeaderValue range) { return range != null && range.Ranges != null && range.Ranges.Count > 1; } private bool IsRangeRequest(RangeHeaderValue range) { return range != null && range.Ranges != null && range.Ranges.Count > 0; } protected async Task WriteVideoAsync(HttpResponse response) { var bufferingFeature = response.HttpContext.Features.Get<IHttpBufferingFeature>(); bufferingFeature?.disableResponseBuffering(); var length = FileStream.Length; var range = response.HttpContext.GetRanges(length); if (IsMultipartRequest(range)) { response.ContentType = $"multipart/byteranges; boundary={MultipartBoundary}"; } else { response.ContentType = ContentType.ToString(); } response.Headers.Add("Accept-Ranges","bytes"); if (IsRangeRequest(range)) { response.StatusCode = (int)HttpStatusCode.PartialContent; if (!IsMultipartRequest(range)) { response.Headers.Add("Content-Range",$"bytes {range.Ranges.First().From}-{range.Ranges.First().To}/{length}"); } foreach (var rangeValue in range.Ranges) { if (IsMultipartRequest(range)) // dunno if multipart works { await response.WriteAsync($"--{MultipartBoundary}"); await response.WriteAsync(Environment.NewLine); await response.WriteAsync($"Content-type: {ContentType}"); await response.WriteAsync(Environment.NewLine); await response.WriteAsync($"Content-Range: bytes {range.Ranges.First().From}-{range.Ranges.First().To}/{length}"); await response.WriteAsync(Environment.NewLine); } await WriteDataToResponseBody(rangeValue,response); if (IsMultipartRequest(range)) { await response.WriteAsync(Environment.NewLine); } } if (IsMultipartRequest(range)) { await response.WriteAsync($"--{MultipartBoundary}--"); await response.WriteAsync(Environment.NewLine); } } else { await FileStream.copyToAsync(response.Body); } } private async Task WriteDataToResponseBody(RangeItemHeaderValue rangeValue,HttpResponse response) { var startIndex = rangeValue.From ?? 0; var endindex = rangeValue.To ?? 0; byte[] buffer = new byte[BufferSize]; long totalToSend = endindex - startIndex; int count = 0; long bytesRemaining = totalToSend + 1; response.ContentLength = bytesRemaining; FileStream.Seek(startIndex,SeekOrigin.Begin); while (bytesRemaining > 0) { try { if (bytesRemaining <= buffer.Length) count = FileStream.Read(buffer,(int)bytesRemaining); else count = FileStream.Read(buffer,buffer.Length); if (count == 0) return; await response.Body.WriteAsync(buffer,count); bytesRemaining -= count; } catch (IndexOutOfRangeException) { await response.Body.FlushAsync(); return; } finally { await response.Body.FlushAsync(); } } } public override async Task ExecuteResultAsync(ActionContext context) { await WriteVideoAsync(context.HttpContext.Response); } }
并解析请求标头范围:
public static RangeHeaderValue GetRanges(this HttpContext context,long contentSize) { RangeHeaderValue rangesResult = null; string rangeHeader = context.Request.Headers["Range"]; if (!string.IsNullOrEmpty(rangeHeader)) { // rangeHeader contains the value of the Range HTTP Header and can have values like: // Range: bytes=0-1 * Get bytes 0 and 1,inclusive // Range: bytes=0-500 * Get bytes 0 to 500 (the first 501 bytes),inclusive // Range: bytes=400-1000 * Get bytes 500 to 1000 (501 bytes in total),inclusive // Range: bytes=-200 * Get the last 200 bytes // Range: bytes=500- * Get all bytes from byte 500 to the end // // Can also have multiple ranges delimited by commas,as in: // Range: bytes=0-500,600-1000 * Get bytes 0-500 (the first 501 bytes),inclusive plus bytes 600-1000 (401 bytes) inclusive // Remove "Ranges" and break up the ranges string[] ranges = rangeHeader.Replace("bytes=",string.Empty).Split(",".tochararray()); rangesResult = new RangeHeaderValue(); for (int i = 0; i < ranges.Length; i++) { const int START = 0,END = 1; long endByte,startByte; long parsedValue; string[] currentRange = ranges[i].Split("-".tochararray()); if (long.TryParse(currentRange[END],out parsedValue)) endByte = parsedValue; else endByte = contentSize - 1; if (long.TryParse(currentRange[START],out parsedValue)) startByte = parsedValue; else { // No beginning specified,get last n bytes of file // We already parsed end,so subtract from total and // make end the actual size of the file startByte = contentSize - endByte; endByte = contentSize - 1; } rangesResult.Ranges.Add(new RangeItemHeaderValue(startByte,endByte)); } } return rangesResult; }
c# – 如何从.NET将数据保存到磁盘?
其他人似乎设计了将平面数据库表中的丰富数据结构编码为从父节点到子节点的查找表的方法.这有助于针对数据运行SQL查询,但我不需要这样做:我只想保存并加载我的树.
明显的解决方案是将数据库中的所有内容都存储为一个blob:一个可能包含长字符串的单个条目.是滥用数据库还是推荐做法?另一个解决方案可能是使用XML数据库?我应该考虑的数据库有什么替代品吗?
最后,我正在从F#这样做,所以一个完整的解决方案来保持来自.NET的数据将是理想的…
编辑:请注意,格式化(例如序列化)是无关紧要的,因为我可以使用F#在格式之间进行平凡的转换.这是关于确认写入已经完成一直到非易失性存储(即磁盘盘),并且没有一部分写入的数据仍然被保存在易失性存储器(例如RAM缓存)中,使得我可以继续保持这种知识(例如通过从磁盘中删除旧版本的数据).
解决方法
这应该确保在您的写入操作(新文件)返回时,数据被提交到磁盘,您可以安全地删除旧文件.
c# – 如何在asp.net中流式传输视频内容?
WebRequest wreq = (HttpWebRequest)WebRequest.Create(url); using (HttpWebResponse wresp = (HttpWebResponse)wreq.GetResponse()) using (Stream mystream = wresp.GetResponseStream()) { using (BinaryReader reader = new BinaryReader(mystream)) { int length = Convert.ToInt32(wresp.ContentLength); byte[] buffer = new byte[length]; buffer = reader.ReadBytes(length); Response.Clear(); Response.Buffer = false; Response.ContentType = "video/mp4"; //Response.BinaryWrite(buffer); Response.OutputStream.Write(buffer,buffer.Length); Response.End(); } }
但问题是整个文件在播放之前下载.如何让它流式传输和播放,因为它仍在下载?还是由客户/接收者应用来管理?
解决方法
您应该在while循环中读入较小的缓冲区.
例如:
byte[] buffer = new byte[4096]; while(true) { int bytesRead = myStream.Read(buffer,buffer.Length); if (bytesRead == 0) break; Response.OutputStream.Write(buffer,bytesRead); }
Golang web 开发实战之 session 缓存:如何使用 redigo 将一个结构体数据保存到 redis?
自定义 session 结构体:type Session struct { SessionID string `json:"sessionId" bson:"sessionId"` User *User `json:"-" bson:"user"` UserType string `json:"userType" bson:"userType"` NickName string `json:"nickName" bson:"nickName"` CreateTime time.Time `json:"-" bson:"createTime"` UpdateTime time.Time `json:"-" bson:"updateTime"` Expires time.Time `json:"-" bson:"expires"` Locale string `json:"-" bson:"locale"` // default is zh_CN Menus []wmodel.Menu `json:"menus" bson:"menus"` }
使用 json.Marshal 将结构体 json 化之后保存到 redis:
/* 【增】 描述:向 session 哈希表中插入一个 session 对象 session 顶级 key,顶级 key 可以设置过期时间 <[session]: 要插入的 session 对象 >[error]:插入失败相关信息 */ func (s *sessionService) addSession(session *model.Session) error { // 从池里获取连接 conn := pool.Get() if conn == nil { log.Errorf("redis connnection is nil") return errors.New("redis connnection is nil") } // 用完后将连接放回连接池 defer conn.Close() // 将session转换成json数据,注意:转换后的value是一个byte数组 value,err := json.Marshal(session) if err != nil { log.Errorf("json marshal err,%s",err) return err } log.Infof("send data[%s]",session.SessionID,value) _,err = conn.Do("SET",value) if err != nil { return err } return nil }
Golang 测试验证:
func TestAddSession(t *testing.T) { s := &model.Session{ SessionID: "20150421120000",UserType: "admin",NickName: "df",} err := sService.addSession(s) if err != nil { t.Errorf("fail to add one session(%+v): %s",s,err) t.FailNow() } }
redis 客户端查看该 session 保存情况:
参考资料
GO: How to save and retrieve a struct to redis using redigo今天的关于如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘和avi是不是流媒体格式的分享已经结束,谢谢您的关注,如果想了解更多关于asp.net-core-mvc – 如何考虑请求和响应范围标头流式传输视频或文件?、c# – 如何从.NET将数据保存到磁盘?、c# – 如何在asp.net中流式传输视频内容?、Golang web 开发实战之 session 缓存:如何使用 redigo 将一个结构体数据保存到 redis?的相关知识,请在本站进行查询。
本文标签: