Skip to content

Commit a8adbed

Browse files
committed
Rearranged closing process to more closely match how the FFMPEG code works to try and fix some random crashes
1 parent 52179d5 commit a8adbed

1 file changed

Lines changed: 117 additions & 79 deletions

File tree

VideoUtils.pas

Lines changed: 117 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,18 @@ procedure _LogCallback(avcl: Pointer; level: Integer; const fmt: PAnsiChar; vl:
8686
begin
8787
if level <= AV_LOG_INFO then
8888
begin
89-
if Assigned(avcl) and (AnsiString(PAVClass(PPointer(avcl)^)^.class_name) = 'AVFormatContext') then
90-
begin
91-
if Assigned(PAVFormatContext(Avcl)^.pb) and Assigned(PAVFormatContext(Avcl)^.pb.opaque) then
89+
try
90+
if Assigned(avcl) and (AnsiString(PAVClass(PPointer(avcl)^)^.class_name) = 'AVFormatContext') then
9291
begin
93-
lInt := 1;
94-
av_log_format_line(avcl, level, fmt, vl, @lBuf, SizeOf(lBuf)-1, @lint);
95-
TVideoConverter(PAVFormatContext(Avcl)^.pb.opaque).LogFmt('%s',[String(AnsiString(lBuf))]);
92+
if Assigned(PAVFormatContext(Avcl)^.pb) and Assigned(PAVFormatContext(Avcl)^.pb.opaque) then
93+
begin
94+
lInt := 1;
95+
av_log_format_line(avcl, level, fmt, vl, @lBuf, SizeOf(lBuf)-1, @lint);
96+
TVideoConverter(PAVFormatContext(Avcl)^.pb.opaque).LogFmt('%s',[String(AnsiString(lBuf))]);
97+
end;
9698
end;
99+
except
100+
// Eat exception
97101
end;
98102
end;
99103
end;
@@ -116,32 +120,39 @@ procedure TVideoConverter.Open;
116120
var
117121
lRet: Integer;
118122
begin
119-
fInputFormatContext := avformat_alloc_context;
120-
fInputFormatContext.pb := avio_alloc_context(av_malloc(cConverterPacketSize), cConverterPacketSize, 0, Self, _ReadPacket, nil, nil);
121-
fInputFormatContext.interrupt_callback.opaque := Self;
122-
fInputFormatContext.interrupt_callback.callback := _InterruptCallback;
123+
try
124+
fInputFormatContext := avformat_alloc_context;
125+
fInputFormatContext.pb := avio_alloc_context(av_malloc(cConverterPacketSize), cConverterPacketSize, 0, Self, _ReadPacket, nil, nil);
126+
fInputFormatContext.interrupt_callback.opaque := Self;
127+
fInputFormatContext.interrupt_callback.callback := _InterruptCallback;
123128

124-
lRet := avformat_open_input(@fInputFormatContext, nil, nil, nil);
125-
if lRet < 0 then
126-
ErrorFmt('Could not open input: %s',[GetErrorStr(lRet)]);
129+
lRet := avformat_open_input(@fInputFormatContext, nil, nil, nil);
130+
if lRet < 0 then
131+
ErrorFmt('Could not open input: %s',[GetErrorStr(lRet)]);
127132

128-
lRet := avformat_find_stream_info(fInputFormatContext, nil);
129-
if (lRet < 0) or (not Assigned(fInputFormatContext.iformat)) then
130-
ErrorFmt('Failed to retrieve input stream information: %s',[GetErrorStr(lRet)]);
133+
lRet := avformat_find_stream_info(fInputFormatContext, nil);
134+
if (lRet < 0) or (not Assigned(fInputFormatContext.iformat)) then
135+
ErrorFmt('Failed to retrieve input stream information: %s',[GetErrorStr(lRet)]);
131136

132-
// av_dump_format(ifmt_ctx, 0, in_filename, 0);
137+
// av_dump_format(ifmt_ctx, 0, in_filename, 0);
133138

134-
avformat_alloc_output_context2(@fOutputFormatContext, nil, fInputFormatContext.iformat.name, nil);
135-
if not Assigned(fOutputFormatContext) then
136-
ErrorFmt('Could not create output context: %s',[GetErrorStr(lRet)]);
139+
avformat_alloc_output_context2(@fOutputFormatContext, nil, fInputFormatContext.iformat.name, nil);
140+
if not Assigned(fOutputFormatContext) then
141+
ErrorFmt('Could not create output context: %s',[GetErrorStr(lRet)]);
137142

138-
fOutputFormatContext.pb := avio_alloc_context(av_malloc(cConverterPacketSize), cConverterPacketSize, 1, Self, nil, _WritePacket, nil);
143+
fOutputFormatContext.pb := avio_alloc_context(av_malloc(cConverterPacketSize), cConverterPacketSize, 1, Self, nil, _WritePacket, nil);
139144

140-
SetupStreams;
145+
SetupStreams;
141146

142-
lRet := avformat_write_header(fOutputFormatContext, nil);
143-
if lRet < 0 then
144-
ErrorFmt('Error occurred writing output header: %s', [GetErrorStr(lRet)]);
147+
lRet := avformat_write_header(fOutputFormatContext, nil);
148+
if lRet < 0 then
149+
ErrorFmt('Error occurred writing output header: %s', [GetErrorStr(lRet)]);
150+
except
151+
on e: EVideoConverterError do
152+
raise;
153+
on e: Exception do
154+
ErrorFmt('Error during open: %s', [e.Message]);
155+
end;
145156
end;
146157

147158
function TVideoConverter.ReadPacket(const buf: PByte;
@@ -161,57 +172,64 @@ procedure TVideoConverter.SetupStreams;
161172
lInStream, lOutStream: PAVStream;
162173
lInCodecParams: PAVCodecParameters;
163174
begin
164-
lStreamIndex := 0;
165-
166-
SetLength(fStreamMapping, fInputFormatContext.nb_streams);
167-
SetLength(fStreamInputDTS, Length(fStreamMapping));
175+
try
176+
lStreamIndex := 0;
168177

169-
for i := 0 to High(fStreamMapping) do
170-
begin
171-
lInStream := PPtrIdx(fInputFormatContext.streams, i);
172-
lInCodecParams := lInStream.codecpar;
178+
SetLength(fStreamMapping, fInputFormatContext.nb_streams);
179+
SetLength(fStreamInputDTS, Length(fStreamMapping));
173180

174-
if fProgramFilter > -1 then
181+
for i := 0 to High(fStreamMapping) do
175182
begin
176-
if avformat_match_stream_specifier(fInputFormatContext, lInStream, PAnsiChar(AnsiString('p:'+IntToStr(fProgramFilter)))) = 0 then
183+
lInStream := PPtrIdx(fInputFormatContext.streams, i);
184+
lInCodecParams := lInStream.codecpar;
185+
186+
if fProgramFilter > -1 then
187+
begin
188+
if avformat_match_stream_specifier(fInputFormatContext, lInStream, PAnsiChar(AnsiString('p:'+IntToStr(fProgramFilter)))) = 0 then
189+
begin
190+
fStreamMapping[i] := -1;
191+
Continue;
192+
end;
193+
end;
194+
195+
if (lInCodecParams.codec_type <> AVMEDIA_TYPE_AUDIO) and
196+
(lInCodecParams.codec_type <> AVMEDIA_TYPE_VIDEO) and
197+
(lInCodecParams.codec_type <> AVMEDIA_TYPE_SUBTITLE) then
177198
begin
178199
fStreamMapping[i] := -1;
179200
Continue;
180201
end;
181-
end;
182202

183-
if (lInCodecParams.codec_type <> AVMEDIA_TYPE_AUDIO) and
184-
(lInCodecParams.codec_type <> AVMEDIA_TYPE_VIDEO) and
185-
(lInCodecParams.codec_type <> AVMEDIA_TYPE_SUBTITLE) then
186-
begin
187-
fStreamMapping[i] := -1;
188-
Continue;
189-
end;
203+
fStreamInputDTS[i] := AV_NOPTS_VALUE;
190204

191-
fStreamInputDTS[i] := AV_NOPTS_VALUE;
205+
fStreamMapping[i] := lStreamIndex;
206+
Inc(lStreamIndex);
192207

193-
fStreamMapping[i] := lStreamIndex;
194-
Inc(lStreamIndex);
208+
lOutStream := avformat_new_stream(fOutputFormatContext, nil);
209+
if not Assigned(lOutStream) then
210+
ErrorFmt('Failed allocating output stream', []);
195211

196-
lOutStream := avformat_new_stream(fOutputFormatContext, nil);
197-
if not Assigned(lOutStream) then
198-
ErrorFmt('Failed allocating output stream', []);
212+
// lOutStream.id := lInStream.id; // Causes black screen
213+
lOutStream.time_base := lInStream.time_base;
214+
lOutStream.avg_frame_rate := lInStream.avg_frame_rate;
199215

200-
// lOutStream.id := lInStream.id; // Causes black screen
201-
lOutStream.time_base := lInStream.time_base;
202-
lOutStream.avg_frame_rate := lInStream.avg_frame_rate;
216+
// Copy metadata (which includes audio language info)
217+
if Assigned(lInStream.metadata) then
218+
av_dict_copy(@lOutStream.metadata, lInStream.metadata, 0);
203219

204-
// Copy metadata (which includes audio language info)
205-
if Assigned(lInStream.metadata) then
206-
av_dict_copy(@lOutStream.metadata, lInStream.metadata, 0);
220+
lRet := avcodec_parameters_copy(lOutStream.codecpar, lInCodecParams);
221+
if lRet < 0 then
222+
ErrorFmt('Failed to copy codec parameters: %s', [GetErrorStr(lRet)]);
207223

208-
lRet := avcodec_parameters_copy(lOutStream.codecpar, lInCodecParams);
209-
if lRet < 0 then
210-
ErrorFmt('Failed to copy codec parameters: %s', [GetErrorStr(lRet)]);
211-
212-
lOutStream.codecpar.codec_tag.tag := 0;
224+
lOutStream.codecpar.codec_tag.tag := 0;
225+
end;
226+
// av_dump_format(ofmt_ctx, 0, nil, 1);
227+
except
228+
on e: EVideoConverterError do
229+
raise;
230+
on e: Exception do
231+
ErrorFmt('Error during setup: %s', [e.Message]);
213232
end;
214-
// av_dump_format(ofmt_ctx, 0, nil, 1);
215233
end;
216234

217235
function TVideoConverter.WritePacket(const buf: PByte;
@@ -226,34 +244,54 @@ function TVideoConverter.WritePacket(const buf: PByte;
226244
end;
227245

228246
procedure TVideoConverter.Close;
247+
var
248+
lAVIOContext: PAVIOContext;
229249
begin
230250
fFinished := True;
251+
try
252+
if Assigned(fOutputFormatContext) then
253+
av_write_trailer(fOutputFormatContext);
231254

232-
if Assigned(fOutputFormatContext) then
233-
av_write_trailer(fOutputFormatContext);
255+
// NOTE: FFMPEG code internally frees avformat context before closing avio
234256

235-
if Assigned(fOutputFormatContext) then
236-
begin
237-
av_freep(@fOutputFormatContext.pb.buffer);
238-
avio_context_free(@fOutputFormatContext.pb);
257+
if Assigned(fInputFormatContext) then
258+
begin
259+
lAVIOContext := fInputFormatContext.pb;
239260

240-
avformat_free_context(fOutputFormatContext);
261+
// This flushes packet queue and calls avformat_free_context. It does not
262+
// call avio_close if context.flags contains AVFMT_FLAG_CUSTOM_IO
263+
avformat_close_input(@fInputFormatContext);
241264

242-
fOutputFormatContext := nil;
243-
end;
265+
// avio_close calls avio_flush, frees the buffer, and calls avio_context_free,
266+
// but can only be used if avio_open was called.
267+
avio_flush(lAVIOContext);
268+
av_freep(@lAVIOContext.buffer);
269+
avio_context_free(@lAVIOContext);
244270

245-
if Assigned(fInputFormatContext) then
246-
begin
247-
av_freep(@fInputFormatContext.pb.buffer);
248-
avio_context_free(@fInputFormatContext.pb);
271+
fInputFormatContext := nil;
272+
end;
273+
274+
if Assigned(fOutputFormatContext) then
275+
begin
276+
lAVIOContext := fOutputFormatContext.pb;
249277

250-
avformat_close_input(@fInputFormatContext);
278+
avformat_free_context(fOutputFormatContext);
251279

252-
fInputFormatContext := nil;
253-
end;
280+
// avio_close calls avio_flush, frees the buffer, and calls avio_context_free,
281+
// but can only be used if avio_open was called.
282+
avio_flush(lAVIOContext);
283+
av_freep(@lAVIOContext.buffer);
284+
avio_context_free(@lAVIOContext);
254285

255-
fStreamMapping := nil;
256-
fStreamInputDTS := nil;
286+
fOutputFormatContext := nil;
287+
end;
288+
289+
fStreamMapping := nil;
290+
fStreamInputDTS := nil;
291+
except
292+
on e: Exception do
293+
ErrorFmt('Error during close: %s', [e.Message]);
294+
end;
257295
end;
258296

259297
constructor TVideoConverter.Create;

0 commit comments

Comments
 (0)