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 +++ 5 files changed, 819 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; -- 1.7.9.5 _______________________________________________ libav-devel mailing list [email protected] https://lists.libav.org/mailman/listinfo/libav-devel
