static bstr read_file(struct mp_log *log, const char *filename) { FILE *f = fopen(filename, "rb"); if (!f) { mp_verbose(log, "Can't open config file: %s\n", mp_strerror(errno)); return (bstr){0}; } char *data = talloc_array(NULL, char, 0); size_t size = 0; while (1) { size_t left = talloc_get_size(data) - size; if (!left) { MP_TARRAY_GROW(NULL, data, size + 1); continue; } size_t s = fread(data + size, 1, left, f); if (!s) { if (ferror(f)) mp_err(log, "Error reading config file.\n"); fclose(f); MP_TARRAY_APPEND(NULL, data, size, 0); return (bstr){data, size - 1}; } size += s; } assert(0); }
struct cue_file *mp_parse_cue(struct bstr data) { struct cue_file *f = talloc_zero(NULL, struct cue_file); f->tags = talloc_zero(f, struct mp_tags); data = skip_utf8_bom(data); char *filename = NULL; // Global metadata, and copied into new tracks. struct cue_track proto_track = {0}; struct cue_track *cur_track = NULL; while (data.len) { struct bstr param; int cmd = read_cmd(&data, ¶m); switch (cmd) { case CUE_ERROR: talloc_free(f); return NULL; case CUE_TRACK: { MP_TARRAY_GROW(f, f->tracks, f->num_tracks); f->num_tracks += 1; cur_track = &f->tracks[f->num_tracks - 1]; *cur_track = proto_track; cur_track->tags = talloc_zero(f, struct mp_tags); break; } case CUE_TITLE: case CUE_PERFORMER: { static const char *metanames[] = { [CUE_TITLE] = "title", [CUE_PERFORMER] = "performer", }; struct mp_tags *tags = cur_track ? cur_track->tags : f->tags; mp_tags_set_bstr(tags, bstr0(metanames[cmd]), param); break; } case CUE_INDEX: { int type = read_int_2(¶m); double time = read_time(¶m); if (cur_track) { if (type == 1) { cur_track->start = time; cur_track->filename = filename; } else if (type == 0) { cur_track->pregap_start = time; } } break; } case CUE_FILE: // NOTE: FILE comes before TRACK, so don't use cur_track->filename filename = read_quoted(f, ¶m); break; } } return f; }
static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, int format, double pts, struct sub_bitmaps *res) { struct sd_ass_priv *ctx = sd->priv; struct MPOpts *opts = sd->opts; bool no_ass = !opts->ass_enabled || ctx->on_top || opts->ass_style_override == 5; bool converted = ctx->is_converted || no_ass; ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track; ASS_Renderer *renderer = ctx->ass_renderer; if (pts == MP_NOPTS_VALUE || !renderer) return; double scale = dim.display_par; if (!converted && (!opts->ass_style_override || opts->ass_vsfilter_aspect_compat)) { // Let's use the original video PAR for vsfilter compatibility: double par = ctx->video_params.p_w / (double)ctx->video_params.p_h; if (isnormal(par)) scale *= par; } configure_ass(sd, &dim, converted, track); ass_set_pixel_aspect(renderer, scale); if (!converted && (!opts->ass_style_override || opts->ass_vsfilter_blur_compat)) { ass_set_storage_size(renderer, ctx->video_params.w, ctx->video_params.h); } else { ass_set_storage_size(renderer, 0, 0); } long long ts = find_timestamp(sd, pts); if (ctx->duration_unknown && pts != MP_NOPTS_VALUE) { mp_ass_flush_old_events(track, ts); ctx->num_seen_packets = 0; sd->preload_ok = false; } if (no_ass) fill_plaintext(sd, pts); int changed; ASS_Image *imgs = ass_render_frame(renderer, track, ts, &changed); mp_ass_packer_pack(ctx->packer, &imgs, 1, changed, format, res); if (!converted && res->num_parts > 0) { // mangle_colors() modifies the color field, so copy the thing. MP_TARRAY_GROW(ctx, ctx->bs, res->num_parts); memcpy(ctx->bs, res->parts, sizeof(ctx->bs[0]) * res->num_parts); res->parts = ctx->bs; mangle_colors(sd, res); } }
static int read_sub(void *ta_parent, struct mpv_node *dst, char **src, int max_depth) { bool is_arr = eat_c(src, '['); bool is_obj = !is_arr && eat_c(src, '{'); if (!is_arr && !is_obj) return -1; // not an array or object char term = is_obj ? '}' : ']'; struct mpv_node_list *list = talloc_zero(ta_parent, struct mpv_node_list); while (1) { eat_ws(src); if (eat_c(src, term)) break; if (list->num > 0 && !eat_c(src, ',')) return -1; // missing ',' eat_ws(src); // non-standard extension: allow a trailing "," if (eat_c(src, term)) break; if (is_obj) { struct mpv_node keynode; // non-standard extension: allow unquoted strings as keys if (read_id(list, &keynode, src) < 0 && read_str(list, &keynode, src) < 0) return -1; // key is not a string eat_ws(src); // non-standard extension: allow "=" instead of ":" if (!eat_c(src, ':') && !eat_c(src, '=')) return -1; // ':' missing eat_ws(src); MP_TARRAY_GROW(list, list->keys, list->num); list->keys[list->num] = keynode.u.string; } MP_TARRAY_GROW(list, list->values, list->num); if (json_parse(ta_parent, &list->values[list->num], src, max_depth) < 0) return -1; list->num++; } dst->format = is_obj ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; dst->u.list = list; return 0; }
static void get_bitmaps(struct sd *sd, struct mp_osd_res d, double pts, struct sub_bitmaps *res) { struct sd_lavc_priv *priv = sd->priv; struct MPOpts *opts = sd->opts; struct sub *current = NULL; for (int n = 0; n < MAX_QUEUE; n++) { struct sub *sub = &priv->subs[n]; if (!sub->valid) continue; if (pts == MP_NOPTS_VALUE || ((sub->pts == MP_NOPTS_VALUE || pts >= sub->pts) && (sub->endpts == MP_NOPTS_VALUE || pts < sub->endpts))) { // Ignore "trailing" subtitles with unknown length after 1 minute. if (sub->endpts == MP_NOPTS_VALUE && pts >= sub->pts + 60) break; current = sub; break; } } if (!current) return; MP_TARRAY_GROW(priv, priv->outbitmaps, current->count); for (int n = 0; n < current->count; n++) priv->outbitmaps[n] = current->inbitmaps[n]; res->parts = priv->outbitmaps; res->num_parts = current->count; if (priv->displayed_id != current->id) res->change_id++; priv->displayed_id = current->id; res->format = SUBBITMAP_INDEXED; double video_par = 0; if (priv->avctx->codec_id == AV_CODEC_ID_DVD_SUBTITLE && opts->stretch_dvd_subs) { // For DVD subs, try to keep the subtitle PAR at display PAR. double par = (priv->video_params.d_w / (double)priv->video_params.d_h) / (priv->video_params.w / (double)priv->video_params.h); if (isnormal(par)) video_par = par; } if (priv->avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE) video_par = -1; int insize[2]; get_resolution(sd, insize); osd_rescale_bitmaps(res, insize[0], insize[1], d, video_par); }
static void mpv_node_array_add(void *ta_parent, mpv_node *src, mpv_node *val) { if (src->format != MPV_FORMAT_NODE_ARRAY) return; if (!src->u.list) src->u.list = talloc_zero(ta_parent, mpv_node_list); MP_TARRAY_GROW(src->u.list, src->u.list->values, src->u.list->num); static const struct m_option type = { .type = CONF_TYPE_NODE }; m_option_get_node(&type, ta_parent, &src->u.list->values[src->u.list->num], val); src->u.list->num++; }
// 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: ; } }
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 duration = packet->duration; AVSubtitle sub; AVPacket pkt; // libavformat sets duration==0, even if the duration is unknown. // 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"); av_init_packet(&pkt); pkt.data = packet->buffer; pkt.size = packet->len; int got_sub; int res = avcodec_decode_subtitle2(ctx, &sub, &got_sub, &pkt); if (res < 0 || !got_sub) return; 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; } double endpts = MP_NOPTS_VALUE; if (pts != MP_NOPTS_VALUE && duration >= 0) endpts = pts + duration; // set end time of previous sub if (priv->subs[0].endpts == MP_NOPTS_VALUE || priv->subs[0].endpts > pts) priv->subs[0].endpts = pts; alloc_sub(priv); struct sub *current = &priv->subs[0]; current->valid = true; current->pts = pts; current->endpts = endpts; current->avsub = sub; MP_TARRAY_GROW(priv, current->inbitmaps, sub.num_rects); MP_TARRAY_GROW(priv, current->imgs, sub.num_rects); for (int i = 0; i < sub.num_rects; i++) { struct AVSubtitleRect *r = sub.rects[i]; struct sub_bitmap *b = ¤t->inbitmaps[current->count]; struct osd_bmp_indexed *img = ¤t->imgs[current->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; img->bitmap = r->pict.data[0]; assert(r->nb_colors > 0); assert(r->nb_colors * 4 <= sizeof(img->palette)); memcpy(img->palette, r->pict.data[1], r->nb_colors * 4); b->bitmap = img; b->stride = r->pict.linesize[0]; b->w = r->w; b->h = r->h; b->x = r->x; b->y = r->y; current->count++; } }
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; MP_TARRAY_GROW(priv, current->inbitmaps, sub.num_rects); MP_TARRAY_GROW(priv, current->imgs, sub.num_rects); for (int i = 0; i < sub.num_rects; i++) { struct AVSubtitleRect *r = sub.rects[i]; struct sub_bitmap *b = ¤t->inbitmaps[current->count]; struct osd_bmp_indexed *img = ¤t->imgs[current->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; #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 img->bitmap = data[0]; assert(r->nb_colors > 0); assert(r->nb_colors * 4 <= sizeof(img->palette)); memcpy(img->palette, data[1], r->nb_colors * 4); b->bitmap = img; b->stride = linesize[0]; b->w = r->w; b->h = r->h; b->x = r->x; b->y = r->y; current->count++; } 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: ; }