[FFmpeg-devel] [PATCH] Playlist API

Michael Niedermayer michaelni
Mon Aug 24 14:47:34 CEST 2009


On Mon, Aug 24, 2009 at 01:16:09AM -0700, Geza Kovacs wrote:
[...]
> +/** @file libavformat/avplaylist.c
> + *  @author Geza Kovacs ( gkovacs mit edu )
> + *
> + *  @brief General components used by playlist formats
> + *
> + *  @details These functions are used to initialize and manipulate playlists
> + *  (AVPlaylistContext) and their individual playlist elements (PlayElem), each

PlayElem?


> + *  of which encapsulates its own AVFormatContext. This abstraction is used for
> + *  implementing file concatenation and support for playlist formats.
> + */
> +
> +#include "libavformat/avplaylist.h"
> +#include "riff.h"
> +#include "libavutil/avstring.h"
> +#include "internal.h"
> +#include "concat.h"
> +
> +#define STREAM_CACHE_SIZE (6)
> +

> +AVFormatContext *av_playlist_alloc_formatcontext(char *filename)
> +{
> +    int err;
> +    AVFormatContext *ic = avformat_alloc_context();
> +    err = av_open_input_file(&ic, filename, ic->iformat, 0, NULL);
> +    if (err < 0) {
> +        av_log(ic, AV_LOG_ERROR, "Error during av_open_input_file\n");
> +        av_free(ic);
> +        return NULL;
> +    }
> +    err = av_find_stream_info(ic);
> +    if (err < 0) {
> +        av_log(ic, AV_LOG_ERROR, "Could not find stream info\n");
> +        av_free(ic);
> +        return NULL;
> +    }

i think this contains memleaks if av_find_stream_info fails, i guess
you should try valgrind on your code, maybe there are more such issues


[...]

> +int ff_concatgen_read_packet(AVFormatContext *s,
> +                             AVPacket *pkt)
> +{
> +    int ret, i, stream_index;
> +    char have_switched_streams = 0;
> +    AVPlaylistContext *ctx = s->priv_data;
> +    AVFormatContext *ic;
> +    stream_index = 0;
> +    for (;;) {
> +        ic = ctx->formatcontext_list[ctx->pe_curidx];
> +        av_init_packet(pkt);
> +        if (s->packet_buffer) {
> +            *pkt = s->packet_buffer->pkt;
> +            s->packet_buffer = s->packet_buffer->next;
> +            ret = 0;
> +        } else {
> +            ret = ic->iformat->read_packet(ic, pkt);
> +        }
> +        if (ret >= 0) {
> +            if (pkt) {
> +                int streams_offset = av_playlist_streams_offset_from_playidx(ctx, ctx->pe_curidx);
> +                stream_index = av_playlist_localstidx_from_streamidx(ctx, pkt->stream_index);
> +                pkt->stream_index = stream_index + streams_offset;
> +                if (!ic->streams[stream_index]->codec->has_b_frames ||
> +                    ic->streams[stream_index]->codec->codec->id == CODEC_ID_MPEG1VIDEO) {
> +                    int64_t time_offset_localbase = av_rescale_q(av_playlist_time_offset(ctx->durations, streams_offset),
> +                                                                 AV_TIME_BASE_Q,
> +                                                                 ic->streams[stream_index]->time_base);

> +                    pkt->dts += time_offset_localbase;
> +                    if (pkt->pts != AV_NOPTS_VALUE)
> +                        pkt->pts += time_offset_localbase;

good, though dts should also be checked for AV_NOPTS_VALUE like pts is

but i see now that there is a more tricky issue above that, namely
the av_playlist_time_offset is rescaled to the stream timebase and that
is not accurate ... for example a 1 frame per second video timebase=1 
and a audio streams that has a 1/44100 timebase could end up with up to
0.5 sec desync
That is, its not just a academic rounding problem.
But i dont know how to solve this ATM, so i suggest unless you have a
great idea to leave this as a known bug for later, it must be fixed but
i need to think about it a little first how to fix it ...


> +                }
> +            }
> +            break;
> +        } else {
> +            if (!have_switched_streams &&
> +                ctx->pe_curidx < ctx->pelist_size - 1 &&
> +                ret != AVERROR(EAGAIN)) {
> +            // TODO switch from AVERROR_EOF to AVERROR_EOS
> +            // -32 AVERROR_EOF for avi, -51 for ogg
> +
> +                av_log(ic, AV_LOG_DEBUG, "Switching stream %d to %d\n", stream_index, ctx->pe_curidx+1);
> +                ctx->durations[ctx->pe_curidx] = ic->duration;
> +                ctx->pe_curidx = av_playlist_stream_index_from_time(ctx,
> +                                                                    av_playlist_time_offset(ctx->durations, ctx->pe_curidx),
> +                                                                    NULL);
> +                if (av_playlist_populate_context(ctx, ctx->pe_curidx) < 0) {
> +                    av_log(NULL, AV_LOG_ERROR, "Failed to switch to AVFormatContext %d\n", ctx->pe_curidx);
> +                    break;
> +                }
> +                if ((av_playlist_set_streams(s)) < 0) {
> +                    av_log(NULL, AV_LOG_ERROR, "Failed to open codecs for streams in %d\n", ctx->pe_curidx);
> +                    break;
> +                }
> +                // have_switched_streams is set to avoid infinite loop
> +                have_switched_streams = 1;
> +                // duration is updated in case it's checked by a parent demuxer (chained concat demuxers)
> +                s->duration = 0;
> +                for (i = 0; i < ctx->pe_curidx; ++i)
> +                    s->duration += ctx->durations[i];
> +                continue;
> +            } else {
> +                av_log(ic, AV_LOG_ERROR, "Packet read error %d\n", ret);
> +                break;
> +            }
> +        }
> +    }
> +    return ret;
> +}
> +

> +int ff_concatgen_read_seek(AVFormatContext *s,
> +                           int stream_index,
> +                           int64_t pts,
> +                           int flags)
> +{
> +    int i, err;
> +    int64_t localpts_avtimebase, localpts, pts_avtimebase;
> +    AVPlaylistContext *ctx = s->priv_data;
> +    AVFormatContext *ic = ctx->formatcontext_list[ctx->pe_curidx];
> +    ctx->durations[ctx->pe_curidx] = ic->duration;
> +    pts_avtimebase = av_rescale_q(pts,
> +                                  ic->streams[stream_index]->time_base,
> +                                  AV_TIME_BASE_Q);
> +    ctx->pe_curidx = av_playlist_stream_index_from_time(ctx,
> +                                                        pts_avtimebase,
> +                                                        &localpts_avtimebase);
> +    err = av_playlist_populate_context(ctx, ctx->pe_curidx);
> +    if (err < 0) {
> +        av_log(NULL, AV_LOG_ERROR, "Failed to switch to AVFormatContext %d\n", ctx->pe_curidx);
> +        return err;
> +    }
> +    err = av_playlist_set_streams(s);
> +    if (err < 0) {
> +        av_log(NULL, AV_LOG_ERROR, "Failed to open codecs for streams in %d\n", ctx->pe_curidx);
> +        return err;
> +    }
> +    ic = ctx->formatcontext_list[ctx->pe_curidx];
> +    localpts = av_rescale_q(localpts_avtimebase,
> +                            AV_TIME_BASE_Q,
> +                            ic->streams[stream_index]->time_base);
> +    s->duration = 0;
> +    for (i = 0; i < ctx->pe_curidx; ++i)
> +        s->duration += ctx->durations[i];
> +    return ic->iformat->read_seek(ic, stream_index, localpts, flags);

this code looses precission, scaling to AV_TIME_BASE_Q and scaling back is not
a lossless operation


[...]
> diff --git a/libavformat/concatgen.h b/libavformat/concatgen.h
> new file mode 100644
> index 0000000..7379b01
> --- /dev/null
> +++ b/libavformat/concatgen.h
> @@ -0,0 +1,46 @@
> +/*
> + * Generic functions used by playlist/concatenation demuxers
> + * Copyright (c) 2009 Geza Kovacs
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVFORMAT_CONCATGEN_H
> +#define AVFORMAT_CONCATGEN_H
> +
> +#include "libavformat/avplaylist.h"
> +
> +int ff_concatgen_read_packet(AVFormatContext *s, AVPacket *pkt);
> +
> +int ff_concatgen_read_seek(AVFormatContext *s,
> +                           int stream_index,
> +                           int64_t pts,
> +                           int flags);
> +
> +int64_t ff_concatgen_read_timestamp(AVFormatContext *s,
> +                                    int stream_index,
> +                                    int64_t *pos,
> +                                    int64_t pos_limit);
> +
> +int ff_concatgen_read_close(AVFormatContext *s);
> +
> +int ff_concatgen_read_play(AVFormatContext *s);
> +
> +int ff_concatgen_read_pause(AVFormatContext *s);

these functions should be documented as they are in a header


[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

While the State exists there can be no freedom; when there is freedom there
will be no State. -- Vladimir Lenin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20090824/6300f3e6/attachment.pgp>



More information about the ffmpeg-devel mailing list