Skip to content

Commit 4460f7b

Browse files
committed
feat: add auto-detected decoder, move ffmpeg configure to pre-built, optimize capture
1 parent 2411937 commit 4460f7b

File tree

17 files changed

+605
-518
lines changed

17 files changed

+605
-518
lines changed

StreamingCaptureBot.Core/Bots/LagrangeBot/LagrangeHost.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,6 @@ Task IHostedLifecycleService.StoppingAsync(CancellationToken cancellationToken)
341341
Task IHostedService.StopAsync(CancellationToken cancellationToken)
342342
=> Task.CompletedTask;
343343

344-
345344
Task IHostedLifecycleService.StoppedAsync(CancellationToken cancellationToken)
346345
=> Task.CompletedTask;
347346

StreamingCaptureBot.Core/Configs/StreamOption.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public record StreamOption
44
{
55
public string? FfMpegLibrariesPath { get; set; } = null;
66
public required Uri Url { get; set; }
7+
public int StreamIndex { get; set; } = -1;
78
public TimeSpan CacheTimeout { get; set; } = TimeSpan.FromSeconds(15);
89
public uint ConnectTimeout { get; set; } = 0;
910
public TimeSpan CodecTimeout { get; set; } = TimeSpan.FromSeconds(12);

StreamingCaptureBot.Core/Controllers/BotController.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,16 @@ namespace StreamingCaptureBot.Core.Controllers;
44

55
public class BotController(
66
ILogger<BotController> logger,
7-
IServiceProvider services
7+
CaptureService captureService
88
)
99
{
10-
private CaptureService? _captureService;
11-
1210
public async Task<ActionResult> HandleCaptureImageCommand(BotRequest request)
1311
{
14-
_captureService ??= services.GetRequiredService<CaptureService>();
1512
var response = new ActionResult();
1613

1714
try
1815
{
19-
var (result, image) = await _captureService.CaptureImageAsync();
16+
var (result, image) = await captureService.CaptureImageAsync();
2017

2118
if (!result || image is null)
2219
{
@@ -37,7 +34,7 @@ public async Task<ActionResult> HandleCaptureImageCommand(BotRequest request)
3734
}
3835
finally
3936
{
40-
await _captureService.FlushDecoderBufferAsync(CancellationToken.None);
37+
await captureService.FlushDecoderBufferAsync(CancellationToken.None);
4138
}
4239

4340
return response;

StreamingCaptureBot.Core/FfMpeg.Net/Codecs/CodecBase.cs

Lines changed: 0 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using FFmpeg.AutoGen.Abstractions;
33
using StreamingCaptureBot.Core.FfMpeg.Net.DataStructs;
44
using StreamingCaptureBot.Core.FfMpeg.Net.Extensions;
5-
using StreamingCaptureBot.Core.FfMpeg.Net.Utils;
65
using StreamingCaptureBot.Core.Utils;
76

87
namespace StreamingCaptureBot.Core.FfMpeg.Net.Codecs;
@@ -128,109 +127,6 @@ public unsafe Queue<byte[]> Encode(AvFrameWrapper rawFrame)
128127
}
129128
}
130129

131-
public unsafe AvFrameWrapper Decode(AVPacket* packet)
132-
{
133-
throw new NotImplementedException();
134-
var decodeResult = 0;
135-
var frame = new AvFrameWrapper();
136-
137-
// 尝试发送
138-
logger.LogDebug("Try send packet to decoder.");
139-
var sendResult = ffmpeg.avcodec_send_packet(CodecCtx, packet);
140-
141-
if (sendResult == ffmpeg.AVERROR(ffmpeg.EAGAIN))
142-
{
143-
// reference:
144-
// * tree/release/6.1/fftools/ffmpeg_dec.c:567
145-
// 理论上不会出现 EAGAIN
146-
147-
logger.LogWarning(
148-
"Receive {error} after sent, this could be cause by ffmpeg bug or some reason, ignored this message.",
149-
nameof(ffmpeg.EAGAIN));
150-
sendResult = 0;
151-
}
152-
153-
if (sendResult == 0 || sendResult == ffmpeg.AVERROR_EOF)
154-
{
155-
// 发送成功
156-
logger.LogDebug("PacketBuffer sent success, try get decoded frame.");
157-
// 获取解码结果
158-
decodeResult = ffmpeg.avcodec_receive_frame(CodecCtx, frame.UnmanagedPointer);
159-
}
160-
else
161-
{
162-
var error = new ApplicationException(FfMpegExtension.av_strerror(sendResult));
163-
164-
// 无法处理的发送失败
165-
logger.LogError(error, "Send packet to decoder failed.\n");
166-
167-
throw error;
168-
}
169-
170-
var scope = logger.BeginScope($"Frame@0x{frame.UnmanagedPointer->GetHashCode():x8}");
171-
172-
if (decodeResult < 0)
173-
{
174-
// 错误处理
175-
ApplicationException error;
176-
var message = FfMpegExtension.av_strerror(decodeResult);
177-
178-
if (decodeResult == ffmpeg.AVERROR_EOF)
179-
{
180-
// reference:
181-
// * https://ffmpeg.org/doxygen/6.1/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c
182-
// > the codec has been fully flushed, and there will be no more output frames
183-
// 理论上不会出现 EOF
184-
message =
185-
"the codec has been fully flushed, and there will be no more output frames.";
186-
187-
error = new(message);
188-
189-
logger.LogError(error, "Received EOF from decoder.\n");
190-
}
191-
else if (decodeResult == ffmpeg.AVERROR(ffmpeg.EAGAIN))
192-
{
193-
// reference:
194-
// * tree/release/6.1/fftools/ffmpeg_dec.c:596
195-
// * https://ffmpeg.org/doxygen/6.1/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c
196-
// > output is not available in this state - user must try to send new input
197-
// 理论上不会出现 EAGAIN
198-
message =
199-
"output is not available in this state - user must try to send new input";
200-
201-
//if (_streamOption.KeyFrameOnly)
202-
//{
203-
// // 抛出异常,仅关键帧模式中,该错误不可能通过发送更多需要的包来解决
204-
// error = new(message);
205-
206-
// _logger.LogError(error, "Received EAGAIN from decoder.\n");
207-
// throw error;
208-
//}
209-
210-
// 忽略错误,发送下一个包进行编码,可能足够的包进入解码器可以解决
211-
logger.LogWarning("Receive EAGAIN from decoder, retry.");
212-
// continue;
213-
}
214-
else
215-
{
216-
error = new(message);
217-
logger.LogError(error, "Uncaught error occured during decoding.\n");
218-
throw error;
219-
}
220-
}
221-
222-
// 解码正常
223-
logger.LogInformation("Decode frame success. type {type}, pts {pts}.",
224-
frame.UnmanagedPointer->pict_type.ToString(),
225-
TimeSpanUtil.FromFfmpeg(frame.UnmanagedPointer->pts, CodecCtx->time_base).ToString("c"));
226-
227-
}
228-
229-
public AvFrameWrapper Decode(Queue<byte[]> binaryQueue)
230-
{
231-
throw new NotImplementedException();
232-
}
233-
234130
private unsafe int ReceivePacket()
235131
{
236132
PacketBuffer.Reset();
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using FFmpeg.AutoGen.Abstractions;
2+
using StreamingCaptureBot.Core.FfMpeg.Net.DataStructs;
3+
using StreamingCaptureBot.Core.FfMpeg.Net.Extensions;
4+
5+
namespace StreamingCaptureBot.Core.FfMpeg.Net.Codecs;
6+
7+
public abstract class DecoderBase(ILogger logger, DecoderContext ctx) : IDisposable
8+
{
9+
public DecoderContext Context => ctx;
10+
public bool Opened { get; private set; }
11+
12+
protected DecoderBase(ILogger logger, string name)
13+
: this(logger, new DecoderContext(name)) { }
14+
15+
protected DecoderBase(ILogger logger, AVCodecID decoderId)
16+
: this(logger, new DecoderContext(decoderId)) { }
17+
18+
protected unsafe DecoderBase(ILogger logger, AVCodec* decoder)
19+
: this(logger, new DecoderContext(decoder)) { }
20+
21+
protected unsafe DecoderBase(ILogger logger, AVCodecContext* unmanagedCtx)
22+
: this(logger, new DecoderContext(unmanagedCtx)) { }
23+
24+
public bool ConfigureAndOpen(Action<DecoderContext> config, IDictionary<string, string>? options = null)
25+
{
26+
if (Opened)
27+
return false;
28+
29+
config.Invoke(Context);
30+
31+
unsafe
32+
{
33+
AVDictionary* openOptions = null;
34+
35+
if (options is null)
36+
return Context.TryOpen(Context.UnmanagedPointer->codec
37+
, &openOptions) == 0;
38+
39+
foreach (var (key, value) in options)
40+
ffmpeg.av_dict_set(&openOptions, key, value, 0x0);
41+
42+
Opened =
43+
Context.TryOpen(Context.UnmanagedPointer->codec, &openOptions) == 0;
44+
45+
return Opened;
46+
}
47+
}
48+
49+
public void Decode(AvPacketWrapper packet, ref AvFrameWrapper frame)
50+
{
51+
int decodeResult;
52+
53+
// 尝试发送
54+
logger.LogDebug("Try send packet to decoder.");
55+
56+
var sendResult = ctx.TrySendPacket(packet);
57+
if (sendResult == ffmpeg.AVERROR(ffmpeg.EAGAIN))
58+
{
59+
// reference:
60+
// * tree/release/6.1/fftools/ffmpeg_dec.c:567
61+
// 理论上不会出现 EAGAIN
62+
63+
logger.LogWarning(
64+
"Receive {error} after sent, this could be cause by ffmpeg bug or some reason, ignored this message.",
65+
nameof(ffmpeg.EAGAIN));
66+
sendResult = 0;
67+
}
68+
69+
if (sendResult == 0 || sendResult == ffmpeg.AVERROR_EOF)
70+
{
71+
// 发送成功
72+
logger.LogDebug("PacketBuffer sent success, try get decoded frame.");
73+
// 获取解码结果
74+
decodeResult = ctx.TryReceivedFrame(ref frame);
75+
}
76+
else
77+
{
78+
var error = new ApplicationException(FfMpegExtension.av_strerror(sendResult));
79+
80+
// 无法处理的发送失败
81+
logger.LogError(error, "Send packet to decoder failed.\n");
82+
83+
throw error;
84+
}
85+
86+
var scope = logger.BeginScope(frame.ToString());
87+
88+
if (decodeResult < 0)
89+
{
90+
// 错误处理
91+
ApplicationException error;
92+
var message = FfMpegExtension.av_strerror(decodeResult);
93+
94+
if (decodeResult == ffmpeg.AVERROR_EOF)
95+
{
96+
// reference:
97+
// * https://ffmpeg.org/doxygen/6.1/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c
98+
// > the codec has been fully flushed, and there will be no more output frames
99+
// 理论上不会出现 EOF
100+
message =
101+
"the codec has been fully flushed, and there will be no more output frames.";
102+
103+
error = new(message);
104+
105+
logger.LogError(error, "Received EOF from decoder.\n");
106+
}
107+
else if (decodeResult == ffmpeg.AVERROR(ffmpeg.EAGAIN))
108+
{
109+
// reference:
110+
// * tree/release/6.1/fftools/ffmpeg_dec.c:596
111+
// * https://ffmpeg.org/doxygen/6.1/group__lavc__decoding.html#ga11e6542c4e66d3028668788a1a74217c
112+
// > output is not available in this state - user must try to send new input
113+
// 理论上不会出现 EAGAIN
114+
message =
115+
"output is not available in this state - user must try to send new input";
116+
117+
//if (_streamOption.KeyFrameOnly)
118+
//{
119+
// // 抛出异常,仅关键帧模式中,该错误不可能通过发送更多需要的包来解决
120+
// error = new(message);
121+
122+
// _logger.LogError(error, "Received EAGAIN from decoder.\n");
123+
// throw error;
124+
//}
125+
126+
// 忽略错误,发送下一个包进行编码,可能足够的包进入解码器可以解决
127+
logger.LogWarning("Receive EAGAIN from decoder, retry.");
128+
// continue;
129+
}
130+
else
131+
{
132+
error = new(message);
133+
logger.LogError(error, "Uncaught error occured during decoding.\n");
134+
throw error;
135+
}
136+
}
137+
138+
// 解码正常
139+
logger.LogInformation("Decode frame success. type {type}, pts {pts}.",
140+
frame.PictureType.ToString(),
141+
frame.GetPresentationTimeSpan(ctx.TimeBase).ToString("c"));
142+
}
143+
144+
public void Dispose()
145+
{
146+
Context.Dispose();
147+
GC.SuppressFinalize(this);
148+
}
149+
}

0 commit comments

Comments
 (0)