Replies: 11 comments
-
@aklemets - any idea? |
Beta Was this translation helpful? Give feedback.
-
I wasn't sure where to write best, so there is a duplicate in WinUI repository - microsoft/microsoft-ui-xaml#2964 |
Beta Was this translation helpful? Give feedback.
-
@aklemets - Could you please have a look? |
Beta Was this translation helpful? Give feedback.
-
In general, seeking in MP4 files should work fine. It should be easy to demonstrate by just opening an MP4 file in Movies & TV. You said you have a problem with a "home-made stream". Have you tried looking for some MP4 file validation tool that can check the file for correctness? Or compare the layout of your file using any other "normal" MP4 file, such as the Big Buck Bunny MP4 file? It might be that your home-made file is missing some information needed for random access seeking. |
Beta Was this translation helpful? Give feedback.
-
Is there a tool to validate CENC-encoded MP4 file? I know I can decrypt it, but that might change the structure... |
Beta Was this translation helpful? Give feedback.
-
I tried to narrow the problem down, and got two issues.
Opening as fileMedia.Source = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(file)); Opening as a streamvar inp = new HomeMadeStream(file, this);
await inp.Init();
Media.Source = new MediaPlaybackItem(MediaSource.CreateFromStream(inp, "video/mp4")); In both cases, |
Beta Was this translation helpful? Give feedback.
-
Here is the trivial implementation of the stream. I can send the whole source code, if you tell me where to. using System;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Streams;
namespace CencPlayer
{
public sealed class HomeMadeStream : IRandomAccessStream
{
#region Fields
private readonly StorageFile mFile;
private readonly IUiLogger mLogger;
private ulong mPosition;
private ulong mLength;
private IRandomAccessStreamWithContentType mStream;
#endregion
#region Properties
bool IRandomAccessStream.CanRead => true;
bool IRandomAccessStream.CanWrite => false;
ulong IRandomAccessStream.Position => mPosition;
ulong IRandomAccessStream.Size { get => mLength; set => throw new NotImplementedException(); }
#endregion
#region Init and clean-up
public HomeMadeStream(StorageFile file, IUiLogger logger)
{
mFile = file;
mLogger = logger;
mLogger.Log($"Opening {file.DisplayName}.");
}
private HomeMadeStream(StorageFile file, ulong position, ulong length, IUiLogger logger)
{
mFile = file;
mPosition = position;
mLength = length;
mLogger = logger;
}
/// <summary>
/// Needs to be called right after the constructor.
/// Separated method due to async.
/// </summary>
public async Task Init()
{
var prop = await mFile.GetBasicPropertiesAsync();
mLength = prop.Size;
mLogger.Log($"Length {mLength}.");
}
void IDisposable.Dispose()
{
mStream.Dispose();
}
#endregion
#region API
IInputStream IRandomAccessStream.GetInputStreamAt(ulong position)
{
mLogger.Log($"Clone stream from position {position}.");
return new HomeMadeStream(mFile, position, mLength, mLogger);
}
IOutputStream IRandomAccessStream.GetOutputStreamAt(ulong position)
{
throw new NotImplementedException();
}
void IRandomAccessStream.Seek(ulong position)
{
if (mPosition == position)
return;
mLogger.Log($"Seek from {mPosition} to {position}.");
mPosition = position;
}
IRandomAccessStream IRandomAccessStream.CloneStream()
{
mLogger.Log("Clone stream.");
return new HomeMadeStream(mFile, mPosition, mLength, mLogger);
}
IAsyncOperationWithProgress<IBuffer, uint> IInputStream.ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
{
return new AsyncOperationWithProgress<IBuffer>(async () =>
{
if (mStream is null)
{
mStream = await mFile.OpenReadAsync();
}
mStream.Seek(mPosition);
mPosition += count;
return await mStream.ReadAsync(buffer, count, options);
});
}
IAsyncOperationWithProgress<uint, uint> IOutputStream.WriteAsync(IBuffer buffer)
{
throw new NotImplementedException();
}
IAsyncOperation<bool> IOutputStream.FlushAsync()
{
throw new NotImplementedException();
}
#endregion
}
} |
Beta Was this translation helpful? Give feedback.
-
This worked for me. But I could not compile your program as-is, because you have a custom implementation of IAsyncOperationWithProgress, and I could not find a suitable replacement for it. But three was no need for this class by moving the code around a little bit: With those changes, I used your class to open a file. I used the implementation of IRandomAccessStreamWithContentType to set it as the source of a MediaPlayer, like this: I tried playing some very large files and was able to seek very quickly to a position near the end of the file. I also set a breakpoint in the implementation of IRandomAccessStream.Seek in your class and it got hit in the debugger. |
Beta Was this translation helpful? Give feedback.
-
Thanks for rearranging. I wasn't happy about the need of a custom |
Beta Was this translation helpful? Give feedback.
-
@aklemets Have you tried a corrupt video file? It seems that some exceptions are not caught properly in the MP4 decoder. And do you by any chance know which MP4 profiles are supported by the protected path player? |
Beta Was this translation helpful? Give feedback.
-
Getting back to this issue, here is a playback log, where the player just hangs when playing a video file called "WPA`". I can reproduce it in my development environment (sometimes), but I don't know what to check for further.
And then it froze. |
Beta Was this translation helpful? Give feedback.
-
I assign a home-made stream to
MediaPlayerElement.Source
. To be more exact, the stream ID is assigned to aMediaBinder
, which is wrapped into aMediaSource
, which is wrapped into aMediaPlaybackItem
, and a bunch of those items are added to aMediaPlaybackList
, which is finally assigned toMediaPlayerElement.Source
.When I start the playback, I can trace that the player seeks close to the end of the file, so I assume it reads the MP4 look-up table - and knows it can seek in the stream. But when I click on the time bar in order to seek forward during the playback, the player starts reading the data sequentially towards the destination, instead of seeking directly to the destination offset - which it should know from the look-up table. This takes really long time (seeking 1 hour video file in ~256KB blocks takes a noticeable amount of seconds).
I assume I am missing something in the setup. Could you tell me, under which conditions the player reads sequentially instead of seeking, or what I can do to make it use seek?
Beta Was this translation helpful? Give feedback.
All reactions