// Make mpi point to buffer, assuming MMAL_ENCODING_I420. // buffer can be NULL. // Return the required buffer space. static size_t layout_buffer(struct mp_image *mpi, MMAL_BUFFER_HEADER_T *buffer, struct mp_image_params *params) { assert(params->imgfmt == IMGFMT_420P); mp_image_set_params(mpi, params); int w = MP_ALIGN_UP(params->w, ALIGN_W); int h = MP_ALIGN_UP(params->h, ALIGN_H); uint8_t *cur = buffer ? buffer->data : NULL; size_t size = 0; for (int i = 0; i < 3; i++) { int div = i ? 2 : 1; mpi->planes[i] = cur; mpi->stride[i] = w / div; size_t plane_size = h / div * mpi->stride[i]; if (cur) cur += plane_size; size += plane_size; } return size; }
// Initialize sub from sub->avsub. static void read_sub_bitmaps(struct sd *sd, struct sub *sub) { struct MPOpts *opts = sd->opts; struct sd_lavc_priv *priv = sd->priv; AVSubtitle *avsub = &sub->avsub; MP_TARRAY_GROW(priv, sub->inbitmaps, avsub->num_rects); packer_set_size(priv->packer, avsub->num_rects); // If we blur, we want a transparent region around the bitmap data to // avoid "cut off" artifacts on the borders. bool apply_blur = opts->sub_gauss != 0.0f; int extend = apply_blur ? 5 : 0; // Assume consumers may use bilinear scaling on it (2x2 filter) int padding = 1 + extend; priv->packer->padding = padding; // For the sake of libswscale, which in some cases takes sub-rects as // source images, and wants 16 byte start pointer and stride alignment. int align = 4; for (int i = 0; i < avsub->num_rects; i++) { struct AVSubtitleRect *r = avsub->rects[i]; struct sub_bitmap *b = &sub->inbitmaps[sub->count]; if (r->type != SUBTITLE_BITMAP) { MP_ERR(sd, "unsupported subtitle type from libavcodec\n"); continue; } if (!(r->flags & AV_SUBTITLE_FLAG_FORCED) && opts->forced_subs_only) continue; if (r->w <= 0 || r->h <= 0) continue; b->bitmap = r; // save for later (dumb hack to avoid more complexity) priv->packer->in[sub->count] = (struct pos){r->w + (align - 1), r->h}; sub->count++; } priv->packer->count = sub->count; if (packer_pack(priv->packer) < 0) { MP_ERR(sd, "Unable to pack subtitle bitmaps.\n"); sub->count = 0; } if (!sub->count) return; struct pos bb[2]; packer_get_bb(priv->packer, bb); sub->bound_w = bb[1].x; sub->bound_h = bb[1].y; if (!sub->data || sub->data->w < sub->bound_w || sub->data->h < sub->bound_h) { talloc_free(sub->data); sub->data = mp_image_alloc(IMGFMT_BGRA, priv->packer->w, priv->packer->h); if (!sub->data) { sub->count = 0; return; } talloc_steal(priv, sub->data); } for (int i = 0; i < sub->count; i++) { struct sub_bitmap *b = &sub->inbitmaps[i]; struct pos pos = priv->packer->result[i]; struct AVSubtitleRect *r = b->bitmap; #if HAVE_AV_SUBTITLE_NOPICT uint8_t **data = r->data; int *linesize = r->linesize; #else uint8_t **data = r->pict.data; int *linesize = r->pict.linesize; #endif b->w = r->w; b->h = r->h; b->x = r->x; b->y = r->y; // Choose such that the extended start position is aligned. pos.x = MP_ALIGN_UP(pos.x - extend, align) + extend; b->src_x = pos.x; b->src_y = pos.y; b->stride = sub->data->stride[0]; b->bitmap = sub->data->planes[0] + pos.y * b->stride + pos.x * 4; sub->src_w = FFMAX(sub->src_w, b->x + b->w); sub->src_h = FFMAX(sub->src_h, b->y + b->h); assert(r->nb_colors > 0); assert(r->nb_colors <= 256); uint32_t pal[256] = {0}; memcpy(pal, data[1], r->nb_colors * 4); convert_pal(pal, 256, opts->sub_gray); for (int y = -padding; y < b->h + padding; y++) { uint32_t *out = (uint32_t*)((char*)b->bitmap + y * b->stride); int start = 0; for (int x = -padding; x < 0; x++) out[x] = 0; if (y >= 0 && y < b->h) { uint8_t *in = data[0] + y * linesize[0]; for (int x = 0; x < b->w; x++) *out++ = pal[*in++]; start = b->w; } for (int x = start; x < b->w + padding; x++) *out++ = 0; } b->bitmap = (char*)b->bitmap - extend * b->stride - extend * 4; b->src_x -= extend; b->src_y -= extend; b->x -= extend; b->y -= extend; b->w += extend * 2; b->h += extend * 2; if (apply_blur) mp_blur_rgba_sub_bitmap(b, opts->sub_gauss); } } static void decode(struct sd *sd, struct demux_packet *packet) { struct MPOpts *opts = sd->opts; struct sd_lavc_priv *priv = sd->priv; AVCodecContext *ctx = priv->avctx; double pts = packet->pts; double endpts = MP_NOPTS_VALUE; double duration = packet->duration; AVSubtitle sub; AVPacket pkt; // libavformat sets duration==0, even if the duration is unknown. Some files // also have actually subtitle packets with duration explicitly set to 0 // (yes, at least some of such mkv files were muxed by libavformat). // Assume there are no bitmap subs that actually use duration==0 for // hidden subtitle events. if (duration == 0) duration = -1; if (pts == MP_NOPTS_VALUE) MP_WARN(sd, "Subtitle with unknown start time.\n"); mp_set_av_packet(&pkt, packet, &priv->pkt_timebase); int got_sub; int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); if (res < 0 || !got_sub) return; if (sub.pts != AV_NOPTS_VALUE) pts = sub.pts / (double)AV_TIME_BASE; if (pts != MP_NOPTS_VALUE) { if (sub.end_display_time > sub.start_display_time && sub.end_display_time != UINT32_MAX) { duration = (sub.end_display_time - sub.start_display_time) / 1000.0; } pts += sub.start_display_time / 1000.0; if (duration >= 0) endpts = pts + duration; // set end time of previous sub struct sub *prev = &priv->subs[0]; if (prev->valid) { if (prev->endpts == MP_NOPTS_VALUE || prev->endpts > pts) prev->endpts = pts; if (opts->sub_fix_timing && pts - prev->endpts <= SUB_GAP_THRESHOLD) prev->endpts = pts; for (int n = 0; n < priv->num_seekpoints; n++) { if (priv->seekpoints[n].pts == prev->pts) { priv->seekpoints[n].endpts = prev->endpts; break; } } } // This subtitle packet only signals the end of subtitle display. if (!sub.num_rects) { avsubtitle_free(&sub); return; } } alloc_sub(priv); struct sub *current = &priv->subs[0]; current->valid = true; current->pts = pts; current->endpts = endpts; current->avsub = sub; read_sub_bitmaps(sd, current); if (pts != MP_NOPTS_VALUE) { for (int n = 0; n < priv->num_seekpoints; n++) { if (priv->seekpoints[n].pts == pts) goto skip; } // Set arbitrary limit as safe-guard against insane files. if (priv->num_seekpoints >= 10000) MP_TARRAY_REMOVE_AT(priv->seekpoints, priv->num_seekpoints, 0); MP_TARRAY_APPEND(priv, priv->seekpoints, priv->num_seekpoints, (struct seekpoint){.pts = pts, .endpts = endpts}); skip: ; } }