int dvb_get_tuner_type(int fe_fd, struct mp_log *log) { struct dvb_frontend_info fe_info; int res = ioctl(fe_fd, FE_GET_INFO, &fe_info); if (res < 0) { mp_err(log, "FE_GET_INFO error: %d, FD: %d\n\n", errno, fe_fd); return 0; } switch (fe_info.type) { case FE_OFDM: mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-T\n"); return TUNER_TER; case FE_QPSK: mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-S\n"); return TUNER_SAT; case FE_QAM: mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-C\n"); return TUNER_CBL; #ifdef DVB_ATSC case FE_ATSC: mp_verbose(log, "TUNER TYPE SEEMS TO BE DVB-ATSC\n"); return TUNER_ATSC; #endif default: mp_err(log, "UNKNOWN TUNER TYPE\n"); return 0; } }
struct playlist *playlist_parse_file(const char *file, struct mpv_global *global) { struct mp_log *log = mp_log_new(NULL, global->log, "!playlist_parser"); mp_verbose(log, "Parsing playlist file %s...\n", file); struct demuxer_params p = {.force_format = "playlist"}; struct demuxer *d = demux_open_url(file, &p, NULL, global); if (!d) { talloc_free(log); return NULL; } struct playlist *ret = NULL; if (d && d->playlist) { ret = talloc_zero(NULL, struct playlist); playlist_transfer_entries(ret, d->playlist); if (d->filetype && strcmp(d->filetype, "hls") == 0) { mp_warn(log, "This might be a HLS stream. For correct operation, " "pass it to the player\ndirectly. Don't use --playlist.\n"); } } free_demuxer_and_stream(d); if (ret) { mp_verbose(log, "Playlist successfully parsed\n"); } else { mp_err(log, "Error while parsing playlist\n"); } if (ret && !ret->first) mp_warn(log, "Warning: empty playlist\n"); talloc_free(log); return ret; }
void mp_ass_configure_fonts(ASS_Renderer *priv, struct osd_style_opts *opts, struct mpv_global *global, struct mp_log *log) { void *tmp = talloc_new(NULL); char *default_font = mp_find_user_config_file(tmp, global, "subfont.ttf"); char *config = mp_find_config_file(tmp, global, "fonts.conf"); if (default_font && !mp_path_exists(default_font)) default_font = NULL; mp_verbose(log, "Setting up fonts...\n"); ass_set_fonts(priv, default_font, opts->font, 1, config, 1); mp_verbose(log, "Done.\n"); talloc_free(tmp); }
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); }
void mp_set_avcodec_threads(struct mp_log *l, AVCodecContext *avctx, int threads) { if (threads == 0) { threads = av_cpu_count(); if (threads < 1) { mp_warn(l, "Could not determine thread count to use, defaulting to 1.\n"); threads = 1; } else { mp_verbose(l, "Detected %d logical cores.\n", threads); if (threads > 1) threads += 1; // extra thread for better load balancing } // Apparently some libavcodec versions have or had trouble with more // than 16 threads, and/or print a warning when using > 16. threads = MPMIN(threads, 16); } mp_verbose(l, "Requesting %d threads for decoding.\n", threads); avctx->thread_count = threads; }
static bool add_volume(struct mp_log *log, struct mp_archive *mpa, struct stream *src, const char* url) { struct mp_archive_volume *vol = talloc_zero(mpa, struct mp_archive_volume); mp_verbose(log, "Adding volume %s\n", url); vol->mpa = mpa; vol->src = src; vol->url = talloc_strdup(vol, url); return archive_read_append_callback_data(mpa->arch, vol) == ARCHIVE_OK; }
static VADisplay *create_native_va_display(struct ra *ra, struct mp_log *log) { for (int n = 0; n < MP_ARRAY_SIZE(create_native_cbs); n++) { const struct va_create_native *disp = &create_native_cbs[n]; mp_verbose(log, "Trying to open a %s VA display...\n", disp->name); VADisplay *display = disp->create(ra); if (display) return display; } return NULL; }
void mp_get_src_dst_rects(struct mp_log *log, struct mp_vo_opts *opts, int vo_caps, struct mp_image_params *video, int window_w, int window_h, double monitor_par, struct mp_rect *out_src, struct mp_rect *out_dst, struct mp_osd_res *out_osd) { int src_w = video->w; int src_h = video->h; int src_dw, src_dh; mp_image_params_get_dsize(video, &src_dw, &src_dh); if (video->rotate % 180 == 90 && (vo_caps & VO_CAP_ROTATE90)) { MPSWAP(int, src_w, src_h); MPSWAP(int, src_dw, src_dh); } window_w = MPMAX(1, window_w); window_h = MPMAX(1, window_h); struct mp_rect dst = {0, 0, window_w, window_h}; struct mp_rect src = {0, 0, src_w, src_h}; struct mp_osd_res osd = { .w = window_w, .h = window_h, .display_par = monitor_par, }; if (opts->keepaspect) { int scaled_width, scaled_height; aspect_calc_panscan(log, opts, src_w, src_h, src_dw, src_dh, window_w, window_h, monitor_par, &scaled_width, &scaled_height); src_dst_split_scaling(src_w, window_w, scaled_width, opts->unscaled, opts->zoom, opts->align_x, opts->pan_x, &src.x0, &src.x1, &dst.x0, &dst.x1, &osd.ml, &osd.mr); src_dst_split_scaling(src_h, window_h, scaled_height, opts->unscaled, opts->zoom, opts->align_y, opts->pan_y, &src.y0, &src.y1, &dst.y0, &dst.y1, &osd.mt, &osd.mb); } *out_src = src; *out_dst = dst; *out_osd = osd; int sw = src.x1 - src.x0, sh = src.y1 - src.y0; int dw = dst.x1 - dst.x0, dh = dst.y1 - dst.y0; mp_verbose(log, "Window size: %dx%d\n", window_w, window_h); mp_verbose(log, "Video source: %dx%d (%d:%d)\n", video->w, video->h, video->p_w, video->p_h); mp_verbose(log, "Video display: (%d, %d) %dx%d -> (%d, %d) %dx%d\n", src.x0, src.y0, sw, sh, dst.x0, dst.y0, dw, dh); mp_verbose(log, "Video scale: %f/%f\n", (double)dw / sw, (double)dh / sh); mp_verbose(log, "OSD borders: l=%d t=%d r=%d b=%d\n", osd.ml, osd.mt, osd.mr, osd.mb); mp_verbose(log, "Video borders: l=%d t=%d r=%d b=%d\n", dst.x0, dst.y0, window_w - dst.x1, window_h - dst.y1); }
struct playlist *playlist_parse_file(const char *file, struct mpv_global *global) { struct mp_log *log = mp_log_new(NULL, global->log, "!playlist_parser"); mp_verbose(log, "Parsing playlist file %s...\n", file); struct playlist *ret = NULL; stream_t *stream = stream_open(file, global); if(!stream) { mp_err(log, "Error while opening playlist file %s\n", file); talloc_free(log); return NULL; } struct demuxer *pl_demux = demux_open(stream, "playlist", NULL, global); if (pl_demux && pl_demux->playlist) { ret = talloc_zero(NULL, struct playlist); playlist_transfer_entries(ret, pl_demux->playlist); }
static bool parse_pid_string(struct mp_log *log, char *pid_string, dvb_channel_t *ptr) { if (pid_string[0]) { int pcnt = 0; /* These tokens also catch vdr-style PID lists. * They can contain 123=deu@3,124=eng+jap@4;125 * 3 and 4 are codes for codec type, =langLeft+langRight is allowed, * and ; may separate a dolby channel. * With the numChars-test and the full token-list, all is handled * gracefully. */ const char *tokens = "+,;"; char *pidPart; char *savePtr = NULL; pidPart = dvb_strtok_r(pid_string, tokens, &savePtr); while (pidPart != NULL) { if (ptr->pids_cnt >= DMX_FILTER_SIZE - 1) { mp_verbose(log, "Maximum number of PIDs for one channel " "reached, ignoring further ones!\n"); return pcnt > 0; } int numChars = 0; int pid = 0; pcnt += sscanf(pidPart, "%d%n", &pid, &numChars); if (numChars > 0) { ptr->pids[ptr->pids_cnt] = pid; ptr->pids_cnt++; } pidPart = dvb_strtok_r(NULL, tokens, &savePtr); } if (pcnt > 0) return true; } return false; }
static dvb_channels_list *dvb_get_channels(struct mp_log *log, char *filename, int type) { dvb_channels_list *list; FILE *f; char line[CHANNEL_LINE_LEN], *colon; if (!filename) return NULL; int fields, cnt, pcnt, k; int has8192, has0; dvb_channel_t *ptr, *tmp, chn; char tmp_lcr[256], tmp_hier[256], inv[256], bw[256], cr[256], mod[256], transm[256], gi[256], vpid_str[256], apid_str[256]; const char *cbl_conf = "%d:%255[^:]:%d:%255[^:]:%255[^:]:%255[^:]:%255[^:]\n"; const char *sat_conf = "%d:%c:%d:%d:%255[^:]:%255[^:]\n"; const char *ter_conf = "%d:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]\n"; const char *atsc_conf = "%d:%255[^:]:%255[^:]:%255[^:]\n"; mp_verbose(log, "CONFIG_READ FILE: %s, type: %d\n", filename, type); if((f=fopen(filename, "r"))==NULL) { mp_fatal(log, "CAN'T READ CONFIG FILE %s\n", filename); return NULL; } list = malloc(sizeof(dvb_channels_list)); if(list == NULL) { fclose(f); mp_verbose(log, "DVB_GET_CHANNELS: couldn't malloc enough memory\n"); return NULL; } ptr = &chn; list->NUM_CHANNELS = 0; list->channels = NULL; while(! feof(f)) { if( fgets(line, CHANNEL_LINE_LEN, f) == NULL ) continue; if((line[0] == '#') || (strlen(line) == 0)) continue; colon = strchr(line, ':'); if(colon) { k = colon - line; if(!k) continue; ptr->name = malloc(k+1); if(! ptr->name) continue; av_strlcpy(ptr->name, line, k+1); } else continue; k++; apid_str[0] = vpid_str[0] = 0; ptr->pids_cnt = 0; ptr->freq = 0; if(type == TUNER_TER) { fields = sscanf(&line[k], ter_conf, &ptr->freq, inv, bw, cr, tmp_lcr, mod, transm, gi, tmp_hier, vpid_str, apid_str); mp_verbose(log, "TER, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq); } else if(type == TUNER_CBL) { fields = sscanf(&line[k], cbl_conf, &ptr->freq, inv, &ptr->srate, cr, mod, vpid_str, apid_str); mp_verbose(log, "CBL, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, SRATE: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate); } #ifdef DVB_ATSC else if(type == TUNER_ATSC) { fields = sscanf(&line[k], atsc_conf, &ptr->freq, mod, vpid_str, apid_str); mp_verbose(log, "ATSC, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d\n", list->NUM_CHANNELS, fields, ptr->name, ptr->freq); } #endif else //SATELLITE { fields = sscanf(&line[k], sat_conf, &ptr->freq, &ptr->pol, &ptr->diseqc, &ptr->srate, vpid_str, apid_str); ptr->pol = toupper(ptr->pol); ptr->freq *= 1000UL; ptr->srate *= 1000UL; ptr->tone = -1; ptr->inv = INVERSION_AUTO; ptr->cr = FEC_AUTO; if((ptr->diseqc > 4) || (ptr->diseqc < 0)) continue; if(ptr->diseqc > 0) ptr->diseqc--; mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, SRATE: %d, POL: %c, DISEQC: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate, ptr->pol, ptr->diseqc); } if(vpid_str[0]) { pcnt = sscanf(vpid_str, "%d+%d+%d+%d+%d+%d+%d", &ptr->pids[0], &ptr->pids[1], &ptr->pids[2], &ptr->pids[3], &ptr->pids[4], &ptr->pids[5], &ptr->pids[6]); if(pcnt > 0) { ptr->pids_cnt = pcnt; fields++; } } if(apid_str[0]) { cnt = ptr->pids_cnt; pcnt = sscanf(apid_str, "%d+%d+%d+%d+%d+%d+%d+%d", &ptr->pids[cnt], &ptr->pids[cnt+1], &ptr->pids[cnt+2], &ptr->pids[cnt+3], &ptr->pids[cnt+4], &ptr->pids[cnt+5], &ptr->pids[cnt+6], &ptr->pids[cnt+7]); if(pcnt > 0) { ptr->pids_cnt += pcnt; fields++; } } if((fields < 2) || (ptr->pids_cnt <= 0) || (ptr->freq == 0) || (strlen(ptr->name) == 0)) continue; has8192 = has0 = 0; for(cnt = 0; cnt < ptr->pids_cnt; cnt++) { if(ptr->pids[cnt] == 8192) has8192 = 1; if(ptr->pids[cnt] == 0) has0 = 1; } if(has8192) { ptr->pids[0] = 8192; ptr->pids_cnt = 1; } else if(! has0) { ptr->pids[ptr->pids_cnt] = 0; //PID 0 is the PAT ptr->pids_cnt++; } mp_verbose(log, " PIDS: "); for(cnt = 0; cnt < ptr->pids_cnt; cnt++) mp_verbose(log, " %d ", ptr->pids[cnt]); mp_verbose(log, "\n"); if((type == TUNER_TER) || (type == TUNER_CBL)) { if(! strcmp(inv, "INVERSION_ON")) ptr->inv = INVERSION_ON; else if(! strcmp(inv, "INVERSION_OFF")) ptr->inv = INVERSION_OFF; else ptr->inv = INVERSION_AUTO; if(! strcmp(cr, "FEC_1_2")) ptr->cr =FEC_1_2; else if(! strcmp(cr, "FEC_2_3")) ptr->cr =FEC_2_3; else if(! strcmp(cr, "FEC_3_4")) ptr->cr =FEC_3_4; else if(! strcmp(cr, "FEC_4_5")) ptr->cr =FEC_4_5; else if(! strcmp(cr, "FEC_6_7")) ptr->cr =FEC_6_7; else if(! strcmp(cr, "FEC_8_9")) ptr->cr =FEC_8_9; else if(! strcmp(cr, "FEC_5_6")) ptr->cr =FEC_5_6; else if(! strcmp(cr, "FEC_7_8")) ptr->cr =FEC_7_8; else if(! strcmp(cr, "FEC_NONE")) ptr->cr =FEC_NONE; else ptr->cr =FEC_AUTO; } if((type == TUNER_TER) || (type == TUNER_CBL) || (type == TUNER_ATSC)) { if(! strcmp(mod, "QAM_128")) ptr->mod = QAM_128; else if(! strcmp(mod, "QAM_256")) ptr->mod = QAM_256; else if(! strcmp(mod, "QAM_64")) ptr->mod = QAM_64; else if(! strcmp(mod, "QAM_32")) ptr->mod = QAM_32; else if(! strcmp(mod, "QAM_16")) ptr->mod = QAM_16; #ifdef DVB_ATSC else if(! strcmp(mod, "VSB_8") || ! strcmp(mod, "8VSB")) ptr->mod = VSB_8; else if(! strcmp(mod, "VSB_16") || !strcmp(mod, "16VSB")) ptr->mod = VSB_16; else if(! strcmp(mod, "QAM_AUTO")) ptr->mod = QAM_AUTO; #endif } if(type == TUNER_TER) { if(! strcmp(bw, "BANDWIDTH_6_MHZ")) ptr->bw = BANDWIDTH_6_MHZ; else if(! strcmp(bw, "BANDWIDTH_7_MHZ")) ptr->bw = BANDWIDTH_7_MHZ; else if(! strcmp(bw, "BANDWIDTH_8_MHZ")) ptr->bw = BANDWIDTH_8_MHZ; if(! strcmp(transm, "TRANSMISSION_MODE_2K")) ptr->trans = TRANSMISSION_MODE_2K; else if(! strcmp(transm, "TRANSMISSION_MODE_8K")) ptr->trans = TRANSMISSION_MODE_8K; else if(! strcmp(transm, "TRANSMISSION_MODE_AUTO")) ptr->trans = TRANSMISSION_MODE_AUTO; if(! strcmp(gi, "GUARD_INTERVAL_1_32")) ptr->gi = GUARD_INTERVAL_1_32; else if(! strcmp(gi, "GUARD_INTERVAL_1_16")) ptr->gi = GUARD_INTERVAL_1_16; else if(! strcmp(gi, "GUARD_INTERVAL_1_8")) ptr->gi = GUARD_INTERVAL_1_8; else if(! strcmp(gi, "GUARD_INTERVAL_1_4")) ptr->gi = GUARD_INTERVAL_1_4; else ptr->gi = GUARD_INTERVAL_AUTO; if(! strcmp(tmp_lcr, "FEC_1_2")) ptr->cr_lp =FEC_1_2; else if(! strcmp(tmp_lcr, "FEC_2_3")) ptr->cr_lp =FEC_2_3; else if(! strcmp(tmp_lcr, "FEC_3_4")) ptr->cr_lp =FEC_3_4; else if(! strcmp(tmp_lcr, "FEC_4_5")) ptr->cr_lp =FEC_4_5; else if(! strcmp(tmp_lcr, "FEC_6_7")) ptr->cr_lp =FEC_6_7; else if(! strcmp(tmp_lcr, "FEC_8_9")) ptr->cr_lp =FEC_8_9; else if(! strcmp(tmp_lcr, "FEC_5_6")) ptr->cr_lp =FEC_5_6; else if(! strcmp(tmp_lcr, "FEC_7_8")) ptr->cr_lp =FEC_7_8; else if(! strcmp(tmp_lcr, "FEC_NONE")) ptr->cr_lp =FEC_NONE; else ptr->cr_lp =FEC_AUTO; if(! strcmp(tmp_hier, "HIERARCHY_1")) ptr->hier = HIERARCHY_1; else if(! strcmp(tmp_hier, "HIERARCHY_2")) ptr->hier = HIERARCHY_2; else if(! strcmp(tmp_hier, "HIERARCHY_4")) ptr->hier = HIERARCHY_4; else if(! strcmp(tmp_hier, "HIERARCHY_AUTO")) ptr->hier = HIERARCHY_AUTO; else ptr->hier = HIERARCHY_NONE; } tmp = realloc(list->channels, sizeof(dvb_channel_t) * (list->NUM_CHANNELS + 1)); if(tmp == NULL) break; list->channels = tmp; memcpy(&(list->channels[list->NUM_CHANNELS]), ptr, sizeof(dvb_channel_t)); list->NUM_CHANNELS++; if(sizeof(dvb_channel_t) * list->NUM_CHANNELS >= 1024*1024) { mp_verbose(log, "dvbin.c, > 1MB allocated for channels struct, dropping the rest of the file\r\n"); break; } } fclose(f); if(list->NUM_CHANNELS == 0) { free(list->channels); free(list); return NULL; } list->current = 0; return list; }
static dvb_channels_list *dvb_get_channels(struct mp_log *log, int cfg_full_transponder, char *filename, int type) { dvb_channels_list *list; FILE *f; char line[CHANNEL_LINE_LEN], *colon; if (!filename) return NULL; int fields, cnt, k; int has8192, has0; dvb_channel_t *ptr, *tmp, chn; char tmp_lcr[256], tmp_hier[256], inv[256], bw[256], cr[256], mod[256], transm[256], gi[256], vpid_str[256], apid_str[256], tpid_str[256], vdr_par_str[256], vdr_loc_str[256]; const char *cbl_conf = "%d:%255[^:]:%d:%255[^:]:%255[^:]:%255[^:]:%255[^:]\n"; const char *sat_conf = "%d:%c:%d:%d:%255[^:]:%255[^:]\n"; const char *ter_conf = "%d:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]:%255[^:]\n"; const char *atsc_conf = "%d:%255[^:]:%255[^:]:%255[^:]\n"; const char *vdr_conf = "%d:%255[^:]:%255[^:]:%d:%255[^:]:%255[^:]:%255[^:]:%*255[^:]:%d:%*d:%*d:%*d\n%n"; mp_verbose(log, "CONFIG_READ FILE: %s, type: %d\n", filename, type); if ((f = fopen(filename, "r")) == NULL) { mp_fatal(log, "CAN'T READ CONFIG FILE %s\n", filename); return NULL; } list = malloc(sizeof(dvb_channels_list)); if (list == NULL) { fclose(f); mp_verbose(log, "DVB_GET_CHANNELS: couldn't malloc enough memory\n"); return NULL; } ptr = &chn; list->NUM_CHANNELS = 0; list->channels = NULL; while (!feof(f)) { if (fgets(line, CHANNEL_LINE_LEN, f) == NULL) continue; if ((line[0] == '#') || (strlen(line) == 0)) continue; colon = strchr(line, ':'); if (colon) { k = colon - line; if (!k) continue; // In some modern VDR-style configs, channel name also has bouquet after ;. // Parse that off, we ignore it. char *bouquet_sep = strchr(line, ';'); int channel_name_length = k; if (bouquet_sep && bouquet_sep < colon) channel_name_length = bouquet_sep - line; ptr->name = malloc(channel_name_length + 1); if (!ptr->name) continue; av_strlcpy(ptr->name, line, channel_name_length + 1); } else { continue; } k++; vpid_str[0] = apid_str[0] = tpid_str[0] = 0; vdr_loc_str[0] = vdr_par_str[0] = 0; ptr->pids_cnt = 0; ptr->freq = 0; ptr->is_dvb_s2 = false; ptr->service_id = -1; ptr->stream_id = NO_STREAM_ID_FILTER; ptr->inv = INVERSION_AUTO; // Check if VDR-type channels.conf-line - then full line is consumed by the scan. int num_chars = 0; fields = sscanf(&line[k], vdr_conf, &ptr->freq, vdr_par_str, vdr_loc_str, &ptr->srate, vpid_str, apid_str, tpid_str, &ptr->service_id, &num_chars); if (num_chars == strlen(&line[k])) { // It's a VDR-style config line. parse_vdr_par_string(vdr_par_str, ptr); // We still need the special SAT-handling here. if (type != TUNER_TER && type != TUNER_CBL && type != TUNER_ATSC) { ptr->freq *= 1000UL; ptr->srate *= 1000UL; ptr->tone = -1; ptr->inv = INVERSION_AUTO; ptr->cr = FEC_AUTO; if (vdr_loc_str[0]) { // In older vdr config format, this field contained the DISEQc information. // If it is numeric, assume that's it. int diseqc_info = 0; int valid_digits = 0; if (sscanf(vdr_loc_str, "%d%n", &diseqc_info, &valid_digits) == 1) { if (valid_digits == strlen(vdr_loc_str)) { ptr->diseqc = diseqc_info; if ((ptr->diseqc > 4) || (ptr->diseqc < 0)) continue; if (ptr->diseqc > 0) ptr->diseqc--; } } } mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, " "FREQ: %d, SRATE: %d, POL: %c, DISEQC: %d, S2: %s, " "StreamID: %d, SID: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate, ptr->pol, ptr->diseqc, ptr->is_dvb_s2 ? "yes" : "no", ptr->stream_id, ptr->service_id); } else { mp_verbose(log, "VDR, NUM: %d, NUM_FIELDS: %d, NAME: %s, " "FREQ: %d, SRATE: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate); } } else if (type == TUNER_TER) { fields = sscanf(&line[k], ter_conf, &ptr->freq, inv, bw, cr, tmp_lcr, mod, transm, gi, tmp_hier, vpid_str, apid_str); mp_verbose(log, "TER, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq); } else if (type == TUNER_CBL) { fields = sscanf(&line[k], cbl_conf, &ptr->freq, inv, &ptr->srate, cr, mod, vpid_str, apid_str); mp_verbose(log, "CBL, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, " "SRATE: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate); } #ifdef DVB_ATSC else if (type == TUNER_ATSC) { fields = sscanf(&line[k], atsc_conf, &ptr->freq, mod, vpid_str, apid_str); mp_verbose(log, "ATSC, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d\n", list->NUM_CHANNELS, fields, ptr->name, ptr->freq); } #endif else { //SATELLITE fields = sscanf(&line[k], sat_conf, &ptr->freq, &ptr->pol, &ptr->diseqc, &ptr->srate, vpid_str, apid_str); ptr->pol = mp_toupper(ptr->pol); ptr->freq *= 1000UL; ptr->srate *= 1000UL; ptr->tone = -1; ptr->inv = INVERSION_AUTO; ptr->cr = FEC_AUTO; if ((ptr->diseqc > 4) || (ptr->diseqc < 0)) continue; if (ptr->diseqc > 0) ptr->diseqc--; mp_verbose(log, "SAT, NUM: %d, NUM_FIELDS: %d, NAME: %s, FREQ: %d, " "SRATE: %d, POL: %c, DISEQC: %d", list->NUM_CHANNELS, fields, ptr->name, ptr->freq, ptr->srate, ptr->pol, ptr->diseqc); } if (parse_pid_string(log, vpid_str, ptr)) fields++; if (parse_pid_string(log, apid_str, ptr)) fields++; /* If we do not know the service_id, PMT can not be extracted. Teletext decoding will fail without PMT. */ if (ptr->service_id != -1) { if (parse_pid_string(log, tpid_str, ptr)) fields++; } if ((fields < 2) || (ptr->pids_cnt <= 0) || (ptr->freq == 0) || (strlen(ptr->name) == 0)) continue; /* Add some PIDs which are mandatory in DVB, * and contain human-readable helpful data. */ /* This is the STD, the service description table. * It contains service names and such, ffmpeg decodes it. */ ptr->pids[ptr->pids_cnt] = 0x0011; ptr->pids_cnt++; /* This is the EIT, which contains EPG data. * ffmpeg can not decode it (yet), but e.g. VLC * shows what was recorded. */ ptr->pids[ptr->pids_cnt] = 0x0012; ptr->pids_cnt++; if (ptr->service_id != -1) { /* We have the PMT-PID in addition. This will be found later, when we tune to the channel. Push back here to create the additional demux. */ ptr->pids[ptr->pids_cnt] = -1; // Placeholder. ptr->pids_cnt++; } has8192 = has0 = 0; for (cnt = 0; cnt < ptr->pids_cnt; cnt++) { if (ptr->pids[cnt] == 8192) has8192 = 1; if (ptr->pids[cnt] == 0) has0 = 1; } /* 8192 is the pseudo-PID for full TP dump, enforce that if requested. */ if (!has8192 && cfg_full_transponder) has8192 = 1; if (has8192) { ptr->pids[0] = 8192; ptr->pids_cnt = 1; } else if (!has0) { ptr->pids[ptr->pids_cnt] = 0; //PID 0 is the PAT ptr->pids_cnt++; } mp_verbose(log, " PIDS: "); for (cnt = 0; cnt < ptr->pids_cnt; cnt++) mp_verbose(log, " %d ", ptr->pids[cnt]); mp_verbose(log, "\n"); if ((type == TUNER_TER) || (type == TUNER_CBL)) { if (!strcmp(inv, "INVERSION_ON")) { ptr->inv = INVERSION_ON; } else if (!strcmp(inv, "INVERSION_OFF")) { ptr->inv = INVERSION_OFF; } else { ptr->inv = INVERSION_AUTO; } if (!strcmp(cr, "FEC_1_2")) { ptr->cr = FEC_1_2; } else if (!strcmp(cr, "FEC_2_3")) { ptr->cr = FEC_2_3; } else if (!strcmp(cr, "FEC_3_4")) { ptr->cr = FEC_3_4; } else if (!strcmp(cr, "FEC_4_5")) { ptr->cr = FEC_4_5; } else if (!strcmp(cr, "FEC_6_7")) { ptr->cr = FEC_6_7; } else if (!strcmp(cr, "FEC_8_9")) { ptr->cr = FEC_8_9; } else if (!strcmp(cr, "FEC_5_6")) { ptr->cr = FEC_5_6; } else if (!strcmp(cr, "FEC_7_8")) { ptr->cr = FEC_7_8; } else if (!strcmp(cr, "FEC_NONE")) { ptr->cr = FEC_NONE; } else { ptr->cr = FEC_AUTO; } } if (type == TUNER_TER || type == TUNER_CBL || type == TUNER_ATSC) { if (!strcmp(mod, "QAM_128")) { ptr->mod = QAM_128; } else if (!strcmp(mod, "QAM_256")) { ptr->mod = QAM_256; } else if (!strcmp(mod, "QAM_64")) { ptr->mod = QAM_64; } else if (!strcmp(mod, "QAM_32")) { ptr->mod = QAM_32; } else if (!strcmp(mod, "QAM_16")) { ptr->mod = QAM_16; #ifdef DVB_ATSC } else if (!strcmp(mod, "VSB_8") || !strcmp(mod, "8VSB")) { ptr->mod = VSB_8; } else if (!strcmp(mod, "VSB_16") || !strcmp(mod, "16VSB")) { ptr->mod = VSB_16; } else if (!strcmp(mod, "QAM_AUTO")) { ptr->mod = QAM_AUTO; } #endif } if (type == TUNER_TER) { if (!strcmp(bw, "BANDWIDTH_6_MHZ")) { ptr->bw = BANDWIDTH_6_MHZ; } else if (!strcmp(bw, "BANDWIDTH_7_MHZ")) { ptr->bw = BANDWIDTH_7_MHZ; } else if (!strcmp(bw, "BANDWIDTH_8_MHZ")) { ptr->bw = BANDWIDTH_8_MHZ; } if (!strcmp(transm, "TRANSMISSION_MODE_2K")) { ptr->trans = TRANSMISSION_MODE_2K; } else if (!strcmp(transm, "TRANSMISSION_MODE_8K")) { ptr->trans = TRANSMISSION_MODE_8K; } else if (!strcmp(transm, "TRANSMISSION_MODE_AUTO")) { ptr->trans = TRANSMISSION_MODE_AUTO; } if (!strcmp(gi, "GUARD_INTERVAL_1_32")) { ptr->gi = GUARD_INTERVAL_1_32; } else if (!strcmp(gi, "GUARD_INTERVAL_1_16")) { ptr->gi = GUARD_INTERVAL_1_16; } else if (!strcmp(gi, "GUARD_INTERVAL_1_8")) { ptr->gi = GUARD_INTERVAL_1_8; } else if (!strcmp(gi, "GUARD_INTERVAL_1_4")) { ptr->gi = GUARD_INTERVAL_1_4; } else { ptr->gi = GUARD_INTERVAL_AUTO; } if (!strcmp(tmp_lcr, "FEC_1_2")) { ptr->cr_lp = FEC_1_2; } else if (!strcmp(tmp_lcr, "FEC_2_3")) { ptr->cr_lp = FEC_2_3; } else if (!strcmp(tmp_lcr, "FEC_3_4")) { ptr->cr_lp = FEC_3_4; } else if (!strcmp(tmp_lcr, "FEC_4_5")) { ptr->cr_lp = FEC_4_5; } else if (!strcmp(tmp_lcr, "FEC_6_7")) { ptr->cr_lp = FEC_6_7; } else if (!strcmp(tmp_lcr, "FEC_8_9")) { ptr->cr_lp = FEC_8_9; } else if (!strcmp(tmp_lcr, "FEC_5_6")) { ptr->cr_lp = FEC_5_6; } else if (!strcmp(tmp_lcr, "FEC_7_8")) { ptr->cr_lp = FEC_7_8; } else if (!strcmp(tmp_lcr, "FEC_NONE")) { ptr->cr_lp = FEC_NONE; } else { ptr->cr_lp = FEC_AUTO; } if (!strcmp(tmp_hier, "HIERARCHY_1")) { ptr->hier = HIERARCHY_1; } else if (!strcmp(tmp_hier, "HIERARCHY_2")) { ptr->hier = HIERARCHY_2; } else if (!strcmp(tmp_hier, "HIERARCHY_4")) { ptr->hier = HIERARCHY_4; } else if (!strcmp(tmp_hier, "HIERARCHY_AUTO")) { ptr->hier = HIERARCHY_AUTO; } else { ptr->hier = HIERARCHY_NONE; } } tmp = realloc(list->channels, sizeof(dvb_channel_t) * (list->NUM_CHANNELS + 1)); if (tmp == NULL) break; list->channels = tmp; memcpy(&(list->channels[list->NUM_CHANNELS]), ptr, sizeof(dvb_channel_t)); list->NUM_CHANNELS++; if (sizeof(dvb_channel_t) * list->NUM_CHANNELS >= 1024 * 1024) { mp_verbose(log, "dvbin.c, > 1MB allocated for channels struct, " "dropping the rest of the file\n"); break; } } fclose(f); if (list->NUM_CHANNELS == 0) { free(list->channels); free(list); return NULL; } list->current = 0; return list; }
dvb_state_t *dvb_get_state(stream_t *stream) { if (global_dvb_state != NULL) { return global_dvb_state; } struct mp_log *log = stream->log; struct mpv_global *global = stream->global; dvb_priv_t *priv = stream->priv; int type, size; char filename[30], *name; dvb_channels_list *list; dvb_card_config_t *cards = NULL, *tmp; dvb_state_t *state = NULL; state = malloc(sizeof(dvb_state_t)); if (state == NULL) return NULL; state->count = 0; state->switching_channel = false; state->stream_used = true; state->cards = NULL; state->fe_fd = state->dvr_fd = -1; for (int i = 0; i < MAX_CARDS; i++) { snprintf(filename, sizeof(filename), "/dev/dvb/adapter%d/frontend0", i); int fd = open(filename, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { mp_verbose(log, "DVB_CONFIG, can't open device %s, skipping\n", filename); continue; } mp_verbose(log, "Opened device %s, FD: %d\n", filename, fd); int* tuner_types = NULL; int num_tuner_types = dvb_get_tuner_types(fd, log, &tuner_types); close(fd); mp_verbose(log, "Frontend device %s offers %d supported delivery systems.\n", filename, num_tuner_types); for (int num_tuner_type=0; num_tuner_type<num_tuner_types; num_tuner_type++) { type = tuner_types[num_tuner_type]; if (type != TUNER_SAT && type != TUNER_TER && type != TUNER_CBL && type != TUNER_ATSC) { mp_verbose(log, "DVB_CONFIG, can't detect tuner type of " "card %d, skipping\n", i); continue; } void *talloc_ctx = talloc_new(NULL); char *conf_file = NULL; if (priv->cfg_file && priv->cfg_file[0]) conf_file = priv->cfg_file; else { switch (type) { case TUNER_TER: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.ter"); break; case TUNER_CBL: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.cbl"); break; case TUNER_SAT: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.sat"); break; case TUNER_ATSC: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.atsc"); break; } if (conf_file) { mp_verbose(log, "Ignoring other channels.conf files.\n"); } else { conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf"); } } list = dvb_get_channels(log, priv->cfg_full_transponder, conf_file, type); talloc_free(talloc_ctx); if (list == NULL) continue; size = sizeof(dvb_card_config_t) * (state->count + 1); tmp = realloc(state->cards, size); if (tmp == NULL) { mp_err(log, "DVB_CONFIG, can't realloc %d bytes, skipping\n", size); free(list); continue; } cards = tmp; name = malloc(20); if (name == NULL) { mp_err(log, "DVB_CONFIG, can't realloc 20 bytes, skipping\n"); free(list); free(tmp); continue; } state->cards = cards; state->cards[state->count].devno = i; state->cards[state->count].list = list; state->cards[state->count].type = type; snprintf(name, 20, "DVB-%c card n. %d", type == TUNER_TER ? 'T' : (type == TUNER_CBL ? 'C' : 'S'), state->count + 1); state->cards[state->count].name = name; state->count++; } talloc_free(tuner_types); } if (state->count == 0) { free(state); state = NULL; } global_dvb_state = state; return state; }
// Runs charset auto-detection on the input buffer, and returns the result. // If auto-detection fails, NULL is returned. // If user_cp doesn't refer to any known auto-detection (for example because // it's a real iconv codepage), user_cp is returned without even looking at // the buf data. // The return value may (but doesn't have to) be allocated under talloc_ctx. const char *mp_charset_guess(void *talloc_ctx, struct mp_log *log, bstr buf, const char *user_cp, int flags) { if (!mp_charset_requires_guess(user_cp)) return user_cp; bool use_auto = strcasecmp(user_cp, "auto") == 0; if (use_auto) { #if HAVE_UCHARDET user_cp = "uchardet"; #elif HAVE_ENCA user_cp = "enca"; #else user_cp = "UTF-8:UTF-8-BROKEN"; #endif } bstr params[3] = {{0}}; split_colon(user_cp, 3, params); bstr type = params[0]; char lang[100]; snprintf(lang, sizeof(lang), "%.*s", BSTR_P(params[1])); const char *fallback = params[2].start; // last item, already 0-terminated const char *res = NULL; if (use_auto) { res = ms_bom_guess(buf); if (res) type = bstr0("auto"); } #if HAVE_ENCA if (bstrcasecmp0(type, "enca") == 0) res = enca_guess(log, buf, lang); #endif #if HAVE_LIBGUESS if (bstrcasecmp0(type, "guess") == 0) res = libguess_guess(log, buf, lang); #endif #if HAVE_UCHARDET if (bstrcasecmp0(type, "uchardet") == 0) res = mp_uchardet(talloc_ctx, log, buf); #endif if (bstrcasecmp0(type, "utf8") == 0 || bstrcasecmp0(type, "utf-8") == 0) { if (!fallback) fallback = params[1].start; // must be already 0-terminated int r = bstr_validate_utf8(buf); if (r >= 0 || (r > -8 && (flags & MP_ICONV_ALLOW_CUTOFF))) res = "utf-8"; } if (res) { mp_dbg(log, "%.*s detected charset: '%s'\n", BSTR_P(type), res); } else { res = fallback; mp_dbg(log, "Detection with %.*s failed: fallback to %s\n", BSTR_P(type), res && res[0] ? res : "broken UTF-8/Latin1"); } if (!res && !(flags & MP_STRICT_UTF8)) res = "UTF-8-BROKEN"; mp_verbose(log, "Using charset '%s'.\n", res); return res; }
struct drm_atomic_context *drm_atomic_create_context(struct mp_log *log, int fd, int crtc_id, int connector_id, int osd_plane_id, int video_plane_id) { drmModePlaneRes *plane_res = NULL; drmModeRes *res = NULL; struct drm_object *plane = NULL; struct drm_atomic_context *ctx; int crtc_index = -1; int layercount = -1; int primary_id = 0; int overlay_id = 0; uint64_t value; res = drmModeGetResources(fd); if (!res) { mp_err(log, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno)); goto fail; } plane_res = drmModeGetPlaneResources(fd); if (!plane_res) { mp_err(log, "Cannot retrieve plane ressources: %s\n", mp_strerror(errno)); goto fail; } ctx = talloc_zero(NULL, struct drm_atomic_context); if (!ctx) { mp_err(log, "Out of memory\n"); goto fail; } ctx->fd = fd; ctx->crtc = drm_object_create(log, ctx->fd, crtc_id, DRM_MODE_OBJECT_CRTC); if (!ctx->crtc) { mp_err(log, "Failed to create CRTC object\n"); goto fail; } for (int i = 0; i < res->count_crtcs; i++) { if (res->crtcs[i] == crtc_id) { crtc_index = i; break; } } for (int i = 0; i < res->count_connectors; i++) { drmModeConnector *connector = drmModeGetConnector(fd, res->connectors[i]); if (connector) { if (connector->connector_id == connector_id) ctx->connector = drm_object_create(log, ctx->fd, connector->connector_id, DRM_MODE_OBJECT_CONNECTOR); drmModeFreeConnector(connector); if (ctx->connector) break; } } for (unsigned int j = 0; j < plane_res->count_planes; j++) { drmModePlane *drmplane = drmModeGetPlane(ctx->fd, plane_res->planes[j]); const uint32_t possible_crtcs = drmplane->possible_crtcs; const uint32_t plane_id = drmplane->plane_id; drmModeFreePlane(drmplane); drmplane = NULL; if (possible_crtcs & (1 << crtc_index)) { plane = drm_object_create(log, ctx->fd, plane_id, DRM_MODE_OBJECT_PLANE); if (!plane) { mp_err(log, "Failed to create Plane object from plane ID %d\n", plane_id); goto fail; } if (drm_object_get_property(plane, "TYPE", &value) == -EINVAL) { mp_err(log, "Unable to retrieve type property from plane %d\n", j); goto fail; } if (value != DRM_PLANE_TYPE_CURSOR) { // Skip cursor planes layercount++; if ((!primary_id) && (value == DRM_PLANE_TYPE_PRIMARY)) primary_id = plane_id; if ((!overlay_id) && (value == DRM_PLANE_TYPE_OVERLAY)) overlay_id = plane_id; if (layercount == osd_plane_id) { ctx->osd_plane = plane; continue; } if (layercount == video_plane_id) { ctx->video_plane = plane; continue; } } drm_object_free(plane); plane = NULL; } } // default OSD plane to primary if unspecified if (!ctx->osd_plane) { if (primary_id) { mp_verbose(log, "Using default plane %d for OSD\n", primary_id); ctx->osd_plane = drm_object_create(log, ctx->fd, primary_id, DRM_MODE_OBJECT_PLANE); } else { mp_err(log, "Failed to find OSD plane with id=%d\n", osd_plane_id); goto fail; } } else { mp_verbose(log, "Found OSD plane with ID %d\n", ctx->osd_plane->id); } // default video plane to overlay if unspecified if (!ctx->video_plane) { if (overlay_id) { mp_verbose(log, "Using default plane %d for video\n", overlay_id); ctx->video_plane = drm_object_create(log, ctx->fd, overlay_id, DRM_MODE_OBJECT_PLANE); } else { mp_verbose(log, "Failed to find video plane with id=%d. drmprime-drm hwdec interop will not work\n", video_plane_id); } } else { mp_verbose(log, "Found video plane with ID %d\n", ctx->video_plane->id); } drmModeFreePlaneResources(plane_res); drmModeFreeResources(res); return ctx; fail: if (res) drmModeFreeResources(res); if (plane_res) drmModeFreePlaneResources(plane_res); if (plane) drm_object_free(plane); return NULL; }
dvb_config_t *dvb_get_config(stream_t *stream) { struct mp_log *log = stream->log; struct mpv_global *global = stream->global; int i, fd, type, size; char filename[30], *conf_file, *name; dvb_channels_list *list; dvb_card_config_t *cards = NULL, *tmp; dvb_config_t *conf = NULL; conf = malloc(sizeof(dvb_config_t)); if(conf == NULL) return NULL; conf->priv = NULL; conf->count = 0; conf->cards = NULL; for(i=0; i<MAX_CARDS; i++) { snprintf(filename, sizeof(filename), "/dev/dvb/adapter%d/frontend0", i); fd = open(filename, O_RDONLY|O_NONBLOCK|O_CLOEXEC); if(fd < 0) { mp_verbose(log, "DVB_CONFIG, can't open device %s, skipping\n", filename); continue; } type = dvb_get_tuner_type(fd, log); close(fd); if(type != TUNER_SAT && type != TUNER_TER && type != TUNER_CBL && type != TUNER_ATSC) { mp_verbose(log, "DVB_CONFIG, can't detect tuner type of card %d, skipping\n", i); continue; } void *talloc_ctx = talloc_new(NULL); conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf"); switch(type) { case TUNER_TER: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.ter"); break; case TUNER_CBL: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.cbl"); break; case TUNER_SAT: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.sat"); break; case TUNER_ATSC: conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf.atsc"); break; } if(conf_file && (access(conf_file, F_OK | R_OK) != 0)) conf_file = mp_find_config_file(talloc_ctx, global, "channels.conf"); list = dvb_get_channels(log, conf_file, type); talloc_free(talloc_ctx); if(list == NULL) continue; size = sizeof(dvb_card_config_t) * (conf->count + 1); tmp = realloc(conf->cards, size); if(tmp == NULL) { fprintf(stderr, "DVB_CONFIG, can't realloc %d bytes, skipping\n", size); continue; } cards = tmp; name = malloc(20); if(name==NULL) { fprintf(stderr, "DVB_CONFIG, can't realloc 20 bytes, skipping\n"); continue; } conf->cards = cards; conf->cards[conf->count].devno = i; conf->cards[conf->count].list = list; conf->cards[conf->count].type = type; snprintf(name, 20, "DVB-%c card n. %d", type==TUNER_TER ? 'T' : (type==TUNER_CBL ? 'C' : 'S'), conf->count+1); conf->cards[conf->count].name = name; conf->count++; } if(conf->count == 0) { free(conf); conf = NULL; } return conf; }
static bool subreader_autodetect(stream_t *fd, struct MPOpts *opts, struct mp_log *log, struct subreader *out) { static const struct subreader sr[]= { { sub_read_line_microdvd, NULL, "microdvd", "microdvd" }, { sub_read_line_subrip, NULL, "subviewer" }, { sub_read_line_subviewer, NULL, "subrip", "subrip" }, { sub_read_line_sami, NULL, "sami" }, { sub_read_line_vplayer, NULL, "vplayer" }, { sub_read_line_rt, NULL, "rt" }, { sub_read_line_ssa, NULL, "ssa", "ass-text" }, { sub_read_line_pjs, NULL, "pjs" }, { sub_read_line_mpsub, NULL, "mpsub" }, { sub_read_line_aqt, NULL, "aqt" }, { sub_read_line_subviewer2, NULL, "subviewer 2.0" }, { sub_read_line_subrip09, NULL, "subrip 0.9" }, { sub_read_line_jacosub, NULL, "jacosub" }, { sub_read_line_mpl2, NULL, "mpl2" } }; const struct subreader *srp; int sub_format = SUB_INVALID; int utf16; int uses_time = 0; for (utf16 = 0; sub_format == SUB_INVALID && utf16 < 3; utf16++) { sub_format=sub_autodetect (fd, &uses_time, utf16); stream_seek(fd,0); } utf16--; if (sub_format==SUB_INVALID) { mp_verbose(log, "Could not determine file format\n"); return false; } srp=sr+sub_format; mp_verbose(log, "Detected subtitle file format: %s\n", srp->name); *out = *srp; out->args = (struct readline_args) { .log = log, .utf16 = utf16, .opts = opts, .sub_slacktime = 20000, //20 sec .mpsub_multiplier = (uses_time ? 100.0 : 1.0), .uses_time = uses_time, }; return true; } static sub_data* sub_read_file(stream_t *fd, struct subreader *srp) { struct MPOpts *opts = fd->opts; float fps = 23.976; int n_max, i, j; subtitle *first, *sub, *return_sub, *alloced_sub = NULL; sub_data *subt_data; int sub_num = 0, sub_errs = 0; struct readline_args args = srp->args; sub_num=0;n_max=32; first=malloc(n_max*sizeof(subtitle)); if (!first) abort(); alloced_sub = sub = malloc(sizeof(subtitle)); //This is to deal with those formats (AQT & Subrip) which define the end of a subtitle //as the beginning of the following args.previous_sub_end = 0; while(1){ if(sub_num>=n_max){ n_max+=16; first=realloc(first,n_max*sizeof(subtitle)); } memset(sub, '\0', sizeof(subtitle)); sub=srp->read(fd, sub, &args); if(!sub) break; // EOF if ( sub == ERR ) { free(first); free(alloced_sub); return NULL; } // Apply any post processing that needs recoding first if ((sub!=ERR) && srp->post) srp->post(sub); if(!sub_num || (first[sub_num - 1].start <= sub->start)){ first[sub_num].start = sub->start; first[sub_num].end = sub->end; first[sub_num].lines = sub->lines; first[sub_num].alignment = sub->alignment; for(i = 0; i < sub->lines; ++i){ first[sub_num].text[i] = sub->text[i]; } if (args.previous_sub_end){ first[sub_num - 1].end = args.previous_sub_end; args.previous_sub_end = 0; } } else { for(j = sub_num - 1; j >= 0; --j){ first[j + 1].start = first[j].start; first[j + 1].end = first[j].end; first[j + 1].lines = first[j].lines; first[j + 1].alignment = first[j].alignment; for(i = 0; i < first[j].lines; ++i){ first[j + 1].text[i] = first[j].text[i]; } if(!j || (first[j - 1].start <= sub->start)){ first[j].start = sub->start; first[j].end = sub->end; first[j].lines = sub->lines; first[j].alignment = sub->alignment; for(i = 0; i < SUB_MAX_TEXT; ++i){ first[j].text[i] = sub->text[i]; } if (args.previous_sub_end){ first[j].end = first[j - 1].end; first[j - 1].end = args.previous_sub_end; args.previous_sub_end = 0; } break; } } } if(sub==ERR) ++sub_errs; else ++sub_num; // Error vs. Valid } free(alloced_sub); // printf ("Subtitle format %s time.\n", uses_time?"uses":"doesn't use"); MP_VERBOSE(&srp->args, "Read %i subtitles, %i bad line(s).\n", sub_num, sub_errs); if(sub_num<=0){ free(first); return NULL; } adjust_subs_time(srp, first, 6.0, fps, opts->sub_fps, 1, sub_num, args.uses_time);/*~6 secs AST*/ return_sub = first; if (return_sub == NULL) return NULL; subt_data = talloc_zero(NULL, sub_data); subt_data->codec = srp->codec_name ? srp->codec_name : "text"; subt_data->sub_uses_time = args.uses_time; subt_data->sub_num = sub_num; subt_data->sub_errs = sub_errs; subt_data->subtitles = return_sub; subt_data->fallback_fps = fps; return subt_data; } static void subdata_free(sub_data *subd) { int i, j; for (i = 0; i < subd->sub_num; i++) for (j = 0; j < subd->subtitles[i].lines; j++) free( subd->subtitles[i].text[j] ); free( subd->subtitles ); talloc_free(subd); }
static struct bstr strip_ext(struct bstr str) { int dotpos = bstrrchr(str, '.'); if (dotpos < 0) return str; return (struct bstr){str.start, dotpos}; } static struct bstr get_ext(struct bstr s) { int dotpos = bstrrchr(s, '.'); if (dotpos < 0) return (struct bstr){NULL, 0}; return bstr_splice(s, dotpos + 1, s.len); } bool mp_might_be_subtitle_file(const char *filename) { return test_ext(get_ext(bstr0(filename))) == STREAM_SUB; } static int compare_sub_filename(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; return strcoll(s1->fname, s2->fname); } static int compare_sub_priority(const void *a, const void *b) { const struct subfn *s1 = a; const struct subfn *s2 = b; if (s1->priority > s2->priority) return -1; if (s1->priority < s2->priority) return 1; return strcoll(s1->fname, s2->fname); } static struct bstr guess_lang_from_filename(struct bstr name) { if (name.len < 2) return (struct bstr){NULL, 0}; int n = 0; int i = name.len - 1; if (name.start[i] == ')' || name.start[i] == ']') i--; while (i >= 0 && mp_isalpha(name.start[i])) { n++; if (n > 3) return (struct bstr){NULL, 0}; i--; } if (n < 2) return (struct bstr){NULL, 0}; return (struct bstr){name.start + i + 1, n}; } static void append_dir_subtitles(struct mpv_global *global, struct subfn **slist, int *nsub, struct bstr path, const char *fname, int limit_fuzziness) { void *tmpmem = talloc_new(NULL); struct MPOpts *opts = global->opts; struct mp_log *log = mp_log_new(tmpmem, global->log, "find_files"); if (mp_is_url(bstr0(fname))) goto out; struct bstr f_fname = bstr0(mp_basename(fname)); struct bstr f_fname_noext = bstrdup(tmpmem, strip_ext(f_fname)); bstr_lower(f_fname_noext); struct bstr f_fname_trim = bstr_strip(f_fname_noext); // 0 = nothing // 1 = any subtitle file // 2 = any sub file containing movie name // 3 = sub file containing movie name and the lang extension char *path0 = bstrdup0(tmpmem, path); DIR *d = opendir(path0); if (!d) goto out; mp_verbose(log, "Loading external files in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { struct bstr dename = bstr0(de->d_name); void *tmpmem2 = talloc_new(tmpmem); // retrieve various parts of the filename struct bstr tmp_fname_noext = bstrdup(tmpmem2, strip_ext(dename)); bstr_lower(tmp_fname_noext); struct bstr tmp_fname_ext = get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); // check what it is (most likely) int type = test_ext(tmp_fname_ext); char **langs = NULL; int fuzz = -1; switch (type) { case STREAM_SUB: langs = opts->sub_lang; fuzz = opts->sub_auto; break; case STREAM_AUDIO: langs = opts->audio_lang; fuzz = opts->audiofile_auto; break; } if (fuzz < 0) goto next_sub; // we have a (likely) subtitle file int prio = 0; char *found_lang = NULL; if (langs) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { struct bstr lang = guess_lang_from_filename(tmp_fname_trim); if (lang.len) { for (int n = 0; langs[n]; n++) { if (bstr_startswith0(lang, langs[n])) { prio = 4; // matches the movie name + lang extension found_lang = langs[n]; break; } } } } } if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && fuzz >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory if (!limit_fuzziness && fuzz >= 2) { prio = 1; } } mp_dbg(log, "Potential external file: \"%s\" Priority: %d\n", de->d_name, prio); if (prio) { prio += prio; char *subpath = mp_path_join(*slist, path, dename); if (mp_path_exists(subpath)) { MP_GROW_ARRAY(*slist, *nsub); struct subfn *sub = *slist + (*nsub)++; // annoying and redundant if (strncmp(subpath, "./", 2) == 0) subpath += 2; sub->type = type; sub->priority = prio; sub->fname = subpath; sub->lang = found_lang; } else talloc_free(subpath); } next_sub: talloc_free(tmpmem2); } closedir(d); out: talloc_free(tmpmem); } static bool case_endswith(const char *s, const char *end) { size_t len = strlen(s); size_t elen = strlen(end); return len >= elen && strcasecmp(s + len - elen, end) == 0; } // Drop .sub file if .idx file exists. // Assumes slist is sorted by compare_sub_filename. static void filter_subidx(struct subfn **slist, int *nsub) { const char *prev = NULL; for (int n = 0; n < *nsub; n++) { const char *fname = (*slist)[n].fname; if (case_endswith(fname, ".idx")) { prev = fname; } else if (case_endswith(fname, ".sub")) { if (prev && strncmp(prev, fname, strlen(fname) - 4) == 0) (*slist)[n].priority = -1; } } for (int n = *nsub - 1; n >= 0; n--) { if ((*slist)[n].priority < 0) MP_TARRAY_REMOVE_AT(*slist, *nsub, n); } } // Return a list of subtitles and audio files found, sorted by priority. // Last element is terminated with a fname==NULL entry. struct subfn *find_external_files(struct mpv_global *global, const char *fname) { struct MPOpts *opts = global->opts; struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); int n = 0; // Load subtitles from current media directory append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0); if (opts->sub_auto >= 0) { // Load subtitles in dirs specified by sub-paths option if (opts->sub_paths) { for (int i = 0; opts->sub_paths[i]; i++) { char *path = mp_path_join(slist, mp_dirname(fname), bstr0(opts->sub_paths[i])); append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0); } } // Load subtitles in ~/.mpv/sub limiting sub fuzziness char *mp_subdir = mp_find_config_file(NULL, global, "sub/"); if (mp_subdir) append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); talloc_free(mp_subdir); } // Sort by name for filter_subidx() qsort(slist, n, sizeof(*slist), compare_sub_filename); filter_subidx(&slist, &n); // Sort subs by priority and append them qsort(slist, n, sizeof(*slist), compare_sub_priority); struct subfn z = {0}; MP_TARRAY_APPEND(NULL, slist, n, z); return slist; }
static mf_t *open_mf_pattern(void *talloc_ctx, struct mp_log *log, char *filename) { int error_count = 0; int count = 0; mf_t *mf = talloc_zero(talloc_ctx, mf_t); mf->log = log; if (filename[0] == '@') { FILE *lst_f = fopen(filename + 1, "r"); if (lst_f) { char *fname = talloc_size(mf, 512); while (fgets(fname, 512, lst_f)) { /* remove spaces from end of fname */ char *t = fname + strlen(fname) - 1; while (t > fname && mp_isspace(*t)) *(t--) = 0; if (!mp_path_exists(fname)) { mp_verbose(log, "file not found: '%s'\n", fname); } else { mf_add(mf, fname); } } fclose(lst_f); mp_info(log, "number of files: %d\n", mf->nr_of_files); goto exit_mf; } mp_info(log, "%s is not indirect filelist\n", filename + 1); } if (strchr(filename, ',')) { mp_info(log, "filelist: %s\n", filename); bstr bfilename = bstr0(filename); while (bfilename.len) { bstr bfname; bstr_split_tok(bfilename, ",", &bfname, &bfilename); char *fname2 = bstrdup0(mf, bfname); if (!mp_path_exists(fname2)) mp_verbose(log, "file not found: '%s'\n", fname2); else { mf_add(mf, fname2); } talloc_free(fname2); } mp_info(log, "number of files: %d\n", mf->nr_of_files); goto exit_mf; } char *fname = talloc_size(mf, strlen(filename) + 32); if (!strchr(filename, '%')) { strcpy(fname, filename); if (!strchr(filename, '*')) strcat(fname, "*"); mp_info(log, "search expr: %s\n", fname); glob_t gg; if (glob(fname, 0, NULL, &gg)) { talloc_free(mf); return NULL; } for (int i = 0; i < gg.gl_pathc; i++) { if (mp_path_isdir(gg.gl_pathv[i])) continue; mf_add(mf, gg.gl_pathv[i]); } mp_info(log, "number of files: %d\n", mf->nr_of_files); globfree(&gg); goto exit_mf; } mp_info(log, "search expr: %s\n", filename); while (error_count < 5) { sprintf(fname, filename, count++); if (!mp_path_exists(fname)) { error_count++; mp_verbose(log, "file not found: '%s'\n", fname); } else { mf_add(mf, fname); } } mp_info(log, "number of files: %d\n", mf->nr_of_files); exit_mf: return mf; }