GVKun编程网logo

如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘(avi是不是流媒体格式)

1

在本文中,您将会了解到关于如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘的新资讯,同时我们还将为您解释avi是不是流媒体格式的相关在本文中,我们将带你探索如何使用AVURLAsset

在本文中,您将会了解到关于如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘的新资讯,同时我们还将为您解释avi是不是流媒体格式的相关在本文中,我们将带你探索如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘的奥秘,分析avi是不是流媒体格式的特点,并给出一些关于asp.net-core-mvc – 如何考虑请求和响应范围标头流式传输视频或文件?、c# – 如何从.NET将数据保存到磁盘?、c# – 如何在asp.net中流式传输视频内容?、Golang web 开发实战之 session 缓存:如何使用 redigo 将一个结构体数据保存到 redis?的实用技巧。

本文目录一览:

如何使用AVURLAsset流式传输视频并将缓存的数据保存到磁盘(avi是不是流媒体格式)

如何使用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

小编典典

解决此问题的方法是使用AVAssetExportSessionAVAssetResourceLoaderDelegate

第一步是添加一条通知,以了解视频何时结束。然后,我们可以开始将其保存到磁盘。

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 – 如何考虑请求和响应范围标头流式传输视频或文件?

asp.net-core-mvc – 如何考虑请求和响应范围标头流式传输视频或文件?

我现在正在使用FileStreamResult,它可以传输视频,但无法搜索它.它总是从头开始.

我正在使用ByterangeStreamContent,但它似乎不再可用于dnxcore50.

那怎么办?

我是否需要手动解析请求范围标头并编写自定义FileResult来设置响应Content-Range和其余标头,并将缓冲区范围写入响应主体,或者是否已经实现了某些内容并且我错过了它?

解决方法

这是一个VideoStreamResult的简单实现,我现在正在使用(多部分内容部分未经过测试):
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将数据保存到磁盘?

c# – 如何从.NET将数据保存到磁盘?

我有多种丰富的数据结构(主要是树),我想坚持到磁盘,这意味着我不仅要将它们写入磁盘,而且要保证数据已经被完全写入,并且能够在掉电.

其他人似乎设计了将平面数据库表中的丰富数据结构编码为从父节点到子节点的查找表的方法.这有助于针对数据运行SQL查询,但我不需要这样做:我只想保存并加载我的树.

明显的解决方案是将数据库中的所有内容都存储为一个blob:一个可能包含长字符串的单个条目.是滥用数据库还是推荐做法?另一个解决方案可能是使用XML数据库?我应该考虑的数据库有什么替代品吗?

最后,我正在从F#这样做,所以一个完整的解决方案来保持来自.NET的数据将是理想的…

编辑:请注意,格式化(例如序列化)是无关紧要的,因为我可以使用F#在格式之间进行平凡的转换.这是关于确认写入已经完成一直到非易失性存储(即磁盘盘),并且没有一部分写入的数据仍然被保存在易失性存储器(例如RAM缓存)中,使得我可以继续保持这种知识(例如通过从磁盘中删除旧版本的数据).

解决方法

.NET的FileStream类的一些构造函数使用类型为 FileOptions的参数.FileOptions的一个值为WriteThrough,表示系统应通过任何中间缓存写入,直接转到磁盘.

这应该确保在您的写入操作(新文件)返回时,数据被提交到磁盘,您可以安全地删除旧文件.

c# – 如何在asp.net中流式传输视频内容?

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?

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?的相关知识,请在本站进行查询。

本文标签: