@@ -86,14 +86,18 @@ procedure _LogCallback(avcl: Pointer; level: Integer; const fmt: PAnsiChar; vl:
8686begin
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 ;
99103end ;
@@ -116,32 +120,39 @@ procedure TVideoConverter.Open;
116120var
117121 lRet: Integer;
118122begin
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 ;
145156end ;
146157
147158function TVideoConverter.ReadPacket (const buf: PByte;
@@ -161,57 +172,64 @@ procedure TVideoConverter.SetupStreams;
161172 lInStream, lOutStream: PAVStream;
162173 lInCodecParams: PAVCodecParameters;
163174begin
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);
215233end ;
216234
217235function TVideoConverter.WritePacket (const buf: PByte;
@@ -226,34 +244,54 @@ function TVideoConverter.WritePacket(const buf: PByte;
226244end ;
227245
228246procedure TVideoConverter.Close ;
247+ var
248+ lAVIOContext: PAVIOContext;
229249begin
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 ;
257295end ;
258296
259297constructor TVideoConverter.Create;
0 commit comments