[PATCH] ffprobe: implement -show_frames option

Stefano Sabatini stefano.sabatini-lala
Sat Mar 12 19:14:42 CET 2011


---
 ffprobe.c |  244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 238 insertions(+), 6 deletions(-)

diff --git a/ffprobe.c b/ffprobe.c
index d7362dd..66932ad 100644
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -32,6 +32,8 @@ const char program_name[] = "FFprobe";
 const int program_birth_year = 2007;
 
 static int do_show_format  = 0;
+static int do_show_frames  = 0;
+static int do_read_packets = 0;
 static int do_show_packets = 0;
 static int do_show_streams = 0;
 
@@ -144,14 +146,228 @@ static void show_packet(AVFormatContext *fmt_ctx, AVPacket *pkt)
     printf("[/PACKET]\n");
 }
 
-static void show_packets(AVFormatContext *fmt_ctx)
+typedef struct FrameData {
+    struct AVStream *st;    ///< the stream to which the frame belongs
+    void *frame;            ///< the video frame, a samples buffer, a subtitle
+    int   size;             ///< the size of the (compressed) frame
+    int   decoded_size;     ///< the size of the decoded frame, 0 if meaningless/unused
+    int64_t pkt_pts;
+    int64_t pkt_dts;
+    int64_t pkt_duration;
+    int64_t pkt_pos;
+} FrameData;
+
+#define FILL_FRAME_DATA_FROM_PKT(frame_data, pkt) \
+    frame_data->pkt_pts      = pkt->pts;          \
+    frame_data->pkt_dts      = pkt->dts;          \
+    frame_data->pkt_duration = pkt->duration;     \
+    frame_data->pkt_pos      = pkt->pos;          \
+
+/**
+ * This function is called when allocating the frame: in some cases
+ * the frame is not returned immediately, you have to wait for another
+ * call to avcodec_decode_video(), but we need to fill the frame_data
+ * struct with information from the current packet.
+ */
+static int ffprobe_get_buffer(AVCodecContext *codec_ctx, AVFrame *frame)
+{
+    AVStream *st = codec_ctx->opaque;
+    AVPacket *pkt;
+    FrameData *frame_data;
+    int ret = avcodec_default_get_buffer(codec_ctx, frame);
+    if (ret < 0)
+        return ret;
+
+    frame_data = av_malloc(sizeof(FrameData));
+    if (!frame_data)
+        return AVERROR(ENOMEM);
+
+    pkt = st->codec->pkt;
+    frame_data->st           = st;
+    FILL_FRAME_DATA_FROM_PKT(frame_data, pkt);
+    frame_data->frame        = frame;
+
+    frame->opaque = frame_data;
+    return ret;
+}
+
+static void ffprobe_release_buffer(AVCodecContext *codec_ctx, AVFrame *frame)
+{
+    FrameData *frame_data = (FrameData *)frame->opaque;
+    av_freep(&frame_data);
+    avcodec_default_release_buffer(codec_ctx, frame);
+}
+
+/**
+ * Decode packet, and return the extracted frame.
+ *
+ * @note A packet (for example an audio frame) may contain several
+ * frames. In order to extract *all* the frames from the input packet,
+ * you should call the function many times, until there is no frame to
+ * extract anymore and the return value is 0.
+ *
+ * @param frame_data_ptr pointer to the location where to put the
+ * frame_data, it is set to NULL if no frame could be decoded
+ * @return size of the decoded data, 0 if no frame could be extracted,
+ * a negative value in case of error
+ */
+static int decode_packet(AVFormatContext *fmt_ctx,
+                         AVPacket *pkt,
+                         FrameData **frame_data_ptr)
+{
+    int ret, got_frame = 0;
+    AVFrame frame;
+    AVSubtitle subtitle;
+    AVStream *st = fmt_ctx->streams[pkt->stream_index];
+    static unsigned int samples_buf_size = 0;
+    static int16_t *samples_buf = NULL;
+    FrameData *frame_data;
+
+    *frame_data_ptr = NULL;
+
+    switch (st->codec->codec_type) {
+    case AVMEDIA_TYPE_AUDIO:
+        samples_buf = av_fast_realloc(samples_buf, &samples_buf_size,
+                                      FFMAX(pkt->size * sizeof(*samples_buf),
+                                            AVCODEC_MAX_AUDIO_FRAME_SIZE));
+        if (!samples_buf)
+            return AVERROR(ENOMEM);
+        ret = avcodec_decode_audio3(st->codec, samples_buf, &samples_buf_size,
+                                    pkt);
+        if (ret < 0)
+            break;
+
+        if (samples_buf_size > 0) {
+            /* allocate a FrameData struct and fill it */
+            frame_data = av_malloc(sizeof(FrameData));
+            frame_data->st           = st;
+            FILL_FRAME_DATA_FROM_PKT(frame_data, pkt);
+            frame_data->size         = ret;
+            frame_data->frame        = (void *)samples_buf;
+            frame_data->decoded_size = samples_buf_size;
+            *frame_data_ptr = frame_data;
+        } else {
+            *frame_data_ptr = NULL;
+        }
+        pkt->data += ret;
+        pkt->size -= ret;
+        break;
+
+    case AVMEDIA_TYPE_VIDEO:
+        got_frame = 0;
+        frame.opaque = NULL;
+        ret = avcodec_decode_video2(st->codec, &frame, &got_frame, pkt);
+        if (ret < 0)
+            break;
+        if (got_frame) {
+            if (frame.opaque) {
+                /* frame information was already stored in the frame by get_buffer */
+                *frame_data_ptr = (FrameData *)frame.opaque;
+            } else {
+                frame_data = av_malloc(sizeof(FrameData));
+                if (!frame_data)
+                    return AVERROR(ENOMEM);
+                frame_data->st           = st;
+                FILL_FRAME_DATA_FROM_PKT(frame_data, pkt);
+                frame_data->size         = ret;
+                frame_data->frame        = &frame;
+                *frame_data_ptr = frame_data;
+            }
+        }
+        pkt->data = NULL;
+        pkt->size = 0;
+        break;
+
+    case AVMEDIA_TYPE_SUBTITLE:
+        ret = avcodec_decode_subtitle2(st->codec, &subtitle, &got_frame, pkt);
+        if (ret < 0)
+            break;
+
+        if (got_frame) {
+            frame_data = av_malloc(sizeof(FrameData));
+            if (!frame_data)
+                return AVERROR(ENOMEM);
+            frame_data->st           = st;
+            FILL_FRAME_DATA_FROM_PKT(frame_data, pkt);
+            frame_data->size         = ret;
+            frame_data->frame        = &subtitle;
+            *frame_data_ptr = frame_data;
+        }
+        pkt->data = NULL;
+        pkt->size = 0;
+        break;
+
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+static void show_frame(AVFormatContext *fmt_ctx, FrameData *frame_data)
+{
+    AVStream *st = frame_data->st;
+    AVFrame *frame;
+    char val_str[128];
+
+    printf("[FRAME]\n");
+    printf("media_type=%s\n", media_type_string(st->codec->codec_type));
+
+    switch (st->codec->codec_type) {
+    case AVMEDIA_TYPE_VIDEO:
+        frame = (AVFrame *)frame_data->frame;
+        printf("pict_type=%c\n",              av_get_pict_type_char(frame->pict_type));
+        printf("coded_picture_number=%d\n",   frame->coded_picture_number);
+        printf("display_picture_number=%d\n", frame->display_picture_number);
+        printf("interlaced_frame=%d\n",       frame->interlaced_frame);
+        printf("top_field_first=%d\n",        frame->top_field_first);
+        printf("repeat_pict=%d\n",            frame->repeat_pict);
+        printf("reference=%d\n",              frame->reference);
+        break;
+
+    case AVMEDIA_TYPE_AUDIO:
+        printf("nb_samples=%d\n",
+               frame_data->decoded_size * 8 / av_get_bits_per_sample_fmt(st->codec->sample_fmt));
+        break;
+
+    default:
+        break;
+    }
+
+    printf("stream_index=%d\n", st->index);
+    printf("size=%s\n",            value_string(val_str, sizeof(val_str),
+                                                (double)frame_data->size, unit_byte_str));
+    printf("pkt_pts=%"PRId64"\n",  frame_data->pkt_pts);
+    printf("pkt_pts_time=%s\n",    time_value_string(val_str, sizeof(val_str),
+                                                     frame_data->pkt_pts, &st->time_base));
+    printf("pkt_dts=%"PRId64"\n",  frame_data->pkt_dts);
+    printf("pkt_dts_time=%s\n",    time_value_string(val_str, sizeof(val_str),
+                                                     frame_data->pkt_dts, &st->time_base));
+    printf("pkt_pos=%"PRId64"\n",  frame_data->pkt_pos);
+    printf("pkt_duration=%s\n",    time_value_string(val_str, sizeof(val_str),
+                                                     frame_data->pkt_duration, &st->time_base));
+
+    printf("[/FRAME]\n");
+}
+
+static void read_packets(AVFormatContext *fmt_ctx)
 {
     AVPacket pkt;
+    FrameData *frame_data;
 
     av_init_packet(&pkt);
 
-    while (!av_read_frame(fmt_ctx, &pkt))
-        show_packet(fmt_ctx, &pkt);
+    while (!av_read_frame(fmt_ctx, &pkt)) {
+        if (do_show_packets)
+            show_packet(fmt_ctx, &pkt);
+
+        if (do_show_frames) {
+            while (pkt.size && (decode_packet(fmt_ctx, &pkt, &frame_data) > 0 || frame_data)) {
+                if (frame_data)
+                    show_frame(fmt_ctx, frame_data);
+            }
+        }
+    }
 }
 
 static void show_stream(AVFormatContext *fmt_ctx, int stream_idx)
@@ -291,6 +507,9 @@ static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename)
             fprintf(stderr, "Error while opening codec for input stream %d\n",
                     stream->index);
         }
+        stream->codec->opaque         = stream;
+        stream->codec->get_buffer     = ffprobe_get_buffer;
+        stream->codec->release_buffer = ffprobe_release_buffer;
     }
 
     *fmt_ctx_ptr = fmt_ctx;
@@ -305,8 +524,8 @@ static int probe_file(const char *filename)
     if ((ret = open_input_file(&fmt_ctx, filename)))
         return ret;
 
-    if (do_show_packets)
-        show_packets(fmt_ctx);
+    if (do_read_packets)
+        read_packets(fmt_ctx);
 
     if (do_show_streams)
         for (i = 0; i < fmt_ctx->nb_streams; i++)
@@ -365,6 +584,18 @@ static void opt_pretty(void)
     use_value_sexagesimal_format = 1;
 }
 
+static void opt_show_frames(void)
+{
+    do_read_packets = 1;
+    do_show_frames  = 1;
+}
+
+static void opt_show_packets(void)
+{
+    do_read_packets = 1;
+    do_show_packets = 1;
+}
+
 static const OptionDef options[] = {
 #include "cmdutils_common_opts.h"
     { "f", HAS_ARG, {(void*)opt_format}, "force format", "format" },
@@ -377,7 +608,8 @@ static const OptionDef options[] = {
     { "pretty", 0, {(void*)&opt_pretty},
       "prettify the format of displayed values, make it more human readable" },
     { "show_format",  OPT_BOOL, {(void*)&do_show_format} , "show format/container info" },
-    { "show_packets", OPT_BOOL, {(void*)&do_show_packets}, "show packets info" },
+    { "show_packets", 0, {(void*)opt_show_packets}, "show packets info" },
+    { "show_frames",  0, {(void*)opt_show_frames},  "show frames info" },
     { "show_streams", OPT_BOOL, {(void*)&do_show_streams}, "show streams info" },
     { "default", OPT_FUNC2 | HAS_ARG | OPT_AUDIO | OPT_VIDEO | OPT_EXPERT, {(void*)opt_default}, "generic catch all option", "" },
     { NULL, },
-- 
1.7.2.3


--TakKZr9L6Hm6aLOc--



More information about the ffmpeg-devel mailing list