c# - Manipular un Adaptive IInputStream(MPEG-DASH) sobre la marcha
uwp binaryreader (1)
Esta es la primera vez que hago una pregunta aquí y no pude encontrar nada en la función de búsqueda.
El problema:
Estoy recibiendo (a través de HttpClient
) un IInputStream (Segmentos MP4). El problema es que mi MediaPlayer no lo reproduce porque estos segmentos MP4 contienen un solo átomo que causa un error ERR_FILETYPE_NOT_SUPPORTED
.
Entonces, si UUID | Position: moov -> trak -> mdia -> minf -> stbl -> stsd -> encv -> sinf -> schi -> uui
manualmente este Atom ( UUID | Position: moov -> trak -> mdia -> minf -> stbl -> stsd -> encv -> sinf -> schi -> uui
) funciona perfectamente bien.
El problema es que, como tengo un flujo adaptable, tengo que modificar esos segmentos (aproximadamente 1250 por película) sobre la marcha antes de enviarlo a AdaptiveMediaSource, y no puedo hacer que esto funcione correctamente.
Código:
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 ... ¿Cómo podría resolver este problema? ¿alguna pista?
Creo que puedes implementar una clase Stream tú mismo. Cuando alguien lee su transmisión, lee el IInputStream original y lo manipula y devuelve el resultado a la persona que llama.
Por cierto, si agrega "using System.IO", será muy fácil convertir .NET Streams ao desde Windows Streams.