📄 player.c
字号:
struct player *player = data; /* output ancillary data */ if (player->ancillary.file && stream->anc_bitlen && write_ancillary(&player->ancillary, stream->anc_ptr, stream->anc_bitlen) == -1) return MAD_FLOW_BREAK; /* first frame accounting */ if (player->stats.absolute_framecount == 0) { ++player->stats.absolute_framecount; mad_timer_add(&player->stats.absolute_timer, frame->header.duration); ++player->stats.global_framecount; mad_timer_add(&player->stats.global_timer, frame->header.duration); if ((player->options & PLAYER_OPTION_SKIP) && mad_timer_compare(player->stats.global_timer, player->global_start) < 0) return MAD_FLOW_IGNORE; } /* run the filter chain */ return filter_run(player->output.filters, frame);}/* * NAME: show_status() * DESCRIPTION: generate and output stream statistics */staticvoid show_status(struct stats *stats,struct mad_header const *header, char const *label, int now){ signed long seconds; static char const *const layer_str[3] = { N_("I"), N_("II"), N_("III") }; static char const *const mode_str[4] = { N_("single channel"), N_("dual channel"), N_("joint stereo"), N_("stereo") }; if (header) { unsigned int bitrate; bitrate = header->bitrate / 1000; stats->vbr_rate += bitrate; stats->vbr_frames++; stats->vbr += (stats->bitrate && stats->bitrate != bitrate) ? 128 : -1; if (stats->vbr < 0) stats->vbr = 0; else if (stats->vbr > 512) stats->vbr = 512; stats->bitrate = bitrate; } seconds = mad_timer_count(stats->global_timer, MAD_UNITS_SECONDS); if (seconds != stats->nsecs || !on_same_line || now) { mad_timer_t timer; char time_str[18]; char const *joint_str = ""; stats->nsecs = seconds; switch (stats->show) { case STATS_SHOW_OVERALL: timer = stats->global_timer; break; case STATS_SHOW_CURRENT: timer = stats->absolute_timer; break; case STATS_SHOW_REMAINING: timer = stats->total_time; if (mad_timer_sign(timer) == 0 && stats->total_bytes) { unsigned long rate; /* estimate based on size and bitrate */ rate = stats->vbr ? stats->vbr_rate * 125 / stats->vbr_frames : stats->bitrate * 125UL; mad_timer_set(&timer, 0, stats->total_bytes, rate); } mad_timer_negate(&timer); mad_timer_add(&timer, stats->absolute_timer); break; } mad_timer_string(timer, time_str, " %02lu:%02u:%02u", MAD_UNITS_HOURS, 0, 0); if (mad_timer_sign(timer) < 0) time_str[0] = '-'; if (label || stats->label) { message("%s %s", time_str, label ? label : stats->label); stats->label = now ? label : 0; } else if (header) { if (header->mode == MAD_MODE_JOINT_STEREO) { switch (header->flags & (MAD_FLAG_MS_STEREO | MAD_FLAG_I_STEREO)) { case 0: joint_str = _(" (LR)"); break; case MAD_FLAG_MS_STEREO: joint_str = _(" (MS)"); break; case MAD_FLAG_I_STEREO: joint_str = _(" (I)"); break; case (MAD_FLAG_MS_STEREO | MAD_FLAG_I_STEREO): joint_str = _(" (MS+I)"); break; } } message(_("%s Layer %s, %s%u kbps%s, %u Hz, %s%s, %s"), time_str, gettext(layer_str[header->layer - 1]), stats->vbr ? _("VBR (avg ") : "", stats->vbr ? ((stats->vbr_rate * 2) / stats->vbr_frames + 1) / 2 : stats->bitrate, stats->vbr ? _(")") : "", header->samplerate, gettext(mode_str[header->mode]), joint_str, (header->flags & MAD_FLAG_PROTECTION) ? _("CRC") : _("no CRC")); } else message("%s", time_str); }}/* * NAME: decode->output() * DESCRIPTION: configure audio module and output decoded samples */staticenum mad_flow decode_output(void *data, struct mad_header const *header, struct mad_pcm *pcm){ struct player *player = data; struct output *output = &player->output; mad_fixed_t const *ch1, *ch2; unsigned int nchannels; union audio_control control; ch1 = pcm->samples[0]; ch2 = pcm->samples[1]; switch (nchannels = pcm->channels) { case 1: ch2 = 0; if (output->select == PLAYER_CHANNEL_STEREO) { ch2 = ch1; nchannels = 2; } break; case 2: switch (output->select) { case PLAYER_CHANNEL_RIGHT: ch1 = ch2; /* fall through */ case PLAYER_CHANNEL_LEFT: ch2 = 0; nchannels = 1; /* fall through */ case PLAYER_CHANNEL_STEREO: break; default: if (header->mode == MAD_MODE_DUAL_CHANNEL) { if (output->select == PLAYER_CHANNEL_DEFAULT) { if (player->verbosity >= -1) { error("output", _("no channel selected for dual channel; using first")); /////////////////////// } output->select = -PLAYER_CHANNEL_LEFT; } ch2 = 0; nchannels = 1; } } } if (output->channels_in != nchannels || output->speed_in != pcm->samplerate) { unsigned int speed_request; if (player->verbosity >= 1 && pcm->samplerate != header->samplerate) { error("output", _("decoded sample frequency %u Hz"), //////////////////// pcm->samplerate); } speed_request = output->speed_request ? output->speed_request : pcm->samplerate; audio_control_init(&control, AUDIO_COMMAND_CONFIG); control.config.channels = nchannels; control.config.speed = speed_request; control.config.precision = output->precision_in; if (output->command(&control) == -1) { error("output", audio_error); return MAD_FLOW_BREAK; } output->channels_in = nchannels; output->speed_in = pcm->samplerate; output->channels_out = control.config.channels; output->speed_out = control.config.speed; output->precision_out = control.config.precision; if (player->verbosity >= -1 && output->channels_in != output->channels_out) { if (output->channels_in == 1) error("output", _("mono output not available; forcing stereo")); ///////////////////// else { error("output", _("stereo output not available; using first channel " "(use -m to mix)")); } } if (player->verbosity >= -1 && output->precision_in && output->precision_in != output->precision_out) { error("output", _("bit depth %u not available; using %u"), output->precision_in, output->precision_out); } if (player->verbosity >= -1 && speed_request != output->speed_out) { error("output", _("sample frequency %u Hz not available; using %u Hz"), ////////////// speed_request, output->speed_out); } /* check whether resampling is necessary */ if (abs(output->speed_out - output->speed_in) < (long) FREQ_TOLERANCE * output->speed_in / 100) { if (output->resampled) { resample_finish(&output->resample[0]); resample_finish(&output->resample[1]); free(output->resampled); output->resampled = 0; } } else { if (output->resampled) { resample_finish(&output->resample[0]); resample_finish(&output->resample[1]); } else { output->resampled = malloc(sizeof(*output->resampled)); if (output->resampled == 0) { error("output", _("not enough memory to allocate resampling buffer")); output->speed_in = 0; return MAD_FLOW_BREAK; } } if (resample_init(&output->resample[0], output->speed_in, output->speed_out) == -1 || resample_init(&output->resample[1], output->speed_in, output->speed_out) == -1) { error("output", _("cannot resample %u Hz to %u Hz"), output->speed_in, output->speed_out); free(output->resampled); output->resampled = 0; output->speed_in = 0; return MAD_FLOW_BREAK; } else if (player->verbosity >= -1) { error("output", _("resampling %u Hz to %u Hz"), ///////////////////// output->speed_in, output->speed_out); } } } audio_control_init(&control, AUDIO_COMMAND_PLAY); if (output->channels_in != output->channels_out) ch2 = (output->channels_out == 2) ? ch1 : 0; if (output->resampled) { control.play.nsamples = resample_block(&output->resample[0], pcm->length, ch1, (*output->resampled)[0]); control.play.samples[0] = (*output->resampled)[0]; if (ch2 == ch1) control.play.samples[1] = control.play.samples[0]; else if (ch2) { resample_block(&output->resample[1], pcm->length, ch2, (*output->resampled)[1]); control.play.samples[1] = (*output->resampled)[1]; } else control.play.samples[1] = 0; } else { control.play.nsamples = pcm->length; control.play.samples[0] = ch1; control.play.samples[1] = ch2; } control.play.mode = output->mode; control.play.stats = &player->stats.audio; if (output->command(&control) == -1) { error("output", audio_error); return MAD_FLOW_BREAK; } ++player->stats.play_framecount; mad_timer_add(&player->stats.play_timer, header->duration); if (player->verbosity > 0) show_status(&player->stats, header, 0, 0); return MAD_FLOW_CONTINUE;}/* * NAME: decode->error() * DESCRIPTION: handle a decoding error */staticenum mad_flow decode_error(void *data, struct mad_stream *stream, struct mad_frame *frame){ return MAD_FLOW_CONTINUE;}/** NAME: decode() DESCRIPTION: decode and output audio for an open file *///被play_one()调用static int decode(struct player *player){ struct stat stat; struct mad_decoder decoder; int options, result; if (fstat(player->input.fd, &stat) == -1) { error("decode", ":fstat"); return -1; } if (S_ISREG(stat.st_mode)) player->stats.total_bytes = stat.st_size; /* prepare input buffers */ if (player->input.data == 0) { player->input.data = malloc(MPEG_BUFSZ); if (player->input.data == 0) { error("decode", _("not enough memory to allocate input buffer")); return -1; } player->input.length = 0; } player->input.eof = 0; /* reset statistics */ player->stats.absolute_timer = mad_timer_zero; player->stats.play_timer = mad_timer_zero; player->stats.absolute_framecount = 0; player->stats.play_framecount = 0; player->stats.error_frame = -1; player->stats.vbr = 0; player->stats.bitrate = 0; player->stats.vbr_frames = 0; player->stats.vbr_rate = 0; player->stats.audio.clipped_samples = 0; player->stats.audio.peak_clipping = 0; player->stats.audio.peak_sample = 0; mad_decoder_init(&decoder, player,decode_input_read,decode_header, decode_filter, player->output.command ? decode_output : 0, decode_error, 0);//decoder.c options = 0; if (player->options & PLAYER_OPTION_DOWNSAMPLE) options |= MAD_OPTION_HALFSAMPLERATE; if (player->options & PLAYER_OPTION_IGNORECRC) options |= MAD_OPTION_IGNORECRC; mad_decoder_options(&decoder, options); result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); mad_decoder_finish(&decoder); if (player->input.data) { free(player->input.data); player->input.data = 0; } return result;}/*NAME: play_one(), DESCRIPTION: open and play a single file *///被play_all()调用static int play_one(struct player *player){ char const *file = player->playlist.entries[player->playlist.current]; int result; if (strcmp(file, "-") == 0) { if (isatty(STDIN_FILENO)) { error(0, "%s: %s", _("stdin"), _("is a tty")); return -1; } player->input.path = _("stdin"); player->input.fd = STDIN_FILENO; } else { player->input.path = file; //struct:player.h player->input.fd = open(file, O_RDONLY | O_BINARY); //int fd if (player->input.fd == -1) { error(0, ":", file); return -1; } } if (player->verbosity >= 0 && player->playlist.length > 1) message(">> %s\n", player->input.path); /* reset file information */ player->stats.total_bytes = 0; player->stats.total_time = mad_timer_zero; if (!(player->options & PLAYER_OPTION_IGNOREVOLADJ)) set_gain(player, GAIN_VOLADJ, 0); player->output.replay_gain &= ~RGAIN_SET; result = decode(player); if (result == 0 && player->verbosity >= 0 && !(player->options & PLAYER_OPTION_SHOWTAGSONLY)) { char time_str[19], db_str[7]; char const *peak_str; mad_fixed_t peak; mad_timer_string(player->stats.play_timer, time_str, "%lu:%02u:%02u.%1u",MAD_UNITS_HOURS, MAD_UNITS_DECISECONDS, 0);# if defined(HAVE_LOCALECONV) { char *point; point = strchr(time_str, '.'); if (point) *point = *localeconv()->decimal_point; }# endif peak = MAD_F_ONE + player->stats.audio.peak_clipping; if (peak == MAD_F_ONE) peak = player->stats.audio.peak_sample; if (peak == 0) peak_str = "-inf"; else { sprintf(db_str, "%+.1f", 20 * log10(mad_f_todouble(peak))); peak_str = db_str; } message("%lu %s (%s), %s dB %s, %lu %s\n", player->stats.play_framecount, player->stats.play_framecount == 1 ? _("frame decoded") : _("frames decoded"), time_str, peak_str, _("peak amplitude"), player->stats.audio.clipped_samples, player->stats.audio.clipped_samples == 1 ? _("clipped sample") : _("clipped samples")); } if (player->input.fd != STDIN_FILENO && close(player->input.fd) == -1 && result == 0) { error(0, ":", player->input.path); result = -1; } return result;}/* NAME: play_all() DESCRIPTION: run the player's playlist *///被player_run()调用static int play_all(struct player *player){ int count, i, j, result = 0; struct playlist *playlist = &player->playlist; char const *tmp; /* set up playlist */ count = playlist->length; if (player->options & PLAYER_OPTION_SHUFFLE) { srand(time(0)); /* initial shuffle */ for (i = 0; i < count; ++i) { j = rand() % count; tmp = playlist->entries[i]; playlist->entries[i] = playlist->entries[j]; playlist->entries[j] = tmp; } } /* run playlist */ while (count && (player->repeat == -1 || player->repeat--)) { while (playlist->current < playlist->length) { i = playlist->current; if (playlist->entries[i] == 0) { ++playlist->current; continue; } player->control = PLAYER_CONTROL_DEFAULT; if (play_one(player) == -1) { playlist->entries[i] = 0; --count; result = -1; } if ((player->options & PLAYER_OPTION_TIMED) && mad_timer_compare(player->stats.global_timer,player->global_stop)>0) { count = 0; break; } switch (player->control) { case PLAYER_CONTROL_DEFAULT: if ((player->options & PLAYER_OPTION_SHUFFLE) && player->repeat && ++i < playlist->length) { /* pick something from the next half only */ j = (i + rand() % ((playlist->length + 1) / 2)) % playlist->length; tmp = playlist->entries[i]; playlist->entries[i] = playlist->entries[j]; playlist->entries[j] = tmp; } /* fall through */ case PLAYER_CONTROL_NEXT: ++playlist->current; break; case PLAYER_CONTROL_PREVIOUS: do { if (playlist->current-- == 0) playlist->current = playlist->length; } while (playlist->current < playlist->length && playlist->entries[playlist->current] == 0); break; case PLAYER_CONTROL_REPLAY: break; case PLAYER_CONTROL_STOP: playlist->current = playlist->length; count = 0; break; } } playlist->current = 0; } return result;}/* * NAME: stop_audio() * DESCRIPTION: stop playing the audio device immediately */staticint stop_audio(struct player *player, int flush){ int result = 0; if (player->output.command) { union audio_control control; audio_control_init(&control, AUDIO_COMMAND_STOP); control.stop.flush = flush; result = player->output.command(&control); } return result;}# if defined(USE_TTY)/* * NAME: readkey() * DESCRIPTION: read a keypress from the keyboard */staticint readkey(int blocking){# if !defined(_WIN32) unsigned char key; ssize_t count; if (!blocking) { /* tty_fd should be a tty in noncanonical mode with VMIN = VTIME = 0 */ count = read(tty_fd, &key, 1); if (count == -1 && errno != EINTR) { error("tty", ":read"); return -1; } return (count == 1) ? key : 0; } else { struct termios tty, save_tty; if (tcgetattr(tty_fd, &tty) == -1) { error("tty", ":tcgetattr"); return -1; } save_tty = tty; /* change terminal temporarily to get a blocking read() */ tty.c_cc[VMIN] = 1; if (tcsetattr(tty_fd, TCSANOW, &tty) == -1) { error("tty", ":tcsetattr"); return -1; } do count = read(tty_fd, &key, 1); while (count == -1 && errno == EINTR); if (count == -1) error("tty", ":read"); if (tcsetattr(tty_fd, TCSANOW, &save_tty) == -1) { error("tty", ":tcsetattr"); return -1; } if (count == -1) return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -