动态操作自适应 IInputStream (MPEG-DASH)

Manipulating an Adaptive IInputStream (MPEG-DASH) on the fly

这是我第一次在这里提问,我在搜索功能中找不到任何内容。

问题:

我正在接收(通过 HttpClientIInputStream(MP4 片段)。问题是,我的 MediaPlayer 没有播放它,因为这个 MP4 片段包含一个 Atom,这会导致 ERR_FILETYPE_NOT_SUPPORTED 错误。

所以如果我手动删除这个 Atom ( UUID | Position: moov -> trak -> mdia -> minf -> stbl -> stsd -> encv -> sinf -> schi -> uui ) 它工作得很好。

问题是,因为我有一个自适应流,所以我必须在将它们发送到 AdaptiveMediaSource 之前即时修改这些片段(每部电影大约 1250 个)——我无法让它正常工作。

代码:

    using System;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Text;
    using System.Threading.Tasks;
    using Windows.Storage.Streams;
    using static System.Diagnostics.Debug;

    namespace BlackMagic
    {
    public class EvilVoodooClass
    {
    private static readonly bool _commitFlag = false;

    /// <summary>
    /// MP4 File parser
    /// </summary>
    /// <param name="awsInputStream"></param>
    public static async Task<Stream> ParseFile(IInputStream awsInputStream)
    {
        int moovPos = 0, moovSize = 0;
        int trakPos = 0, trakSize = 0;
        int mdiaPos = 0, mdiaSize = 0;
        int minfPos = 0, minfSize = 0;
        int stblPos = 0, stblSize = 0;
        int stsdPos = 0, stsdSize = 0;
        int encvPos = 0, encvSize = 0;
        int sinfPos = 0, sinfSize = 0;
        int schiPos = 0, schiSize = 0;
        int uuidPos = 0, uuidSize = 0;

        var bufferSize = new Windows.Storage.Streams.Buffer(200000);
        var readBuffer = await awsInputStream.ReadAsync(bufferSize, 200000, InputStreamOptions.ReadAhead);
        var tempInputStream = readBuffer.AsStream();
        var tempOutStream = readBuffer.AsStream();

        WriteLine("Parsing segment... ");

        using (var br = new BinaryReader(tempInputStream))
        {
            var originalFilelength = (int)br.BaseStream.Length;

            WriteLine($"original File length: {originalFilelength}");

            br.BaseStream.Seek(4, SeekOrigin.Begin);

            if ("ftypiso6" == Encoding.ASCII.GetString(br.ReadBytes(8)))
            {
                FindAtom(br, 0, "moov", ref moovPos, ref moovSize);
                WriteLine($"found moov at position: {moovPos} / size: {moovSize}");
                FindAtom(br, moovPos + 8, "trak", ref trakPos, ref trakSize);
                WriteLine($"found trak at position: {trakPos} / size: {trakSize}");
                FindAtom(br, trakPos + 8, "mdia", ref mdiaPos, ref mdiaSize);
                WriteLine($"found mdiaPos at position: {mdiaPos} / size: {mdiaSize}");
                FindAtom(br, moovPos + 8, "minf", ref minfPos, ref minfSize);
                WriteLine($"found minfPos at position: {minfPos} / size: {minfSize}");
                FindAtom(br, minfPos + 8, "stbl", ref stblPos, ref stblSize);
                WriteLine($"found stblPos at position: {stblPos} / size: {stblSize}");
                FindAtom(br, stblPos + 8, "stsd", ref stsdPos, ref stsdSize);
                WriteLine($"found stsdSize at position: {stsdPos} / size: {stsdSize}");
                // Breakpoint

                FindAtom(br, stsdPos + 8, "encv", ref encvPos, ref encvSize);
                WriteLine($"found encvSize at position: {encvPos} / size: {encvSize}");
                FindAtom(br, encvPos + 8, "sinf", ref sinfPos, ref sinfSize);
                WriteLine($"found sinfPos at position: {sinfPos} / size: {sinfSize}");
                FindAtom(br, sinfPos + 8, "schi", ref schiPos, ref schiSize);
                WriteLine($"found schiSize at position: {schiPos} / size: {schiSize}");
                FindAtom(br, schiPos, "uuid", ref uuidPos, ref uuidSize);
                WriteLine($"found UU--ID at position: {uuidPos} / size: {uuidSize}");
                br.BaseStream.Seek(0, SeekOrigin.Begin);

                if (uuidPos == 0)
                {
                    WriteLine(" [No UUID Atom found]");
                    return tempOutStream;
                }

                WriteLine("[UUID Atom found]");

                if (!_commitFlag) return tempOutStream;
                WriteLine("Rewriting segments... ");

                try
                {
                    br.BaseStream.Seek(moovPos, SeekOrigin.Begin);
                    var uuidBuffer = br.ReadBytes(moovSize);

                    if (BufferArrayFindTag(ref uuidBuffer, "uuid", ref uuidPos, ref uuidSize))
                        CleanUuid(ref uuidBuffer, uuidPos, uuidSize);
                    UpdateUuidSize(ref uuidBuffer);

                    using (var binWri = new BinaryWriter(tempOutStream))
                    {
                        br.BaseStream.Seek(0, SeekOrigin.Begin);
                        BufferedBinaryCopy(br, binWri, moovPos);

                        if (uuidBuffer.Length > 8) binWri.Write(uuidBuffer);

                        br.BaseStream.Seek(moovSize, SeekOrigin.Current);
                        BufferedBinaryCopy(br, binWri, originalFilelength - (moovPos + moovSize));

                    }

                    WriteLine("FIXED!");
                    return tempOutStream;
                }
                catch
                {
                    WriteLine(" --FAILED--");
                    return tempOutStream;
                }
            }
            else
            {
                WriteLine("[No MP4 Segment] ");
                return tempOutStream;
            }
        }
    }

    public static bool BufferedBinaryCopy(BinaryReader source, BinaryWriter destination, int length)
    {
        const int iobufferSize = 32 * 1024 * 1024;

        var bytesLeft = length;

        while (bytesLeft > 0)
        {
            var bytesToRead = Math.Min(iobufferSize, bytesLeft);
            var buffer = source.ReadBytes(bytesToRead);
            bytesLeft -= buffer.Length;

            destination.Write(buffer);
        }
        return true;
    }

    public static bool FindAtom(BinaryReader br, int offset, string tag, ref int pos, ref int size)
    {
        try
        {
            br.BaseStream.Seek(offset, SeekOrigin.Begin);
            while (br.BaseStream.Position < br.BaseStream.Length - 8)
            {
                var tagdata = br.ReadBytes(4);
                Array.Reverse(tagdata);
                var tagsize = (int)BitConverter.ToUInt32(tagdata, 0);
                if (tagsize == 1)
                {
                    var exdata = br.ReadBytes(4);
                    Array.Reverse(exdata);
                    tagsize = (tagsize << 32) + (int)BitConverter.ToUInt32(exdata, 0);
                }

                tagdata = br.ReadBytes(4);
                var tagname = Encoding.ASCII.GetString(tagdata);

                if (tagname == tag)
                {
                    pos = (int)br.BaseStream.Position - 8;
                    size = tagsize;
                    return true;
                }
                if (tagsize == 0)
                    return false;
                br.BaseStream.Seek(tagsize - 8, SeekOrigin.Current);
            }
        }
        catch (Exception ex)
        {
            WriteLine(ex.ToString());
        }
        return false;
    }

    public static void CleanUuid(ref byte[] buffer, int pos, int size)
    {
        var lb = buffer.ToList();
        lb.RemoveRange(pos, size);
        buffer = lb.ToArray();
    }

    public static void UpdateUuidSize(ref byte[] buffer)
    {
        var bufferLength = buffer.Length;
        var tagsize = BitConverter.GetBytes(bufferLength);
        Array.Reverse(tagsize);

        var lb = buffer.ToList();
        lb.RemoveRange(0, 4);
        lb.InsertRange(0, tagsize);
        buffer = lb.ToArray();
    }

    public static bool RewriteFile(ref BinaryReader input, long udtaPos, long udtaSize, long metaPos, long metaSize,
        long xtraPos, long xtraSize)
    {
        var tempname = string.Format(@"{0}.txt", Guid.NewGuid());
        using (var b = new BinaryWriter(File.Open("tempname", FileMode.Create, FileAccess.Read)))
        {
            //throw new NotImplementedException();
        }
        return true;
    }

    public static bool BufferArrayFindTag(ref byte[] bb, string tag, ref int tagPos, ref int tagSize)
    {
        var pattern = Encoding.UTF8.GetBytes(tag);
        var byteIndex = BufferArrayIndexOf(bb, pattern);
        if (byteIndex < 0) return false;
        tagPos = byteIndex - 4;
        tagSize = (bb[byteIndex - 4] << 24) | (bb[byteIndex - 3] << 16) | (bb[byteIndex - 2] << 8) | bb[byteIndex - 1];
        return true;
    }

    public static int BufferArrayIndexOf(byte[] data, byte[] pattern)
    {
        if (pattern.Length > data.Length) return -1;

        for (var i = 0; i < data.Length - pattern.Length; i++)
        {
            var found = !pattern.Where((t, j) => data[i + j] != t).Any();
            if (found) return i;
        }
        return -1;
    }
}

}

hm...我该如何解决这个问题?有什么线索吗?

我认为您可以自己实现 Stream class。当有人读取您的流时,您会读取原始的 IInputStream 并对其进行操作,并将结果 return 发送给调用者。

顺便说一句,如果您添加 "using System.IO",那么 .NET Streams 与 Windows Streams 之间的转换将非常容易。