Outdated v2 of patch. See PATCH 1/4 (v3) under '[PATCH 0/4] Support for HDMV Interactive Graphics Stream' stream.
Sorry, David Le dimanche 03 juin 2012 à 11:42 +0200, David Girault a écrit : > This new decoder will allow to decode menu stream found > in m2ts files in Bluray or AVCHD disks. > > The new decoder was developped using AVMEDIA_TYPE_OVERLAY, > inspired from AVMEDIA_TYPE_SUBTITLE, since both may send > multiple decoded pictures to add to video currently > displayed. > > Signed-off-by: David Girault <[email protected]> > --- > libavcodec/Makefile | 1 + > libavcodec/allcodecs.c | 3 + > libavcodec/avcodec.h | 53 ++++ > libavcodec/igsmnudec.c | 726 > ++++++++++++++++++++++++++++++++++++++++++++++++ > libavcodec/utils.c | 36 +++ > libavutil/avutil.h | 1 + > 6 files changed, 820 insertions(+) > create mode 100644 libavcodec/igsmnudec.c > > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index da8aa8a..e530e56 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -309,6 +309,7 @@ OBJS-$(CONFIG_PGM_ENCODER) += pnmenc.o pnm.o > OBJS-$(CONFIG_PGMYUV_DECODER) += pnmdec.o pnm.o > OBJS-$(CONFIG_PGMYUV_ENCODER) += pnmenc.o pnm.o > OBJS-$(CONFIG_PGSSUB_DECODER) += pgssubdec.o > +OBJS-$(CONFIG_IGSMENU_DECODER) += igsmnudec.o > OBJS-$(CONFIG_PICTOR_DECODER) += pictordec.o cga_data.o > OBJS-$(CONFIG_PNG_DECODER) += png.o pngdec.o pngdsp.o > OBJS-$(CONFIG_PNG_ENCODER) += png.o pngenc.o > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c > index a1df47a..e118b46 100644 > --- a/libavcodec/allcodecs.c > +++ b/libavcodec/allcodecs.c > @@ -374,6 +374,9 @@ void avcodec_register_all(void) > REGISTER_DECODER (SRT, srt); > REGISTER_ENCDEC (XSUB, xsub); > > + /* specific data */ > + REGISTER_DECODER (IGSMENU, igsmenu); > + > /* external libraries */ > REGISTER_ENCODER (LIBFAAC, libfaac); > REGISTER_ENCDEC (LIBGSM, libgsm); > diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h > index 102df3a..0832ae0 100644 > --- a/libavcodec/avcodec.h > +++ b/libavcodec/avcodec.h > @@ -410,6 +410,8 @@ enum CodecID { > CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at > the start of various fake codecs. > CODEC_ID_TTF = 0x18000, > > + CODEC_ID_HDMV_IGS_MENU, > + > CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like > CODEC_ID_NONE) but lavf should attempt to identify it > > CODEC_ID_MPEG2TS = 0x20000, /**< _FAKE_ codec to indicate a raw MPEG-2 TS > @@ -3095,6 +3097,30 @@ typedef struct AVSubtitle { > int64_t pts; ///< Same as packet pts, in AV_TIME_BASE > } AVSubtitle; > > +typedef struct AVOverlayRect { > + int x; ///< top left corner of pict, undefined when pict is not > set > + int y; ///< top left corner of pict, undefined when pict is not > set > + int w; ///< width of pict, undefined when pict is not > set > + int h; ///< height of pict, undefined when pict is not > set > + int nb_colors; ///< number of colors in pict, undefined when pict is not > set > + > + /** > + * data+linesize for the bitmap of this overlay. > + */ > + AVPicture pict; > +} AVOverlayRect; > + > +typedef struct AVOverlay { > + uint16_t format; /* 0 = HDMV Interactive Graphic menu */ > + uint32_t start_display_time; /* relative to packet pts, in ms */ > + uint32_t end_display_time; /* relative to packet pts, in ms */ > + unsigned num_rects; > + AVOverlayRect **rects; > + int64_t pts; ///< Same as packet pts, in AV_TIME_BASE > + void *extra; ///< Extra data > + int extra_sz; ///< Size in bytes of extra data > +} AVOverlay; > + > /** > * If c is NULL, returns the first registered codec, > * if c is non-NULL, returns the next registered codec after c, > @@ -3261,6 +3287,13 @@ int avcodec_close(AVCodecContext *avctx); > void avsubtitle_free(AVSubtitle *sub); > > /** > + * Free all allocated data in the given overlay struct. > + * > + * @param overlay AVOverlay to free. > + */ > +void avoverlay_free(AVOverlay *overlay); > + > +/** > * @} > */ > > @@ -3581,6 +3614,26 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, > AVSubtitle *sub, > AVPacket *avpkt); > > /** > + * Decode a overlay message. > + * Return a negative value on error, otherwise return the number of bytes > used. > + * If no overlay could be decompressed, got_sub_ptr is zero. > + * Otherwise, the overlay is stored in *sub. > + * Note that CODEC_CAP_DR1 is not available for overlay codecs. This is for > + * simplicity, because the performance difference is expect to be negligible > + * and reusing a get_buffer written for video codecs would probably perform > badly > + * due to a potentially very different allocation pattern. > + * > + * @param avctx the codec context > + * @param[out] overlay The AVOverlay in which the decoded overlay will be > stored, must be > + freed with avoverlay_free if *got_overlay_ptr is set. > + * @param[in,out] got_overlay_ptr Zero if no overlay could be decompressed, > otherwise, it is nonzero. > + * @param[in] avpkt The input AVPacket containing the input buffer. > + */ > +int avcodec_decode_overlay2(AVCodecContext *avctx, AVOverlay *overlay, > + int *got_overlay_ptr, > + AVPacket *avpkt); > + > +/** > * @defgroup lavc_parsing Frame parsing > * @{ > */ > diff --git a/libavcodec/igsmnudec.c b/libavcodec/igsmnudec.c > new file mode 100644 > index 0000000..4bea6d5 > --- /dev/null > +++ b/libavcodec/igsmnudec.c > @@ -0,0 +1,726 @@ > +/* > + * IGS menu decoder > + * Copyright (c) 2012 David Girault > + * > + * This file is part of Libav. > + * > + * Libav 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. > + * > + * Libav 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 Libav; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 > USA > + */ > + > +/** > + * @file > + * IGS menu decoder > + */ > + > +#include "avcodec.h" > +#include "dsputil.h" > +#include "bytestream.h" > +#include "libavutil/colorspace.h" > +#include "libavutil/imgutils.h" > + > +#define RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) > + > +enum SegmentType { > + PALETTE_SEGMENT = 0x14, > + PICTURE_SEGMENT = 0x15, > + BUTTON_SEGMENT = 0x18, > + DISPLAY_SEGMENT = 0x80, > +}; > + > +typedef struct IGSPicture { > + uint16_t id; > + int w; > + int h; > + uint8_t *rle; > + unsigned int rle_buffer_size, rle_data_len; > + unsigned int rle_remaining_len; > +} IGSPicture; > + > +typedef struct IGSButton { > + uint16_t id; > + uint16_t v; > + uint8_t f; > + uint16_t x; > + uint16_t y; > + uint16_t n[4];//0:up,1:down,2:left,3:right > + uint16_t pstart[3]; //0:normal,1:selected,2:activated > + uint16_t pstop[3]; > + uint16_t cmds_count; > + uint32_t *cmds; > +} IGSButton; > + > +typedef struct IGSBOG { > + uint16_t def_button; > + uint16_t cur_button; > + IGSButton *buttons; > + int buttons_count; > +} IGSBOG; > + > +typedef struct IGSPage { > + int w; > + int h; > + int def_button; > + int sel_button; > + int in_time; > + int timeout; > + int palette; > + IGSBOG *bogs; > + int bogs_count; > +} IGSPage; > + > +typedef struct IGSPalette { > + uint32_t clut[256]; > +} IGSPalette; > + > +typedef struct IGSContext { > + IGSPalette *palettes; > + int palettes_count; > + IGSPicture *pictures; > + int pictures_count; > + IGSPage *pages; > + int pages_count; > + int page; > +} IGSContext; > + > +static av_cold int init_decoder(AVCodecContext *avctx) > +{ > + avctx->pix_fmt = PIX_FMT_PAL8; > + return 0; > +} > + > +static av_cold int close_decoder(AVCodecContext *avctx) > +{ > + int i; > + > + IGSContext *ctx = avctx->priv_data; > + > + for (i=0;i<ctx->pictures_count;i++) { > + av_freep(&ctx->pictures[i].rle); > + } > + av_freep(&ctx->pictures); > + av_freep(&ctx->palettes); > + > + for (i=0;i<ctx->pages_count;i++) { > + IGSPage *page = &ctx->pages[i]; > + int j; > + for (j=0;j<page->bogs_count;j++) { > + IGSBOG *bog = &page->bogs[j]; > + int k; > + for (k=0;k<bog->buttons_count;k++) { > + IGSButton *button = &bog->buttons[k]; > + if (button->cmds) av_freep(&button->cmds); > + } > + av_freep(&bog->buttons); > + } > + av_freep(&page->bogs); > + } > + av_freep(&ctx->pages); > + return 0; > +} > + > +/** > + * Decode the RLE data. > + * > + * The subtitle is stored as an Run Length Encoded image. > + * > + * @param avctx contains the current codec context > + * @param ove pointer to the processed overlay data > + * @param buf pointer to the RLE data to process > + * @param buf_size size of the RLE data to process > + */ > +static int decode_rle(AVCodecContext *avctx, AVOverlay *ove, int i, > + const uint8_t *buf, unsigned int buf_size) > +{ > + const uint8_t *rle_bitmap_end; > + int pixel_count, line_count; > + > + rle_bitmap_end = buf + buf_size; > + > + ove->rects[i]->pict.data[0] = av_malloc(ove->rects[i]->w * > ove->rects[i]->h); > + > + if (!ove->rects[i]->pict.data[0]) > + return -1; > + > + pixel_count = 0; > + line_count = 0; > + > + while (buf < rle_bitmap_end && line_count < ove->rects[i]->h) { > + uint8_t flags, color; > + int run; > + > + color = bytestream_get_byte(&buf); > + run = 1; > + > + if (color == 0x00) { > + flags = bytestream_get_byte(&buf); > + run = flags & 0x3f; > + if (flags & 0x40) > + run = (run << 8) + bytestream_get_byte(&buf); > + color = flags & 0x80 ? bytestream_get_byte(&buf) : 0; > + } > + > + if (run > 0 && pixel_count + run <= ove->rects[i]->w * > ove->rects[i]->h) { > + memset(ove->rects[i]->pict.data[0] + pixel_count, color, run); > + pixel_count += run; > + } else if (!run) { > + /* > + * New Line. Check if correct pixels decoded, if not display > warning > + * and adjust bitmap pointer to correct new line position. > + */ > + if (pixel_count % ove->rects[i]->w > 0) > + av_log(avctx, AV_LOG_ERROR, "Decoded %d pixels, when line > should be %d pixels\n", > + pixel_count % ove->rects[i]->w, ove->rects[i]->w); > + line_count++; > + } > + } > + > + if (pixel_count < ove->rects[i]->w * ove->rects[i]->h) { > + av_log(avctx, AV_LOG_ERROR, "Insufficient RLE data for overlay > button %d\n", i); > + return -1; > + } > + > + av_dlog(avctx, " Pixel Count = %d, Area = %d\n", pixel_count, > ove->rects[i]->w * ove->rects[i]->h); > + > + return 0; > +} > + > +/** > + * Parse the picture segment packet. > + * > + * The picture segment contains details on the sequence id, > + * width, height and Run Length Encoded (RLE) bitmap data. > + * > + * @param avctx contains the current codec context > + * @param buf pointer to the packet to process > + * @param buf_size size of packet to process > + * @todo TODO: Enable support for RLE data over multiple packets > + */ > +static int parse_picture_segment(AVCodecContext *avctx, > + const uint8_t *buf, int buf_size) > +{ > + IGSContext *ctx = avctx->priv_data; > + IGSPicture *pic = NULL; > + uint16_t oid; > + uint8_t sequence_desc; > + unsigned int rle_bitmap_len, width, height; > + > + if (buf_size <= 4) > + return -1; > + buf_size -= 4; > + > + /* skip 3 unknown bytes: Object ID (2 bytes), Version Number */ > + oid = bytestream_get_be16(&buf); > + buf += 1; > + > + /* Read the Sequence Description to determine if start of RLE data or > appended to previous RLE */ > + sequence_desc = bytestream_get_byte(&buf); > + > + if (!(sequence_desc & 0x80)) { > + /* Additional RLE data to last picture */ > + pic = &(ctx->pictures[ctx->pictures_count-1]); > + if (buf_size > pic->rle_remaining_len) > + return -1; > + av_dlog(avctx, "Continuing bitmap @ idx %d (%p).\n", > ctx->pictures_count-1, pic); > + memcpy(pic->rle + pic->rle_data_len, buf, buf_size); > + pic->rle_data_len += buf_size; > + pic->rle_remaining_len -= buf_size; > + return 0; > + } > + > + if (buf_size <= 7) > + return -1; > + buf_size -= 7; > + > + /* Decode rle bitmap length, stored size includes width/height data */ > + rle_bitmap_len = bytestream_get_be24(&buf) - 2*2; > + > + /* Get bitmap dimensions from data */ > + width = bytestream_get_be16(&buf); > + height = bytestream_get_be16(&buf); > + > + /* Make sure the bitmap is not too large */ > + if (avctx->width < width || avctx->height < height) { > + av_log(avctx, AV_LOG_ERROR, "Bitmap dimensions (%dx%d) larger than > video.\n", width, height); > + return -1; > + } > + > + /* add this picture */ > + ctx->pictures_count++; > + pic = av_realloc(ctx->pictures, ctx->pictures_count * > sizeof(IGSPicture)); > + if (pic == NULL) { > + av_log(avctx, AV_LOG_ERROR, "Can't reallocate bitmaps table.\n"); > + ctx->pictures_count--; > + return -2; > + } > + ctx->pictures = pic; > + pic = &(ctx->pictures[ctx->pictures_count-1]); > + > + av_log(avctx, AV_LOG_DEBUG, "New bitmap %04X: %dx%d @ idx %d (%p).\n", > + oid, width, height, ctx->pictures_count-1, pic); > + > + pic->id = oid; > + pic->w = width; > + pic->h = height; > + > + pic->rle = av_malloc(rle_bitmap_len); > + if (!pic->rle) > + return -1; > + > + pic->rle_buffer_size=rle_bitmap_len; > + > + av_dlog(avctx, "Bitmap RLE %d bytes @ %p (%d, %d).\n", > pic->rle_buffer_size, pic->rle, rle_bitmap_len, buf_size); > + if (buf_size>pic->rle_buffer_size) buf_size=pic->rle_buffer_size; > + > + memcpy(pic->rle, buf, buf_size); > + pic->rle_data_len = buf_size; > + pic->rle_remaining_len = rle_bitmap_len - buf_size; > + > + return 0; > +} > + > +/** > + * Parse the palette segment packet. > + * > + * The palette segment contains details of the palette, > + * a maximum of 256 colors can be defined. > + * > + * @param avctx contains the current codec context > + * @param buf pointer to the packet to process > + * @param buf_size size of packet to process > + */ > +static int parse_palette_segment(AVCodecContext *avctx, > + const uint8_t *buf, int buf_size) > +{ > + IGSContext *ctx = avctx->priv_data; > + IGSPalette *pal = NULL; > + uint16_t oid; > + > + const uint8_t *buf_end = buf + buf_size; > + const uint8_t *cm = ff_cropTbl + MAX_NEG_CROP; > + int color_id; > + int y, cb, cr, alpha; > + int r, g, b, r_add, g_add, b_add; > + > + /* add this picture */ > + ctx->palettes_count++; > + pal = av_realloc(ctx->palettes, ctx->palettes_count * > sizeof(IGSPalette)); > + if (pal == NULL) { > + av_log(avctx, AV_LOG_ERROR, "Can't reallocate palettes table.\n"); > + ctx->palettes_count--; > + return -2; > + } > + ctx->palettes = pal; > + pal = &(ctx->palettes[ctx->palettes_count-1]); > + > + /* Skip two null bytes */ > + oid = bytestream_get_be16(&buf); > + > + av_log(avctx, AV_LOG_DEBUG, "New palette %04X @ %p: idx=%d, size=%d, %d > colors.\n", > + oid, pal, ctx->palettes_count-1, buf_size-2, (buf_size-2)/5); > + while (buf < buf_end) { > + color_id = bytestream_get_byte(&buf); > + y = bytestream_get_byte(&buf); > + cr = bytestream_get_byte(&buf); > + cb = bytestream_get_byte(&buf); > + alpha = bytestream_get_byte(&buf); > + > + YUV_TO_RGB1(cb, cr); > + YUV_TO_RGB2(r, g, b, y); > + > + av_log(avctx, AV_LOG_DEBUG, " Color %d := (%d,%d,%d,%d)\n", > color_id, r, g, b, alpha); > + > + /* Store color in palette */ > + pal->clut[color_id] = RGBA(r,g,b,alpha); > + } > + return 0; > +} > + > +/** > + * Parse the button segment packet. > + * > + * The button segment contains details on the interactive graphics. > + * > + * @param avctx contains the current codec context > + * @param buf pointer to the packet to process > + * @param buf_size size of packet to process > + * @todo TODO: Implement cropping > + */ > +static int parse_button_segment(AVCodecContext *avctx, > + const uint8_t *buf, int buf_size) > +{ > + IGSContext *ctx = avctx->priv_data; > + const uint8_t *start = buf; > + int i, page_cnt; > + uint32_t to; > + int w = bytestream_get_be16(&buf); > + int h = bytestream_get_be16(&buf); > + > + av_log(avctx, AV_LOG_DEBUG, "Interactive Graphics dimensions %dx%d\n", > w, h); > + if (av_image_check_size(w, h, 0, avctx) >= 0) > + avcodec_set_dimensions(avctx, w, h); > + > + buf+=1; // skip framerate (0x20 => 2 => 24fps, 0x60 => 50fps) > + buf+=2; // unknown (0x0000) > + buf+=1; // skip some flags (0x80) > + buf+=4; // skip IN_time (0xc0000163) > + to = bytestream_get_be32(&buf); // user timeout (bit 31 seems to be a > flags) > + // ten more byte skipped (output generated by TMpegEnc Authoring 5) > + if (to == 0) buf+=10; > + > + page_cnt = bytestream_get_byte(&buf); > + av_log(avctx, AV_LOG_DEBUG, "Interactive Graphics had %d pages\n", > page_cnt); > + > + ctx->pages = av_mallocz(page_cnt*sizeof(IGSPage)); > + if (ctx->pages) ctx->pages_count = page_cnt; > + > + /* pages loop */ > + for (i=0;i<ctx->pages_count;i++) { > + IGSPage *page = &(ctx->pages[i]); > + int j, bog_cnt; > + buf+=1; /* skip page_id */ > + buf+=1; /* skip unknown */ > + buf+=8; /* skip UO */ > + buf+=4; /* skip in/out effects count (this may be followed by effect > data) */ > + buf+=1; /* skip framerate divider */ > + page->def_button = bytestream_get_be16(&buf); > + page->sel_button = page->def_button; > + > + buf+=2; /* skip def_activated, palette */ > + page->palette = bytestream_get_byte(&buf); > + > + bog_cnt = bytestream_get_byte(&buf); > + av_log(avctx, AV_LOG_DEBUG, "Page %d @ %X has %d BOGs, use palette > %d\n", i, ((int)(buf-start))-21, bog_cnt, page->palette); > + > + page->bogs = av_mallocz(bog_cnt*sizeof(IGSBOG)); > + if (page->bogs) page->bogs_count = bog_cnt; > + else break; > + > + /* bogs loop */ > + for (j=0;j<page->bogs_count;j++) { > + IGSBOG *bog = &(page->bogs[j]); > + int k, button_cnt; > + > + bog->def_button = bytestream_get_be16(&buf); > + bog->cur_button = bog->def_button; > + > + button_cnt = bytestream_get_byte(&buf); > + av_log(avctx, AV_LOG_DEBUG, " BOG %d had %d buttons, > default=%04X\n", j, button_cnt, bog->def_button); > + > + bog->buttons = av_mallocz(button_cnt*sizeof(IGSButton)); > + if (bog->buttons) bog->buttons_count = button_cnt; > + else break; > + > + /* buttons loop */ > + for (k=0;k<bog->buttons_count;k++) { > + IGSButton *but = &(bog->buttons[k]); > + but->id = bytestream_get_be16(&buf); > + but->v = bytestream_get_be16(&buf); > + but->f = bytestream_get_byte(&buf); > + but->x = bytestream_get_be16(&buf); > + but->y = bytestream_get_be16(&buf); > + but->n[0] = bytestream_get_be16(&buf); > + but->n[1] = bytestream_get_be16(&buf); > + but->n[2] = bytestream_get_be16(&buf); > + but->n[3] = bytestream_get_be16(&buf); > + av_log(avctx, AV_LOG_DEBUG, " Button %04X @ %p: x=%d, > y=%d, navig=%04X/%04X/%04X/%04X, ", > + but->id, but, but->x, but->y, but->n[0], but->n[1], > but->n[2], but->n[3]); > + > + but->pstart[0] = bytestream_get_be16(&buf); > + but->pstop[0] = bytestream_get_be16(&buf); > + buf+=2;//repeat, sound > + > + but->pstart[1] = bytestream_get_be16(&buf); > + but->pstop[1] = bytestream_get_be16(&buf); > + buf+=2;//repeat, sound > + > + but->pstart[2] = bytestream_get_be16(&buf); > + but->pstop[2] = bytestream_get_be16(&buf); > + but->cmds_count = bytestream_get_be16(&buf); > + if (but->cmds_count) { > + size_t sz = 3*sizeof(uint32_t) * but->cmds_count; > + but->cmds = av_mallocz(sz); > + if (but->cmds) memcpy(but->cmds, buf, sz); > + buf+= sz; // skip commands > + } > + av_log(avctx, AV_LOG_DEBUG, "pics=%04X/%04X/%04X, > cmds=%d\n", > + but->pstart[0], but->pstart[1], but->pstart[2], > but->cmds_count); > + > + // set this button current if not already set > + if (bog->cur_button == 0xffff) bog->cur_button = but->id; > + } > + > + > + } > + > + // set selected button if not already set > + if (page->sel_button == 0xffff) { > + for (j=0;j<page->bogs_count;j++) { > + IGSBOG *bog = &(page->bogs[j]); > + int k; > + for (k=0;k<bog->buttons_count;k++) { > + IGSButton *but = &(bog->buttons[k]); > + // search first button with nav to other button > + if (but->n[0] != but->id || > + but->n[1] != but->id || > + but->n[2] != but->id || > + but->n[3] != but->id) { > + page->sel_button = but->id; > + break; > + } > + } > + if (page->sel_button != 0xffff) break; > + } > + } > + } > + return 0; > +} > + > +/** > + * Parse the display segment packet. > + * > + * The display segment controls the updating of the display. > + * > + * @param avctx contains the current codec context > + * @param data pointer to the data pertaining the subtitle to display > + * @param buf pointer to the packet to process > + * @param buf_size size of packet to process > + * @todo TODO: Fix start time, relies on correct PTS, currently too late > + * > + * @todo TODO: Fix end time, normally cleared by a second display > + * @todo segment, which is currently ignored as it clears > + * @todo the subtitle too early. > + */ > +static int display_end_segment(AVCodecContext *avctx, void *data, > + const uint8_t *buf, int buf_size) > +{ > + AVOverlay *ove = data; > + IGSContext *ctx = avctx->priv_data; > + IGSPage *page = NULL; > + > + int i, activated = 0; > + > + /* > + * The end display time is a timeout value and is only reached > + * if the next subtitle is later then timeout or subtitle has > + * not been cleared by a subsequent empty display command. > + */ > + > + memset(ove, 0, sizeof(*ove)); > + // Blank if last object_number was 0. > + // Note that this may be wrong for more complex subtitles. > + if (!ctx->pages_count) > + return 1; > + > + page = &(ctx->pages[ctx->page]); > + > + if (buf_size >= 1) { > + // this may be a fake display segment, with a navigation key embedded > + IGSButton *button=NULL; > + // handle page switch early > + if (*(const char*)buf == 'p') { > + //Todo: handle extra parameters to allow page/button selection > from HDMV command 'SetButtonPage' > + if (buf_size >= 2) { > + if (buf[1]<ctx->pages_count) ctx->page = buf[1]; > + } > + else { > + if (++ctx->page >= ctx->pages_count) ctx->page=0; > + } > + page = &(ctx->pages[ctx->page]); > + if (buf_size >= 4) { > + page->sel_button = *(const uint16_t*)(buf+2); > + } > + } > + > + for (i=0;i<page->bogs_count;i++) { > + IGSBOG *bog = &(page->bogs[i]); > + int j; > + > + for (j=0;j<bog->buttons_count;j++) { > + button = &(bog->buttons[j]); > + if (button->id == page->sel_button) break; > + } > + if (j<bog->buttons_count) break; > + button=NULL; > + } > + if (button != NULL) { > + uint16_t next = button->id; > + switch(*(const char*)buf) { > + case 13: activated=1; break; > + case 'u': next = button->n[0]; break; > + case 'd': next = button->n[1]; break; > + case 'l': next = button->n[2]; break; > + case 'r': next = button->n[3]; break; > + } > + page->sel_button = next; > + } > + } > + > + av_log(avctx, AV_LOG_DEBUG, "Displaying menu page %d, button %d %s.\n", > + ctx->page, page->sel_button, (activated?"activated":"selected")); > + > + ove->start_display_time = 0; > + ove->end_display_time = 20000; > + ove->format = 0; > + > + ove->num_rects = page->bogs_count; > + ove->rects = av_mallocz(sizeof(AVOverlayRect*)*ove->num_rects); > + > + for (i=0;i<ove->num_rects;i++) { > + IGSBOG *bog = &(page->bogs[i]); > + IGSButton *button=NULL; > + IGSPicture *picture=NULL; > + AVOverlayRect *rect; > + int j; > + uint16_t oid; > + > + av_dlog(avctx, " BOG %d, Current Button %04X\n", i, > bog->cur_button); > + > + // search button in bog > + for (j=0;j<bog->buttons_count;j++) { > + button = &(bog->buttons[j]); > + if (button->id == bog->cur_button) break; > + } > + if (j==bog->buttons_count) { > + button = &(bog->buttons[0]); > + bog->cur_button = button->id; > + } > + // get picture for button > + oid = (page->sel_button == button->id ? > (activated?button->pstart[2]:button->pstart[1]) : button->pstart[0]); > + > + for (j=0;j<ctx->pictures_count;j++) { > + picture = &(ctx->pictures[j]); > + if (picture->id == oid) break; > + } > + if (j==ctx->pictures_count) return 0; // picture not found!!! > + > + av_dlog(avctx, " Button %04X @ %p => Picture %04X @ %p\n", > button->id, button, oid, picture); > + > + rect = av_mallocz(sizeof(AVOverlayRect)); > + rect->x = button->x; > + rect->y = button->y; > + rect->w = picture->w; > + rect->h = picture->h; > + > + ove->rects[i] = rect; > + > + /* Process bitmap */ > + rect->pict.linesize[0] = picture->w; > + > + if (picture->rle) { > + if (picture->rle_remaining_len) > + av_log(avctx, AV_LOG_ERROR, "RLE data length %u is %u bytes > shorter than expected\n", > + picture->rle_data_len, picture->rle_remaining_len); > + if(decode_rle(avctx, ove, i, picture->rle, > picture->rle_data_len) < 0) > + return 0; > + } > + > + /* Allocate memory for colors */ > + rect->nb_colors = 256; > + rect->pict.data[1] = av_mallocz(AVPALETTE_SIZE); > + memcpy(rect->pict.data[1], ctx->palettes[page->palette].clut, > rect->nb_colors * sizeof(uint32_t)); > + > + av_dlog(avctx, " Rect %d @ %p: x=%d, y=%d, w=%d, h=%d, data=%p, > pal=%p\n", > + i, rect, rect->x, rect->y, rect->w, rect->h, > rect->pict.data[0], rect->pict.data[1]); > + > + /* Add extra data */ > + if ((page->sel_button == button->id) && activated) { > + // TODO: button HDMV program must be included when activated > + if (button->cmds_count > 0) { > + ove->extra_sz = 3*sizeof(uint32_t) * button->cmds_count; > + ove->extra = av_mallocz(ove->extra_sz); > + if (ove->extra) memcpy(ove->extra, button->cmds, > ove->extra_sz); > + else ove->extra_sz = 0; // ignore alloc error for now > + av_log(avctx, AV_LOG_DEBUG, " Extra data for %d commands @ > %p\n", > + button->cmds_count, ove->extra); > + } > + } > + } > + return 1; > +} > + > +static int decode(AVCodecContext *avctx, void *data, int *data_size, > + AVPacket *avpkt) > +{ > + const uint8_t *buf = avpkt->data; > + int buf_size = avpkt->size; > + > + const uint8_t *buf_end; > + uint8_t segment_type; > + int segment_length; > + int ret = 0; > + > + *data_size = 0; > + > + /* Ensure that we have received at a least a segment code and segment > length */ > + if (buf_size < 3) > + return -1; > + > + buf_end = buf + buf_size; > + > + /* Step through buffer to identify segments */ > + while (buf < buf_end) { > + segment_type = bytestream_get_byte(&buf); > + segment_length = bytestream_get_be16(&buf); > + > + if (segment_type != DISPLAY_SEGMENT && segment_length > buf_end - > buf) > + break; > + > + switch (segment_type) { > + case PALETTE_SEGMENT: > + ret = parse_palette_segment(avctx, buf, segment_length); > + break; > + case PICTURE_SEGMENT: > + ret = parse_picture_segment(avctx, buf, segment_length); > + break; > + case BUTTON_SEGMENT: > + ret = parse_button_segment(avctx, buf, segment_length); > + break; > + case DISPLAY_SEGMENT: > + *data_size = display_end_segment(avctx, data, buf, > segment_length); > + break; > + default: > + av_log(avctx, AV_LOG_ERROR, "Unknown interactive segment type > 0x%x, length %d\n", > + segment_type, segment_length); > + break; > + } > + if (ret<0) { > + int i; > + av_log(avctx, AV_LOG_DEBUG, "Error returned for segment length > %d with type %x\n", segment_length, segment_type); > + av_log(avctx, AV_LOG_DEBUG, "Segment Dump:"); > + for (i = 0; i < segment_length; i++) { > + if (i%16 == 0) > + av_log(avctx, AV_LOG_DEBUG, "\n%03X: %02x", i, buf[i]); > + else > + av_log(avctx, AV_LOG_DEBUG, " %02x", buf[i]); > + } > + av_log(avctx, AV_LOG_DEBUG, "\n"); > + } > + > + buf += segment_length; > + } > + > + return buf_size; > +} > + > +AVCodec ff_igsmenu_decoder = { > + .name = "igsmenu", > + .type = AVMEDIA_TYPE_OVERLAY, > + .id = CODEC_ID_HDMV_IGS_MENU, > + .priv_data_size = sizeof(IGSContext), > + .init = init_decoder, > + .close = close_decoder, > + .decode = decode, > + .long_name = NULL_IF_CONFIG_SMALL("HDMV Interactive Graphic Stream > menus"), > +}; > diff --git a/libavcodec/utils.c b/libavcodec/utils.c > index d2ee9f8..15b2d5d 100644 > --- a/libavcodec/utils.c > +++ b/libavcodec/utils.c > @@ -1368,6 +1368,39 @@ void avsubtitle_free(AVSubtitle *sub) > memset(sub, 0, sizeof(AVSubtitle)); > } > > +int avcodec_decode_overlay2(AVCodecContext *avctx, AVOverlay *overlay, > + int *got_overlay_ptr, > + AVPacket *avpkt) > +{ > + int ret; > + > + avctx->pkt = avpkt; > + *got_overlay_ptr = 0; > + ret = avctx->codec->decode(avctx, overlay, got_overlay_ptr, avpkt); > + if (*got_overlay_ptr) > + avctx->frame_number++; > + return ret; > +} > + > +void avoverlay_free(AVOverlay *overlay) > +{ > + int i; > + > + for (i = 0; i < overlay->num_rects; i++) > + { > + av_freep(&overlay->rects[i]->pict.data[0]); > + av_freep(&overlay->rects[i]->pict.data[1]); > + av_freep(&overlay->rects[i]->pict.data[2]); > + av_freep(&overlay->rects[i]->pict.data[3]); > + av_freep(&overlay->rects[i]); > + } > + > + av_freep(&overlay->rects); > + if (overlay->extra) av_freep(&overlay->extra); > + > + memset(overlay, 0, sizeof(AVOverlay)); > +} > + > av_cold int avcodec_close(AVCodecContext *avctx) > { > /* If there is a user-supplied mutex locking routine, call it. */ > @@ -1600,6 +1633,9 @@ void avcodec_string(char *buf, int buf_size, > AVCodecContext *enc, int encode) > case AVMEDIA_TYPE_SUBTITLE: > snprintf(buf, buf_size, "Subtitle: %s", codec_name); > break; > + case AVMEDIA_TYPE_OVERLAY: > + snprintf(buf, buf_size, "Overlay: %s", codec_name); > + break; > case AVMEDIA_TYPE_ATTACHMENT: > snprintf(buf, buf_size, "Attachment: %s", codec_name); > break; > diff --git a/libavutil/avutil.h b/libavutil/avutil.h > index 1c8e076..d81b01c 100644 > --- a/libavutil/avutil.h > +++ b/libavutil/avutil.h > @@ -230,6 +230,7 @@ enum AVMediaType { > AVMEDIA_TYPE_DATA, ///< Opaque data information usually > continuous > AVMEDIA_TYPE_SUBTITLE, > AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse > + AVMEDIA_TYPE_OVERLAY, > AVMEDIA_TYPE_NB > }; > _______________________________________________ libav-devel mailing list [email protected] https://lists.libav.org/mailman/listinfo/libav-devel
