[FFmpeg-devel] ffprobe: Do not decode zero-sized packets in ffprobe -show_frame
Petter Ericson
petter.ericson at codemill.se
Tue Feb 21 11:18:44 CET 2012
On Mon, Feb 20, 2012 at 06:00:29PM +0100, Stefano Sabatini wrote:
> On date Thursday 2012-02-16 09:55:56 +0100, Petter Ericson encoded:
> > On Wed, Feb 15, 2012 at 05:39:15PM +0100, Michael Niedermayer wrote:
> > > On Tue, Feb 14, 2012 at 05:26:13PM +0100, Petter Ericson wrote:
> > > > Greetings
> > > >
> > > > Ticket #997 details a segfault in ffprobe that was exposed by the file
> > > > http://titan.codemill.se/~peteri/120210144737.ts
> > > >
> > > > This patch fixes the segfault. However, the desyncing issue that I
> > > > mentioned in the ticket still remains (I was sloppy when looking at the
> > > > output - It is still present in git master). If anyone could advice on what
> > > > it is that is causing transcoding to result in desynced output, I would be
> > > > most grateful.
> > > >
> > > > The patched ffmpeg passes make fate.
> > > >
> > > > Best regards
> > > >
> > > > Petter Ericson
> > > >
> > >
> > > > ffprobe.c | 2 ++
> > > > 1 file changed, 2 insertions(+)
> > > > d4cd04827b5c74b8d5d67f85d3f49b9646ae392d patch
> > > > commit e40952af2f5b6eccf24b34926bf09747117affdc
> > > > Author: Petter Ericson <petter.ericson at codemill.se>
> > > > Date: Tue Feb 14 16:59:56 2012 +0100
> > > >
> > > > ffprobe: Do not avcodec_decode_video2 video packets with size == 0
> > > >
> > > > diff --git a/ffprobe.c b/ffprobe.c
> > > > index 79f7494..e23ef98 100644
> > > > --- a/ffprobe.c
> > > > +++ b/ffprobe.c
> > > > @@ -1349,6 +1349,8 @@ static av_always_inline int get_decoded_frame(AVFormatContext *fmt_ctx,
> > > > *got_frame = 0;
> > > > switch (dec_ctx->codec_type) {
> > > > case AVMEDIA_TYPE_VIDEO:
> > > > + if(pkt->size == 0)
> > > > + return ret;
> > > > ret = avcodec_decode_video2(dec_ctx, frame, got_frame, pkt);
> > >
> > > This also will break flushing the last frames
> > > the check could be moved to the non flushing codepath
> > >
> > > [...]
> > >
> >
> > Ah, so it will. Amended patch attached. The reason why the check can't
> > easily be done in the non-flushing loop is because zero-sized audio packets
> > have to be passed through. One could go with just checking the codec_type,
> > but that would lead to quite the ugly if-condition. Easier to just have a
> > flushing switch to get_decode_frame imo.
> >
> > Still, I have not managed to hit this particular segfault with files from
> > any other source, so it is likely that something weird is going on, anyway,
> > especially considering the timing problems.
> >
> > /P
>
> > commit 95d2777c00ca3f3756aa6090ce900ae5c7b9ca2b
> > Author: Petter Ericson <petter.ericson at codemill.se>
> > Date: Tue Feb 14 16:59:56 2012 +0100
> >
> > ffprobe: Do not avcodec_decode_video2 video packets with size == 0 unless flushing
> >
> > diff --git a/ffprobe.c b/ffprobe.c
> > index 79f7494..b5ca44c 100644
> > --- a/ffprobe.c
> > +++ b/ffprobe.c
> > @@ -1341,7 +1341,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream)
> >
> > static av_always_inline int get_decoded_frame(AVFormatContext *fmt_ctx,
> > AVFrame *frame, int *got_frame,
> > - AVPacket *pkt)
> > + AVPacket *pkt, int flushing)
> > {
> > AVCodecContext *dec_ctx = fmt_ctx->streams[pkt->stream_index]->codec;
> > int ret = 0;
> > @@ -1349,6 +1349,8 @@ static av_always_inline int get_decoded_frame(AVFormatContext *fmt_ctx,
> > *got_frame = 0;
> > switch (dec_ctx->codec_type) {
> > case AVMEDIA_TYPE_VIDEO:
> > + if(!flushing && pkt->size == 0)
> > + return ret;
> > ret = avcodec_decode_video2(dec_ctx, frame, got_frame, pkt);
> > break;
> >
> > @@ -1375,7 +1377,7 @@ static void show_packets(WriterContext *w, AVFormatContext *fmt_ctx)
> > pkt1 = pkt;
> > while (1) {
> > avcodec_get_frame_defaults(&frame);
> > - ret = get_decoded_frame(fmt_ctx, &frame, &got_frame, &pkt1);
> > + ret = get_decoded_frame(fmt_ctx, &frame, &got_frame, &pkt1, 0);
> > if (ret < 0 || !got_frame)
> > break;
> > show_frame(w, &frame, fmt_ctx->streams[pkt.stream_index]);
> > @@ -1391,7 +1393,7 @@ static void show_packets(WriterContext *w, AVFormatContext *fmt_ctx)
> > //Flush remaining frames that are cached in the decoder
> > for (i = 0; i < fmt_ctx->nb_streams; i++) {
> > pkt.stream_index = i;
> > - while (get_decoded_frame(fmt_ctx, &frame, &got_frame, &pkt) >= 0 && got_frame)
> > + while (get_decoded_frame(fmt_ctx, &frame, &got_frame, &pkt, 1) >= 0 && got_frame)
> > show_frame(w, &frame, fmt_ctx->streams[pkt.stream_index]);
> > }
> > }
>
> This should be fine, but I'd prefer to fix the crash in the decoder
> instead.
>
> Indeed from my reading of the documentation there is nothing wrong
> about passing video packets with size of 0, and the expected behavior
> should be to just ignore such packets, thus avoiding to complicate
> application level code.
I agree that this would be ideal, but I am not familiar enough with the
relevant decoders and standards to feel comfortable in submitting a fix for
that.. And ffmpeg.c does not decode zero-sized packets unless flushing,
which is why the segfault does not appear when transcoding.
I will take another look at the problem, though, and see if I can find a
solution in the decoder instead.
/P
More information about the ffmpeg-devel
mailing list