static bool try_open(struct MPContext *mpctx, char *filename) { struct bstr bfilename = bstr0(filename); // Avoid trying to open itself or another .cue file. Best would be // to check the result of demuxer auto-detection, but the demuxer // API doesn't allow this without opening a full demuxer. if (bstr_case_endswith(bfilename, bstr0(".cue")) || bstrcasecmp(bstr0(mpctx->demuxer->filename), bfilename) == 0) return false; struct stream *s = stream_open(filename, mpctx->opts); if (!s) return false; struct demuxer *d = demux_open(s, NULL, NULL, mpctx->opts); // Since .bin files are raw PCM data with no headers, we have to explicitly // open them. Also, try to avoid to open files that are most likely not .bin // files, as that would only play noise. Checking the file extension is // fragile, but it's about the only way we have. // TODO: maybe also could check if the .bin file is a multiple of the Audio // CD sector size (2352 bytes) if (!d && bstr_case_endswith(bfilename, bstr0(".bin"))) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: Opening as BIN file!\n"); d = demux_open(s, "rawaudio", NULL, mpctx->opts); } if (d) { add_source(mpctx, d); return true; } mp_msg(MSGT_CPLAYER, MSGL_ERR, "Could not open source '%s'!\n", filename); free_stream(s); return false; }
static bool try_open(struct timeline *tl, char *filename) { struct bstr bfilename = bstr0(filename); // Avoid trying to open itself or another .cue file. Best would be // to check the result of demuxer auto-detection, but the demuxer // API doesn't allow this without opening a full demuxer. if (bstr_case_endswith(bfilename, bstr0(".cue")) || bstrcasecmp(bstr0(tl->demuxer->filename), bfilename) == 0) return false; struct demuxer *d = demux_open_url(filename, NULL, tl->cancel, tl->global); // Since .bin files are raw PCM data with no headers, we have to explicitly // open them. Also, try to avoid to open files that are most likely not .bin // files, as that would only play noise. Checking the file extension is // fragile, but it's about the only way we have. // TODO: maybe also could check if the .bin file is a multiple of the Audio // CD sector size (2352 bytes) if (!d && bstr_case_endswith(bfilename, bstr0(".bin"))) { MP_WARN(tl, "CUE: Opening as BIN file!\n"); struct demuxer_params p = {.force_format = "rawaudio"}; d = demux_open_url(filename, &p, tl->cancel, tl->global); } if (d) { add_source(tl, d); return true; } MP_ERR(tl, "Could not open source '%s'!\n", filename); return false; }
static int parse_ref_init(struct pl_parser *p) { bstr line = bstr_strip(pl_get_line(p)); if (!bstr_equals0(line, "[Reference]")) return -1; // ASF http streaming redirection - this is needed because ffmpeg http:// // and mmsh:// can not automatically switch automatically between each // others. Both protocols use http - MMSH requires special http headers // to "activate" it, and will in other cases return this playlist. static const char *const mmsh_types[] = {"audio/x-ms-wax", "audio/x-ms-wma", "video/x-ms-asf", "video/x-ms-afs", "video/x-ms-wmv", "video/x-ms-wma", "application/x-mms-framed", "application/vnd.ms.wms-hdr.asfv1", NULL}; bstr burl = bstr0(p->s->url); if (bstr_eatstart0(&burl, "http://") && check_mimetype(p->s, mmsh_types)) { MP_INFO(p, "Redirecting to mmsh://\n"); playlist_add_file(p->pl, talloc_asprintf(p, "mmsh://%.*s", BSTR_P(burl))); return 0; } while (!pl_eof(p)) { line = bstr_strip(pl_get_line(p)); if (bstr_case_startswith(line, bstr0("Ref"))) { bstr_split_tok(line, "=", &(bstr){0}, &line); if (line.len) pl_add(p, line); } } return 0; }
static stream_t *open_stream_plugin(const stream_info_t *sinfo, const char *filename, int mode, struct MPOpts *options, int *file_format, int *ret, char **redirected_url) { void* arg = NULL; stream_t* s; m_struct_t* desc = (m_struct_t*)sinfo->opts; // Parse options if(desc) { arg = m_struct_alloc(desc); if(sinfo->opts_url) { m_option_t url_opt = { "stream url", arg , CONF_TYPE_CUSTOM_URL, 0, 0 ,0, (void *)sinfo->opts }; if (m_option_parse(&url_opt, bstr0("stream url"), bstr0(filename), arg) < 0) { mp_tmsg(MSGT_OPEN,MSGL_ERR, "URL parsing failed on url %s\n",filename); m_struct_free(desc,arg); return NULL; } } } s = new_stream(-2,-2); s->opts = options; s->url=strdup(filename); s->flags |= mode; *ret = sinfo->open(s,mode,arg,file_format); if((*ret) != STREAM_OK) { #ifdef CONFIG_NETWORKING if (*ret == STREAM_REDIRECTED && redirected_url) { if (s->streaming_ctrl && s->streaming_ctrl->url && s->streaming_ctrl->url->url) *redirected_url = strdup(s->streaming_ctrl->url->url); else *redirected_url = NULL; } streaming_ctrl_free(s->streaming_ctrl); #endif free(s->url); free(s); return NULL; } if(s->type <= -2) mp_msg(MSGT_OPEN,MSGL_WARN, "Warning streams need a type !!!!\n"); if(s->flags & MP_STREAM_SEEK && !s->seek) s->flags &= ~MP_STREAM_SEEK; if(s->seek && !(s->flags & MP_STREAM_SEEK)) s->flags |= MP_STREAM_SEEK; s->mode = mode; mp_msg(MSGT_OPEN,MSGL_V, "STREAM: [%s] %s\n",sinfo->name,filename); mp_msg(MSGT_OPEN,MSGL_V, "STREAM: Description: %s\n",sinfo->info); mp_msg(MSGT_OPEN,MSGL_V, "STREAM: Author: %s\n", sinfo->author); mp_msg(MSGT_OPEN,MSGL_V, "STREAM: Comment: %s\n", sinfo->comment); return s; }
// Add entry, but only if it's not yet on the list, and if the codec matches. // If codec == NULL, don't compare codecs. static void add_new(struct mp_decoder_list *to, struct mp_decoder_entry *entry, const char *codec) { if (!entry || (codec && strcmp(entry->codec, codec) != 0)) return; if (!find_decoder(to, bstr0(entry->family), bstr0(entry->decoder))) mp_add_decoder_entry(to, entry); }
static void add_indent(bstr *b, int indent) { if (indent < 0) return; bstr_xappend(NULL, b, bstr0("\n")); for (int n = 0; n < indent; n++) bstr_xappend(NULL, b, bstr0(" ")); }
// Select a decoder from the given list for the given codec. The selection // can be influenced by the selection string, which can specify a priority // list of preferred decoders. // This returns a list of decoders to try, with the preferred decoders first. // The selection string corresponds to --vd/--ad directly, and has the // following syntax: // selection = [<entry> ("," <entry>)*] // entry = <family> ":" <decoder> // prefer decoder // entry = <family> ":*" // prefer all decoders // entry = "+" <family> ":" <decoder> // force a decoder // entry = "-" <family> ":" <decoder> // exclude a decoder // entry = "-" // don't add fallback decoders // Forcing a decoder means it's added even if the codec mismatches. struct mp_decoder_list *mp_select_decoders(struct mp_decoder_list *all, const char *codec, const char *selection) { struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list); struct mp_decoder_list *remove = talloc_zero(NULL, struct mp_decoder_list); if (!codec) codec = "unknown"; bool stop = false; bstr sel = bstr0(selection); while (sel.len) { bstr entry; bstr_split_tok(sel, ",", &entry, &sel); if (bstr_equals0(entry, "-")) { stop = true; break; } bool force = bstr_eatstart0(&entry, "+"); bool exclude = !force && bstr_eatstart0(&entry, "-"); struct mp_decoder_list *dest = exclude ? remove : list; bstr family, decoder; if (!bstr_split_tok(entry, ":", &family, &decoder)) { mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Decoders must be specified as " "'family:decoder' for the --ad/--vd options.\n"); break; } if (bstr_equals0(decoder, "*")) { for (int n = 0; n < all->num_entries; n++) { struct mp_decoder_entry *cur = &all->entries[n]; if (bstr_equals0(family, cur->family)) add_new(dest, cur, codec); } } else { add_new(dest, find_decoder(all, family, decoder), force ? NULL : codec); } } if (!stop) { // Add the remaining codecs which haven't been added yet for (int n = 0; n < all->num_entries; n++) add_new(list, &all->entries[n], codec); } for (int n = 0; n < remove->num_entries; n++) { struct mp_decoder_entry *ex = &remove->entries[n]; struct mp_decoder_entry *del = find_decoder(list, bstr0(ex->family), bstr0(ex->decoder)); if (del) { int index = del - &list->entries[0]; MP_TARRAY_REMOVE_AT(list->entries, list->num_entries, index); } } talloc_free(remove); return list; }
void playlist_add_base_path(struct playlist *pl, bstr base_path) { if (base_path.len == 0 || bstrcmp0(base_path, ".") == 0) return; for (struct playlist_entry *e = pl->first; e; e = e->next) { if (!mp_is_url(bstr0(e->filename))) { char *new_file = mp_path_join(e, base_path, bstr0(e->filename)); talloc_free(e->filename); e->filename = new_file; } } }
static bool open_source(struct MPContext *mpctx, struct bstr filename) { void *ctx = talloc_new(NULL); bool res = false; struct bstr dirname = mp_dirname(mpctx->demuxer->filename); struct bstr base_filename = bstr0(mp_basename(bstrdup0(ctx, filename))); if (!base_filename.len) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: Invalid audio filename in .cue file!\n"); } else { char *fullname = mp_path_join(ctx, dirname, base_filename); if (try_open(mpctx, fullname)) { res = true; goto out; } } // Try an audio file with the same name as the .cue file (but different // extension). // Rationale: this situation happens easily if the audio file or both files // are renamed. struct bstr cuefile = bstr_strip_ext(bstr0(mp_basename(mpctx->demuxer->filename))); DIR *d = opendir(bstrdup0(ctx, dirname)); if (!d) goto out; struct dirent *de; while ((de = readdir(d))) { char *dename0 = de->d_name; struct bstr dename = bstr0(dename0); if (bstr_case_startswith(dename, cuefile)) { mp_msg(MSGT_CPLAYER, MSGL_WARN, "CUE: No useful audio filename " "in .cue file found, trying with '%s' instead!\n", dename0); if (try_open(mpctx, mp_path_join(ctx, dirname, dename))) { res = true; break; } } } closedir(d); out: talloc_free(ctx); if (!res) mp_msg(MSGT_CPLAYER, MSGL_ERR, "CUE: Could not open audio file!\n"); return res; }
// selection is a ","-separated list of decoders, all in the given family. struct mp_decoder_list *mp_select_decoder_list(struct mp_decoder_list *all, const char *codec, const char *family, const char *selection) { struct mp_decoder_list *list = talloc_zero(NULL, struct mp_decoder_list); bstr sel = bstr0(selection); while (sel.len) { bstr decoder; bstr_split_tok(sel, ",", &decoder, &sel); add_new(list, find_decoder(all, bstr0(family), decoder), codec); } return list; }
char *mp_get_win_config_path(const char *filename) { wchar_t w_appdir[MAX_PATH + 1] = {0}; wchar_t w_exedir[MAX_PATH + 1] = {0}; char *res = NULL; void *tmp = talloc_new(NULL); #ifndef __CYGWIN__ if (SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, w_appdir) != S_OK) w_appdir[0] = '\0'; #endif get_exe_dir(w_exedir); if (filename && filename[0] && w_exedir[0]) { char *dir = mp_to_utf8(tmp, w_exedir); char *temp = mp_path_join(tmp, bstr0(dir), bstr0("mpv")); res = mp_path_join(NULL, bstr0(temp), bstr0(filename)); if (!mp_path_exists(res) || mp_path_isdir(res)) { talloc_free(res); res = NULL; } } if (!res && w_appdir[0]) { char *dir = mp_to_utf8(tmp, w_appdir); char *temp = mp_path_join(tmp, bstr0(dir), bstr0("mpv")); res = mp_path_join(NULL, bstr0(temp), bstr0(filename)); } talloc_free(tmp); return res; }
static int open_f(stream_t *stream) { stream->fill_buffer = fill_buffer; stream->seek = seek; stream->seekable = true; stream->control = control; stream->read_chunk = 1024 * 1024; struct priv *p = talloc_zero(stream, struct priv); stream->priv = p; // Initial data bstr data = bstr0(stream->url); bool use_hex = bstr_eatstart0(&data, "hex://"); if (!use_hex) bstr_eatstart0(&data, "memory://"); stream_control(stream, STREAM_CTRL_SET_CONTENTS, &data); if (use_hex && !bstr_decode_hex(stream, p->data, &p->data)) { MP_FATAL(stream, "Invalid data.\n"); return STREAM_ERROR; } return STREAM_OK; }
static void fill_plaintext(struct sd *sd, double pts) { struct sd_ass_priv *ctx = sd->priv; ASS_Track *track = ctx->shadow_track; ass_flush_events(track); char *text = get_text(sd, pts); if (!text) return; bstr dst = {0}; while (*text) { if (*text == '{') bstr_xappend(NULL, &dst, bstr0("\\")); bstr_xappend(NULL, &dst, (bstr){text, 1}); // Break ASS escapes with U+2060 WORD JOINER if (*text == '\\') mp_append_utf8_bstr(NULL, &dst, 0x2060); text++; } if (!dst.start || !dst.start[0]) return; int n = ass_alloc_event(track); ASS_Event *event = track->events + n; event->Start = 0; event->Duration = INT_MAX; event->Style = track->default_style; event->Text = strdup(dst.start); if (track->default_style < track->n_styles) track->styles[track->default_style].Alignment = ctx->on_top ? 6 : 2; }
static int json_append_str(char **dst, struct mpv_node *src, int indent) { bstr buffer = bstr0(*dst); int r = json_append(&buffer, src, indent); *dst = buffer.start; return r; }
static int init(struct ao *ao) { struct priv *priv = ao->priv; ao->untimed = priv->untimed; struct mp_chmap_sel sel = {.tmp = ao}; if (priv->channel_layouts) { for (int n = 0; priv->channel_layouts[n]; n++) { struct mp_chmap map = {0}; if (!mp_chmap_from_str(&map, bstr0(priv->channel_layouts[n]))) { MP_FATAL(ao, "Invalid channel map in option.\n"); return -1; } mp_chmap_sel_add_map(&sel, &map); } } else { mp_chmap_sel_add_any(&sel); } if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) mp_chmap_from_channels(&ao->channels, 2); priv->latency = priv->latency_sec * ao->samplerate; // A "buffer" for this many seconds of audio int bursts = (int)(ao->samplerate * priv->bufferlen + 1) / priv->outburst; priv->buffersize = priv->outburst * bursts + priv->latency; priv->last_time = mp_time_sec(); return 0; }
struct playlist_entry *playlist_entry_new(const char *filename) { struct playlist_entry *e = talloc_zero(NULL, struct playlist_entry); char *local_filename = mp_file_url_to_filename(e, bstr0(filename)); e->filename = local_filename ? local_filename : talloc_strdup(e, filename); return e; }
static int parse_dir(struct pl_parser *p) { if (p->real_stream->type != STREAMTYPE_DIR) return -1; if (p->probing) return 0; char *path = mp_file_get_path(p, bstr0(p->real_stream->url)); char **files = NULL; int num_files = 0; struct stat dir_stack[MAX_DIR_STACK]; scan_dir(p, path, dir_stack, 0, &files, &num_files); if (files) qsort(files, num_files, sizeof(files[0]), cmp_filename); for (int n = 0; n < num_files; n++) playlist_add_file(p->pl, files[n]); p->add_base = false; return num_files > 0 ? 0 : -1; }
struct lavc_conv *lavc_conv_create(struct mp_log *log, const char *codec_name, char *extradata, int extradata_len) { struct lavc_conv *priv = talloc_zero(NULL, struct lavc_conv); priv->log = log; priv->cur_list = talloc_array(priv, char*, 0); priv->codec = talloc_strdup(priv, codec_name); AVCodecContext *avctx = NULL; const char *fmt = get_lavc_format(priv->codec); AVCodec *codec = avcodec_find_decoder(mp_codec_to_av_codec_id(fmt)); if (!codec) goto error; avctx = avcodec_alloc_context3(codec); if (!avctx) goto error; avctx->extradata_size = extradata_len; avctx->extradata = talloc_memdup(priv, extradata, extradata_len); if (avcodec_open2(avctx, codec, NULL) < 0) goto error; // Documented as "set by libavcodec", but there is no other way avctx->time_base = (AVRational) {1, 1000}; priv->avctx = avctx; priv->extradata = talloc_strndup(priv, avctx->subtitle_header, avctx->subtitle_header_size); disable_styles(bstr0(priv->extradata)); return priv; error: MP_FATAL(priv, "Could not open libavcodec subtitle converter\n"); av_free(avctx); talloc_free(priv); return NULL; }
static int parse_mov_rtsptext(struct pl_parser *p) { bstr line = pl_get_line(p); if (!bstr_eatstart(&line, bstr0("RTSPtext"))) return -1; if (p->probing) return 0; line = bstr_strip(line); do { if (bstr_case_startswith(line, bstr0("rtsp://"))) { pl_add(p, line); return 0; } } while (!pl_eof(p) && (line = bstr_strip(pl_get_line(p))).len); return -1; }
/* Write the contents of *src as JSON, and append the JSON string to *dst. * This will use strlen() to determine the start offset, and ta_get_size() * and ta_realloc() to extend the memory allocation of *dst. * Returns: 0 on success, <0 on failure. */ int json_write(char **dst, struct mpv_node *src) { bstr buffer = bstr0(*dst); int r = json_append(&buffer, src); *dst = buffer.start; return r; }
static int read_str(void *ta_parent, struct mpv_node *dst, char **src) { if (!eat_c(src, '"')) return -1; // not a string char *str = *src; char *cur = str; bool has_escapes = false; while (cur[0] && cur[0] != '"') { if (cur[0] == '\\') { has_escapes = true; // skip >\"< and >\\< (latter to handle >\\"< correctly) if (cur[1] == '"' || cur[1] == '\\') cur++; } cur++; } if (cur[0] != '"') return -1; // invalid termination // Mutate input string so we have a null-terminated string to the literal. // This is a stupid micro-optimization, so we can avoid allocation. cur[0] = '\0'; *src = cur + 1; if (has_escapes) { bstr unescaped = {0}; bstr r = bstr0(str); if (!mp_append_escaped_string(ta_parent, &unescaped, &r)) return -1; // broken escapes str = unescaped.start; // the function guarantees null-termination } dst->format = MPV_FORMAT_STRING; dst->u.string = str; return 0; }
static void mp_load_per_file_config(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; char *confpath; char cfg[512]; const char *file = mpctx->filename; if (snprintf(cfg, sizeof(cfg), "%s.conf", file) >= sizeof(cfg)) { MP_WARN(mpctx, "Filename is too long, " "can not load file or directory specific config files\n"); return; } char *name = mp_basename(cfg); if (opts->use_filedir_conf) { bstr dir = mp_dirname(cfg); char *dircfg = mp_path_join(NULL, dir, bstr0("mpv.conf")); try_load_config(mpctx, dircfg, FILE_LOCAL_FLAGS); talloc_free(dircfg); if (try_load_config(mpctx, cfg, FILE_LOCAL_FLAGS)) return; } if ((confpath = mp_find_user_config_file(NULL, mpctx->global, name))) { try_load_config(mpctx, confpath, FILE_LOCAL_FLAGS); talloc_free(confpath); } }
void mp_load_auto_profiles(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; mp_auto_load_profile(mpctx, "protocol", mp_split_proto(bstr0(mpctx->filename), NULL)); mp_auto_load_profile(mpctx, "extension", bstr0(mp_splitext(mpctx->filename, NULL))); mp_load_per_file_config(mpctx); if (opts->vo.video_driver_list) mp_auto_load_profile(mpctx, "vo", bstr0(opts->vo.video_driver_list[0].name)); if (opts->audio_driver_list) mp_auto_load_profile(mpctx, "ao", bstr0(opts->audio_driver_list[0].name)); }
// Iterate entries. The first call establishes the first entry. Returns false // if no entry found, otherwise returns true and sets mpa->entry/entry_filename. bool mp_archive_next_entry(struct mp_archive *mpa) { mpa->entry = NULL; talloc_free(mpa->entry_filename); mpa->entry_filename = NULL; while (!mp_cancel_test(mpa->primary_src->cancel)) { struct archive_entry *entry; int r = archive_read_next_header(mpa->arch, &entry); if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) MP_ERR(mpa, "%s\n", archive_error_string(mpa->arch)); if (r < ARCHIVE_WARN) { MP_FATAL(mpa, "could not read archive entry\n"); break; } if (archive_entry_filetype(entry) != AE_IFREG) continue; // Some archives may have no filenames, or libarchive won't return some. const char *fn = archive_entry_pathname(entry); char buf[64]; if (!fn || bstr_validate_utf8(bstr0(fn)) < 0) { snprintf(buf, sizeof(buf), "mpv_unknown#%d", mpa->entry_num); fn = buf; } mpa->entry = entry; mpa->entry_filename = talloc_strdup(mpa, fn); mpa->entry_num += 1; return true; } return false; }
static void write_arg(bstr *cmdline, char *arg) { // Empty args must be represented as an empty quoted string if (!arg[0]) { bstr_xappend(NULL, cmdline, bstr0("\"\"")); return; } // If the string doesn't have characters that need to be escaped, it's best // to leave it alone for the sake of Windows programs that don't process // quoted args correctly. if (!strpbrk(arg, " \t\"")) { bstr_xappend(NULL, cmdline, bstr0(arg)); return; } // If there are characters that need to be escaped, write a quoted string bstr_xappend(NULL, cmdline, bstr0("\"")); // Escape the argument. To match the behavior of CommandLineToArgvW, // backslashes are only escaped if they appear before a quote or the end of // the string. int num_slashes = 0; for (int pos = 0; arg[pos]; pos++) { switch (arg[pos]) { case '\\': // Count consecutive backslashes num_slashes++; break; case '"': // Write the argument up to the point before the quote bstr_xappend(NULL, cmdline, (struct bstr){arg, pos}); arg += pos; pos = 0; // Double backslashes preceding the quote for (int i = 0; i < num_slashes; i++) bstr_xappend(NULL, cmdline, bstr0("\\")); num_slashes = 0; // Escape the quote itself bstr_xappend(NULL, cmdline, bstr0("\\")); break; default: num_slashes = 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 bool test_ext_list(bstr ext, const char *const *list) { for (int n = 0; list[n]; n++) { if (bstrcasecmp(bstr0(list[n]), ext) == 0) return true; } return false; }
static bool is_sub_ext(bstr ext) { for (int n = 0; sub_exts[n]; n++) { if (bstrcasecmp(bstr0(sub_exts[n]), ext) == 0) return true; } return false; }
static int name_to_imgfmt(const char *name) { for (int n = 0; format_names[n].name; n++) { if (strcasecmp(format_names[n].name, name) == 0) return format_names[n].fmt; } return mp_imgfmt_from_name(bstr0(name), false); }
static int d_open(demuxer_t *demuxer, enum demux_check check) { struct priv *p = demuxer->priv = talloc_zero(demuxer, struct priv); if (check != DEMUX_CHECK_FORCE) return -1; char *demux = "+lavf"; if (demuxer->stream->uncached_type == STREAMTYPE_CDDA) demux = "+rawaudio"; char *t = NULL; stream_control(demuxer->stream, STREAM_CTRL_GET_DISC_NAME, &t); if (t) { mp_tags_set_bstr(demuxer->metadata, bstr0("TITLE"), bstr0(t)); talloc_free(t); } // Initialize the playback time. We need to read _some_ data to get the // correct stream-layer time (at least with libdvdnav). stream_peek(demuxer->stream, 1); reset_pts(demuxer); p->slave = demux_open(demuxer->stream, demux, NULL, demuxer->global); if (!p->slave) return -1; // So that we don't miss initial packets of delayed subtitle streams. demux_set_stream_autoselect(p->slave, true); // Can be seekable even if the stream isn't. demuxer->seekable = true; // With cache enabled, the stream can be seekable. This causes demux_lavf.c // (actually libavformat/mpegts.c) to seek sometimes when reading a packet. // It does this to seek back a bit in case the current file position points // into the middle of a packet. demuxer->stream->seekable = false; add_dvd_streams(demuxer); add_streams(demuxer); add_stream_chapters(demuxer); return 0; }