static int control(struct af_instance *af, int cmd, void *arg) { struct priv *s = af->priv; switch (cmd) { case AF_CONTROL_REINIT: { struct mp_audio *in = arg; mp_audio_copy_config(af->data, in); mp_audio_force_interleaved_format(af->data); if (s->fast && af_fmt_from_planar(in->format) != AF_FORMAT_FLOAT) { mp_audio_set_format(af->data, AF_FORMAT_S16); } else { mp_audio_set_format(af->data, AF_FORMAT_FLOAT); } if (af_fmt_is_planar(in->format)) mp_audio_set_format(af->data, af_fmt_to_planar(af->data->format)); return af_test_output(af, in); } case AF_CONTROL_SET_VOLUME: s->level = *(float *)arg; return AF_OK; case AF_CONTROL_GET_VOLUME: *(float *)arg = s->level; return AF_OK; } return AF_UNKNOWN; }
static void do_reorder(struct mp_audio *mpa, int *reorder) { if (af_fmt_is_planar(mpa->format)) { reorder_planes(mpa, reorder); } else { reorder_channels(mpa->planes[0], reorder, mpa->bps, mpa->nch, mpa->samples); } }
int af_fmt_seconds_to_bytes(int format, float seconds, int channels, int samplerate) { assert(!af_fmt_is_planar(format)); int bps = (af_fmt2bits(format) / 8); int framelen = channels * bps; int bytes = seconds * bps * samplerate; if (bytes % framelen) bytes += framelen - (bytes % framelen); return bytes; }
// must get exactly ac->aframesize amount of data static void encode(struct ao *ao, double apts, void **data) { struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; double realapts = ac->aframecount * (double) ac->aframesize / ao->samplerate; ac->aframecount++; if (data) ectx->audio_pts_offset = realapts - apts; if(data) { AVFrame *frame = av_frame_alloc(); frame->format = af_to_avformat(ao->format); frame->nb_samples = ac->aframesize; size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1; assert(num_planes <= AV_NUM_DATA_POINTERS); for (int n = 0; n < num_planes; n++) frame->extended_data[n] = data[n]; frame->linesize[0] = frame->nb_samples * ao->sstride; if (ectx->options->rawts || ectx->options->copyts) { // real audio pts frame->pts = floor(apts * ac->codec->time_base.den / ac->codec->time_base.num + 0.5); } else { // audio playback time frame->pts = floor(realapts * ac->codec->time_base.den / ac->codec->time_base.num + 0.5); } int64_t frame_pts = av_rescale_q(frame->pts, ac->codec->time_base, ac->worst_time_base); if (ac->lastpts != AV_NOPTS_VALUE && frame_pts <= ac->lastpts) { // this indicates broken video // (video pts failing to increase fast enough to match audio) MP_WARN(ao, "audio frame pts went backwards (%d <- %d), autofixed\n", (int)frame->pts, (int)ac->lastpts); frame_pts = ac->lastpts + 1; frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, ac->codec->time_base); } ac->lastpts = frame_pts; frame->quality = ac->codec->global_quality; encode_audio_and_write(ao, frame); av_frame_free(&frame); } else encode_audio_and_write(ao, NULL); }
static struct mp_audio *play(struct af_instance *af, struct mp_audio *data) { struct af_resample *s = af->priv; struct mp_audio *in = data; struct mp_audio *out = af->data; out->samples = avresample_available(s->avrctx) + av_rescale_rnd(get_delay(s) + in->samples, s->ctx.out_rate, s->ctx.in_rate, AV_ROUND_UP); mp_audio_realloc_min(out, out->samples); af->delay = get_delay(s) / (double)s->ctx.in_rate; #if !USE_SET_CHANNEL_MAPPING do_reorder(in, s->reorder_in); #endif if (out->samples) { out->samples = avresample_convert(s->avrctx, (uint8_t **) out->planes, out->samples * out->sstride, out->samples, (uint8_t **) in->planes, in->samples * in->sstride, in->samples); if (out->samples < 0) return NULL; // error } *data = *out; #if USE_SET_CHANNEL_MAPPING if (needs_reorder(s->reorder_out, out->nch)) { if (af_fmt_is_planar(out->format)) { reorder_planes(data, s->reorder_out); } else { int out_size = out->samples * out->sstride; if (talloc_get_size(s->reorder_buffer) < out_size) s->reorder_buffer = talloc_realloc_size(s, s->reorder_buffer, out_size); data->planes[0] = s->reorder_buffer; int out_samples = avresample_convert(s->avrctx_out, (uint8_t **) data->planes, out_size, out->samples, (uint8_t **) out->planes, out_size, out->samples); assert(out_samples == data->samples); } } #else do_reorder(data, s->reorder_out); #endif return data; }
// this should round samples down to frame sizes // return: number of samples played static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *ac = ao->priv; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; int bufpos = 0; double nextpts; double outpts; int orig_samples = samples; pthread_mutex_lock(&ectx->lock); if (!encode_lavc_start(ectx)) { MP_WARN(ao, "not ready yet for encoding audio\n"); pthread_mutex_unlock(&ectx->lock); return 0; } double pts = ectx->last_audio_in_pts; pts += ectx->samples_since_last_pts / (double)ao->samplerate; size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1; void *tempdata = NULL; void *padded[MP_NUM_CHANNELS]; if ((flags & AOPLAY_FINAL_CHUNK) && (samples % ac->aframesize)) { tempdata = talloc_new(NULL); size_t bytelen = samples * ao->sstride; size_t extralen = (ac->aframesize - 1) * ao->sstride; for (int n = 0; n < num_planes; n++) { padded[n] = talloc_size(tempdata, bytelen + extralen); memcpy(padded[n], data[n], bytelen); af_fill_silence((char *)padded[n] + bytelen, extralen, ao->format); } data = padded; samples = (bytelen + extralen) / ao->sstride; } if (pts == MP_NOPTS_VALUE) { MP_WARN(ao, "frame without pts, please report; synthesizing pts instead\n"); // synthesize pts from previous expected next pts pts = ac->expected_next_pts; } if (ac->worst_time_base.den == 0) { //if (ac->codec->time_base.num / ac->codec->time_base.den >= ac->stream->time_base.num / ac->stream->time_base.den) if (ac->codec->time_base.num * (double) ac->stream->time_base.den >= ac->stream->time_base.num * (double) ac->codec->time_base.den) { MP_VERBOSE(ao, "NOTE: using codec time base (%d/%d) for pts " "adjustment; the stream base (%d/%d) is not worse.\n", (int)ac->codec->time_base.num, (int)ac->codec->time_base.den, (int)ac->stream->time_base.num, (int)ac->stream->time_base.den); ac->worst_time_base = ac->codec->time_base; ac->worst_time_base_is_stream = 0; } else { MP_WARN(ao, "NOTE: not using codec time base (%d/%d) for pts " "adjustment; the stream base (%d/%d) is worse.\n", (int)ac->codec->time_base.num, (int)ac->codec->time_base.den, (int)ac->stream->time_base.num, (int)ac->stream->time_base.den); ac->worst_time_base = ac->stream->time_base; ac->worst_time_base_is_stream = 1; } // NOTE: we use the following "axiom" of av_rescale_q: // if time base A is worse than time base B, then // av_rescale_q(av_rescale_q(x, A, B), B, A) == x // this can be proven as long as av_rescale_q rounds to nearest, which // it currently does // av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B" // and: // av_rescale_q(av_rescale_q(x, A, B), B, A) * A // == "round av_rescale_q(x, A, B)*B to nearest multiple of A" // == "round 'round x*A to nearest multiple of B' to nearest multiple of A" // // assume this fails. Then there is a value of x*A, for which the // nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[. // Absurd, as this range MUST contain at least one multiple of B. } // Fix and apply the discontinuity pts offset. if (!ectx->options->rawts && ectx->options->copyts) { // fix the discontinuity pts offset nextpts = pts; if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) { ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts; } else if (fabs(nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) { MP_WARN(ao, "detected an unexpected discontinuity (pts jumped by " "%f seconds)\n", nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts); ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts; } outpts = pts + ectx->discontinuity_pts_offset; } else { outpts = pts; } // Shift pts by the pts offset first. outpts += encode_lavc_getoffset(ectx, ac->codec); while (samples - bufpos >= ac->aframesize) { void *start[MP_NUM_CHANNELS] = {0}; for (int n = 0; n < num_planes; n++) start[n] = (char *)data[n] + bufpos * ao->sstride; encode(ao, outpts + bufpos / (double) ao->samplerate, start); bufpos += ac->aframesize; } // Calculate expected pts of next audio frame (input side). ac->expected_next_pts = pts + bufpos / (double) ao->samplerate; // Set next allowed input pts value (input side). if (!ectx->options->rawts && ectx->options->copyts) { nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset; if (nextpts > ectx->next_in_pts) ectx->next_in_pts = nextpts; } talloc_free(tempdata); int taken = FFMIN(bufpos, orig_samples); ectx->samples_since_last_pts += taken; pthread_mutex_unlock(&ectx->lock); if (flags & AOPLAY_FINAL_CHUNK) { if (bufpos < orig_samples) { MP_ERR(ao, "did not write enough data at the end\n"); } } else { if (bufpos > orig_samples) { MP_ERR(ao, "audio buffer overflow (should never happen)\n"); } } return taken; }
int AudioController::reinitialize(mp_audio *in) { if (!in) return AF_ERROR; auto makeFormat = [] (const mp_audio *audio) { AudioFormat format; format.m_samplerate = audio->rate/1000.0; // kHz format.m_bitrate = audio->rate*audio->nch*audio->bps*8; format.m_bits = audio->bps*8; format.m_channels = ChannelLayoutInfo::description(ChannelLayoutMap::toLayout(audio->channels)); format.m_type = af_fmt_to_str(audio->format); return format; }; d->input = makeFormat(in); auto out = d->af->data; out->rate = in->rate; bool ret = true; if (!isSupported(in->format)) { ret = false; mp_audio_set_format(in, af_fmt_is_planar(in->format) ? AF_FORMAT_FLOATP : AF_FORMAT_FLOAT); } if (d->fmt_conv) { mp_audio_set_format(out, d->fmt_conv); d->fmt_conv = AF_FORMAT_UNKNOWN; } else mp_audio_set_format(out, in->format); d->chmap = in->channels; if (!mp_chmap_from_str(&d->chmap, bstr0(ChannelLayoutInfo::data(d->layout).constData()))) _Error("Cannot find matched channel layout for '%%'", ChannelLayoutInfo::description(d->layout)); mp_audio_set_channels(out, &d->chmap); if (d->outrate != 0) out->rate = d->outrate; if (!ret) return false; d->af->mul = (double)out->channels.num/in->channels.num; if (d->tempoScalerActivated) d->af->mul /= d->scale; if ((d->resample = out->rate != in->rate)) { d->af->mul *= (double)out->rate/in->rate; const auto nch = in->channels.num;/*mp_chmap_to_lavc_unchecked(&in->channels);*/ const auto fmt = af_to_avformat(in->format); if (!d->swr) d->swr = swr_alloc(); av_opt_set_int(d->swr, "in_channel_count", nch, 0); av_opt_set_int(d->swr, "out_channel_count", nch, 0); av_opt_set_int(d->swr, "in_sample_rate", in->rate, 0); av_opt_set_int(d->swr, "out_sample_rate", out->rate, 0); av_opt_set_sample_fmt(d->swr, "in_sample_fmt", fmt, 0); av_opt_set_sample_fmt(d->swr, "out_sample_fmt", fmt, 0); swr_init(d->swr); if (!d->resampled) d->resampled = talloc_zero(nullptr, mp_audio); *d->resampled = *in; d->resampled->rate = out->rate; in = d->resampled; } d->output = makeFormat(out); const AudioDataFormat fmt_in(*in), fmt_out(*out); check(d->mixer, d->clip, fmt_in, fmt_out); d->mixer->setOutput(out); d->mixer->setChannelLayoutMap(d->map); d->dirty = 0xffffffff; return true; }