-
Notifications
You must be signed in to change notification settings - Fork 46
Description
I show you the current test code with a WinForms project:
using HLSDownload.Converter;
using HLSDownload.Grabber;
using HLSDownload.Grabber.Grabbed;
using HLSDownload.HLS;
using System.Diagnostics;
namespace HLSDownloadTestForm
{
public partial class Form1 : Form
{
private const string ffmpeg_path =
@"C:\Program Files\ffmpeg\ffmpeg.exe";
private const string output_path =
@"G:\VideoFiles";
private const string m3u8_url1 =
@"https://content.jwplatform.com/manifests/yp34SRmf.m3u8";
private static readonly IGrabber _grabber;
private static readonly HttpClient _client =
new();
public Form1()
{
InitializeComponent();
}
static Form1()
{
IMultiGrabber grabber = GrabberBuilder.New()
.UseDefaultServices()
.AddHls()
.Build();
_grabber = grabber;
}
public static void DeleteTSFiles()
{
string directoryPath = Path.GetTempPath();
IEnumerable<string> tsFiles =
Directory.EnumerateFiles(directoryPath, "*.ts");
foreach (string file in tsFiles)
{
try
{
File.Delete(file);
}
catch (Exception ex)
{
Debug.Print($"Unable to delete file {file}. Error: {ex.Message}");
continue;
}
}
}
private static async Task Grab(Uri uri)
{
Debug.Print($"Grabbing from {uri}...");
GrabResult grabResult =
await _grabber.GrabAsync(uri).ConfigureAwait(false);
GrabbedHlsStreamReference reference =
grabResult.Resource<GrabbedHlsStreamReference>();
if (grabResult == null)
{
Debug.Print($"GrabResult is null for URI: {uri}");
return;
}
if (reference != null)
{
// Redirect to an M3U8 playlist
await Grab(reference.ResourceUri);
return;
}
GrabbedHlsStreamMetadata[] metadataResources =
grabResult.Resources<GrabbedHlsStreamMetadata>().ToArray();
if (metadataResources.Length > 0)
{
// Description for one or more M3U8 playlists
GrabbedHlsStreamMetadata selection;
if (metadataResources.Length == 1)
{
selection = metadataResources.Single();
}
else
{
Debug.Print("");
Debug.Print("=== Streams ===");
selection = metadataResources[int.Parse("0")];
}
// Get information from the HLS stream
GrabbedHlsStream grabbedStream = await selection.Stream.Value;
await Grab(grabbedStream, selection, grabResult);
return;
}
throw new Exception("Could not grab the HLS stream.");
}
private static async Task Grab(GrabbedHlsStream stream,
GrabbedHlsStreamMetadata metadata, GrabResult grabResult)
{
Debug.Print("");
Debug.Print("=== Downloading ===");
Debug.Print("{0} segments", stream.Segments.Count);
Debug.Print("Duration: {0}", stream.Length);
List<string> tempFiles = [];
try
{
for (int i = 0; i < stream.Segments.Count; i++)
{
MediaSegment segment = stream.Segments[i];
Debug.Write($"Downloading segment #{i + 1} {segment.Title}...");
string outputPath = Path.GetTempFileName();
tempFiles.Add(outputPath);
using Stream responseStream =
await _client.GetStreamAsync(segment.Uri);
using Stream inputStream =
await grabResult.WrapStreamAsync(responseStream);
using FileStream outputStream = new(outputPath, FileMode.Create);
await inputStream.CopyToAsync(outputStream);
Debug.Print(" OK");
}
CreateOutputFile(tempFiles, metadata);
}
finally
{
foreach (string tempFile in tempFiles)
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
Debug.Print("Cleaned up temp files.");
}
}
private static void CreateOutputFile(List<string> tempFiles, GrabbedHlsStreamMetadata metadata)
{
Debug.Print("All segments were downloaded successfully.");
MediaConcatenator concatenator = new(output_path)
{
OutputMimeType = metadata.OutputFormat.Mime,
OutputExtension = metadata.OutputFormat.Extension,
};
foreach (string tempFile in tempFiles)
concatenator.AddSource(tempFile);
concatenator.Build();
Debug.Print("Output file created successfully!");
}
private async void BTNDownload_Click(object sender, EventArgs e)
{
DeleteTSFiles();
FFmpeg.AutoGen.ffmpeg.RootPath = ffmpeg_path;
await Grab(new Uri(m3u8_url1));
}
private void BTNFinish_Click(object sender, EventArgs e)
{
Console.Beep();
Environment.Exit(0);
}
}
}
=>
I show you the debug output from this WinForms test code:
OK, I changed the class library, and it got compiled, but when I run the test code, I got the same error:
System.Reflection.TargetInvocationException
HResult=0x80131604
Message=Exception has been thrown by the target of an invocation.
Source=System.Private.CoreLib
StackTrace:
at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in System.Reflection\MethodBaseInvoker.cs:line 117
at System.Delegate.DynamicInvokeImpl(Object[] args) in System\Delegate.cs:line 55
at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) in System.Threading\ExecutionContext.cs:line 154
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) in System.Threading\ExecutionContext.cs:line 154
at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(HWND hWnd, MessageId msg, WPARAM wparam, LPARAM lparam)
at Windows.Win32.PInvoke.DispatchMessage(MSG* lpMsg)
at System.Windows.Forms.Application.ComponentManager.Microsoft.Office.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context)
at HLSDownloadTestForm.Program.Main() in D:\RaceOCR\HLSVideo\HLSDownloadTestForm\HLSDownloadTestForm\Program.cs:line 14
This exception was originally thrown at this call stack:
FFmpeg.AutoGen.DynamicallyLoadedBindings.Initialize.AnonymousMethod__2_1320(FFmpeg.AutoGen.AVIOContext**, string, int, FFmpeg.AutoGen.AVIOInterruptCB*, FFmpeg.AutoGen.AVDictionary**)
FFmpeg.AutoGen.DynamicallyLoadedBindings.Initialize.AnonymousMethod__2_609(FFmpeg.AutoGen.AVIOContext**, string, int, FFmpeg.AutoGen.AVIOInterruptCB*, FFmpeg.AutoGen.AVDictionary**)
FFmpeg.AutoGen.ffmpeg.avio_open2(FFmpeg.AutoGen.AVIOContext**, string, int, FFmpeg.AutoGen.AVIOInterruptCB*, FFmpeg.AutoGen.AVDictionary**)
HLSDownload.Converter.IOContext.IOContext(string, int) in IOContext.cs
HLSDownload.Converter.MediaConcatenator.Build() in MediaConcatenator.cs
HLSDownloadTestForm.Form1.CreateOutputFile(System.Collections.Generic.List, HLSDownload.Grabber.Grabbed.GrabbedHlsStreamMetadata) in Form1.cs
HLSDownloadTestForm.Form1.Grab(HLSDownload.Grabber.Grabbed.GrabbedHlsStream, HLSDownload.Grabber.Grabbed.GrabbedHlsStreamMetadata, HLSDownload.Grabber.GrabResult) in Form1.cs
HLSDownloadTestForm.Form1.Grab(System.Uri) in Form1.cs
HLSDownloadTestForm.Form1.BTNDownload_Click(object, System.EventArgs) in Form1.cs
System.Threading.Tasks.Task.ThrowAsync.AnonymousMethod__128_0(object) in Task.cs
...
[Call Stack Truncated]
Inner Exception 1:
NotSupportedException: Specified method is not supported.
=> I show you the debug output:
Grabbing from https://content.jwplatform.com/manifests/yp34SRmf.m3u8...
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\PrivateAssemblies\Runtime\Microsoft.VisualStudio.Debugger.Runtime.NetCoreApp.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\IntelliTrace\Microsoft.IntelliTrace.TelemetryObserver.Common.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\IntelliTrace\Microsoft.IntelliTrace.TelemetryObserver.CoreClr.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Reflection.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Reflection.Extensions.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Runtime.Extensions.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Net.Sockets.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Net.NameResolution.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Threading.ThreadPool.dll'.
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.4\System.Runtime.Intrinsics.dll'.
=== Streams ===
=== Downloading ===
7 segments
Duration: 00:00:25.5000000
Downloading segment #1 no desc... OK
Downloading segment #2 no desc... OK
Downloading segment #3 no desc... OK
Downloading segment #4 no desc... OK
Downloading segment #5 no desc... OK
Downloading segment #6 no desc... OK
Downloading segment #7 no desc... OK
'HLSDownloadTestForm.exe' (CoreCLR: clrhost): Loaded 'D:\RaceOCR\HLSVideo\HLSDownloadTestForm\HLSDownloadTestForm\bin\Debug\net8.0-windows\HLSDownload.Converter.dll'. Symbols loaded.
All segments were downloaded successfully.
Exception thrown: 'System.NotSupportedException' in FFmpeg.AutoGen.dll
Cleaned up temp files.
Exception thrown: 'System.NotSupportedException' in System.Private.CoreLib.dll
Exception thrown: 'System.NotSupportedException' in System.Private.CoreLib.dll
Exception thrown: 'System.NotSupportedException' in System.Private.CoreLib.dll
Exception thrown: 'System.Reflection.TargetInvocationException' in System.Private.CoreLib.dll
Exception thrown: 'System.Reflection.TargetInvocationException' in System.Private.CoreLib.dll
Exception thrown: 'System.Reflection.TargetInvocationException' in System.Windows.Forms.dll
An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in System.Windows.Forms.dll
Exception has been thrown by the target of an invocation.
Extracted embedded document "System.Reflection\MethodBaseInvoker.cs" to "C:\Users\John\AppData\Local\Temp.vsdbgsrc\106739112c966cc39d25dd039145ce9de2cff17182935e358a18dd53276b70ce\MethodBaseInvoker.cs"
I can show you some related code for this part: public void Build()
{
AVStream*[] streams = new AVStream*[2];
Dictionary<MediaStreamType, int> stream_dic = [];
ValidateArguments();
// create decoders
Dictionary<MediaStreamType, MediaDecoder> decoders = MakeDecoders();
try
{
// open output iocontext
using IOContext output = new(OutputPath, ffmpeg.AVIO_FLAG_WRITE);
// open muxer
using MediaMuxer muxer = new(output, OutputShortName, OutputMimeType);
// add streams
int index = 0;
foreach (KeyValuePair<MediaStreamType, MediaDecoder> decoderPair in decoders)
{
MediaDecoder decoder = decoderPair.Value;
AVCodecID targetCodec = decoder.CodecId;
AVCodecContext* decoderCodec = decoder.CodecContext;
switch (decoderPair.Key)
{
case MediaStreamType.Audio:
if (TargetAudioCodec != null)
targetCodec = TargetAudioCodec.Value;
break;
case MediaStreamType.Video:
if (TargetVideoCodec != null)
targetCodec = TargetVideoCodec.Value;
break;
}
AVCodec* encoder = ffmpeg.avcodec_find_encoder(targetCodec);
AVStream* outStream = muxer.AddStream(encoder);
AVCodecParameters* param = outStream->codecpar;
streams[index] = outStream;
stream_dic.Add(decoderPair.Key, index++);
AVCodecContext* codecContext = ffmpeg.avcodec_alloc_context3(encoder);
if (codecContext == null)
{
// Handle error
}
if (ffmpeg.avcodec_parameters_to_context(codecContext, outStream->codecpar) < 0)
{
// Handle error
}
if (decoder.CodecId == targetCodec)
{
// converting to the same codec
ffmpeg.avcodec_parameters_from_context(param, codecContext).ThrowOnError();
}
else
{
// converting to another codec
switch (decoderPair.Key)
{
case MediaStreamType.Audio:
param->codec_id = targetCodec;
param->codec_type = AVMediaType.AVMEDIA_TYPE_AUDIO;
param->sample_rate = decoderCodec->sample_rate;
outStream->time_base = decoderCodec->time_base;
break;
case MediaStreamType.Video:
throw new NotSupportedException();
}
}
outStream->codecpar->codec_tag = 0;
}
// write headers
muxer.WriteHeader();
// write packets
long audio_dts = ffmpeg.AV_NOPTS_VALUE;
long video_dts = ffmpeg.AV_NOPTS_VALUE;
long audio_pts = 0, video_pts = 0;
KeyValuePair<MediaStreamType, MediaDecoder> audio_stream =
decoders.Where(pair => pair.Key == MediaStreamType.Audio).First();
KeyValuePair<MediaStreamType, MediaDecoder> video_stream =
decoders.Where(pair => pair.Key == MediaStreamType.Video).First();
bool any_audio = true, any_video = true;
while (true)
{
bool anyPacket = false;
while (any_audio || any_video)
{
KeyValuePair<MediaStreamType, MediaDecoder> decoderPair;
// choose a decoder pair
if (!any_audio)
decoderPair = video_stream;
else if (!any_video)
decoderPair = audio_stream;
else
{
// choose between audio and video
decoderPair = video_dts < audio_dts ? video_stream : audio_stream;
}
// decoder is chosen now,
// let's read and encode
MediaDecoder decoder = decoderPair.Value;
int stream_index = stream_dic[decoderPair.Key];
AVStream* outputStream = streams[stream_index];
if (decoder.CodecId == outputStream->codecpar->codec_id)
{
// simply copy to target stream
using MediaPacket inputFrame = decoder.ReadPacket();
if (inputFrame == null)
{
if (decoder == audio_stream.Value)
any_audio = false;
else
any_video = false;
continue;
}
AVPacket* pck = inputFrame.Pointer;
ffmpeg.av_packet_rescale_ts(pck, decoder.TimeBase, outputStream->time_base);
pck->stream_index = stream_index;
long* last_dts, last_pts;
switch (decoder.CodecContext->codec_type)
{
case AVMediaType.AVMEDIA_TYPE_AUDIO:
last_dts = &audio_dts;
last_pts = &audio_pts;
break;
case AVMediaType.AVMEDIA_TYPE_VIDEO:
last_dts = &video_dts;
last_pts = &video_pts;
break;
default:
throw new NotSupportedException();
}
if (pck->dts < (*last_dts + ((muxer.FormatContextPtr->oformat->flags & ffmpeg.AVFMT_TS_NONSTRICT) > 0 ? 0 : 1)) && pck->dts != ffmpeg.AV_NOPTS_VALUE && *last_dts != ffmpeg.AV_NOPTS_VALUE)
{
long next_dts = (*last_dts) + 1;
if (pck->pts >= pck->dts && pck->pts != ffmpeg.AV_NOPTS_VALUE)
pck->pts = Math.Max(pck->pts, next_dts);
if (pck->pts == ffmpeg.AV_NOPTS_VALUE)
pck->pts = next_dts;
pck->dts = next_dts;
}
(*last_dts) = pck->dts;
muxer.WritePacket(pck);
anyPacket = true;
}
else
throw new NotSupportedException("Format conversion is not supported.");
}
if (!anyPacket)
break;
}
// write trailer
muxer.WriteTrailer();
}
finally
{
// dispose decoders
foreach (MediaDecoder decoder in decoders.Values)
decoder.Dispose();
}
}
Please advice if you think it is due to the new version of Ffmpeg.AutoGen, the new version is 7.0.0; but your original repo has the Ffmpeg.AutoGen version of 4.4.1.1; I think the new version should be better, since it will be more stable than old version.
But I doubt that the new version has introduced some break changes, so I can't run my test code. Please take a look at the related code to see if you can fix this new issue?
Thanks