mirror of
https://github.com/mpv-player/mpv
synced 2024-12-22 06:42:03 +00:00
audio: introduce a new type to hold audio frames
This is pretty pointless, but I believe it allows us to claim that the new code is not affected by the copyright of the old code. This is needed, because the original mp_audio struct was written by someone who has disagreed with LGPL relicensing (it was called af_data at the time, and was defined in af.h). The "GPL'ed" struct contents that surive are pretty trivial: just the data pointer, and some metadata like the format, samplerate, etc. - but at least in this case, any new code would be extremely similar anyway, and I'm not really sure whether it's OK to claim different copyright. So what we do is we just use AVFrame (which of course is LGPL with 100% certainty), and add some accessors around it to adapt it to mpv conventions. Also, this gets rid of some annoying conventions of mp_audio, like the struct fields that require using an accessor to write to them anyway. For the most part, this change is only dumb replacements of mp_audio related functions and fields. One minor actual change is that you can't allocate the new type on the stack anymore. Some code still uses mp_audio. All audio filter code will be deleted, so it makes no sense to convert this code. (Audio filters which are LGPL and which we keep will have to be ported to a new filter infrastructure anyway.) player/audio.c uses it because it interacts with the old filter code. push.c has some complex use of mp_audio and mp_audio_buffer, but this and pull.c will most likely be rewritten to do something else.
This commit is contained in:
parent
16e0a39482
commit
1f593beeb4
444
audio/aframe.c
Normal file
444
audio/aframe.c
Normal file
@ -0,0 +1,444 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* mpv is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libavutil/frame.h>
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include "chmap.h"
|
||||
#include "fmt-conversion.h"
|
||||
#include "format.h"
|
||||
#include "aframe.h"
|
||||
|
||||
struct mp_aframe {
|
||||
AVFrame *av_frame;
|
||||
// We support channel layouts different from AVFrame channel masks
|
||||
struct mp_chmap chmap;
|
||||
// We support spdif formats, which are allocated as AV_SAMPLE_FMT_S16.
|
||||
int format;
|
||||
double pts;
|
||||
};
|
||||
|
||||
static void free_frame(void *ptr)
|
||||
{
|
||||
struct mp_aframe *frame = ptr;
|
||||
av_frame_free(&frame->av_frame);
|
||||
}
|
||||
|
||||
struct mp_aframe *mp_aframe_create(void)
|
||||
{
|
||||
struct mp_aframe *frame = talloc_zero(NULL, struct mp_aframe);
|
||||
frame->pts = MP_NOPTS_VALUE;
|
||||
frame->av_frame = av_frame_alloc();
|
||||
if (!frame->av_frame)
|
||||
abort();
|
||||
talloc_set_destructor(frame, free_frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
struct mp_aframe *mp_aframe_new_ref(struct mp_aframe *frame)
|
||||
{
|
||||
if (!frame)
|
||||
return NULL;
|
||||
|
||||
struct mp_aframe *dst = mp_aframe_create();
|
||||
|
||||
dst->chmap = frame->chmap;
|
||||
dst->format = frame->format;
|
||||
dst->pts = frame->pts;
|
||||
|
||||
if (mp_aframe_is_allocated(frame)) {
|
||||
if (av_frame_ref(dst->av_frame, frame->av_frame) < 0)
|
||||
abort();
|
||||
} else {
|
||||
// av_frame_ref() would fail.
|
||||
mp_aframe_config_copy(dst, frame);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Revert to state after mp_aframe_create().
|
||||
void mp_aframe_reset(struct mp_aframe *frame)
|
||||
{
|
||||
av_frame_unref(frame->av_frame);
|
||||
frame->chmap.num = 0;
|
||||
frame->format = 0;
|
||||
frame->pts = MP_NOPTS_VALUE;
|
||||
}
|
||||
|
||||
// Remove all actual audio data and leave only the metadata.
|
||||
void mp_aframe_unref_data(struct mp_aframe *frame)
|
||||
{
|
||||
// In a fucked up way, this is less complex than just unreffing the data.
|
||||
struct mp_aframe *tmp = mp_aframe_create();
|
||||
MPSWAP(struct mp_aframe, *tmp, *frame);
|
||||
mp_aframe_reset(frame);
|
||||
mp_aframe_config_copy(frame, tmp);
|
||||
talloc_free(tmp);
|
||||
}
|
||||
|
||||
// Return a new reference to the data in av_frame. av_frame itself is not
|
||||
// touched. Returns NULL if not representable, or if input is NULL.
|
||||
// Does not copy the timestamps.
|
||||
struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame)
|
||||
{
|
||||
if (!av_frame || av_frame->width > 0 || av_frame->height > 0)
|
||||
return NULL;
|
||||
|
||||
int format = af_from_avformat(av_frame->format);
|
||||
if (!format && av_frame->format != AV_SAMPLE_FMT_NONE)
|
||||
return NULL;
|
||||
|
||||
struct mp_aframe *frame = mp_aframe_create();
|
||||
|
||||
// This also takes care of forcing refcounting.
|
||||
if (av_frame_ref(frame->av_frame, av_frame) < 0)
|
||||
abort();
|
||||
|
||||
frame->format = format;
|
||||
mp_chmap_from_lavc(&frame->chmap, frame->av_frame->channel_layout);
|
||||
|
||||
#if LIBAVUTIL_VERSION_MICRO >= 100
|
||||
// FFmpeg being a stupid POS again
|
||||
if (frame->chmap.num != frame->av_frame->channels)
|
||||
mp_chmap_from_channels(&frame->chmap, av_frame->channels);
|
||||
#endif
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
// Return a new reference to the data in frame. Returns NULL is not
|
||||
// representable (), or if input is NULL.
|
||||
// Does not copy the timestamps.
|
||||
struct AVFrame *mp_aframe_to_avframe(struct mp_aframe *frame)
|
||||
{
|
||||
if (!frame)
|
||||
return NULL;
|
||||
|
||||
if (af_to_avformat(frame->format) != frame->av_frame->format)
|
||||
return NULL;
|
||||
|
||||
if (!mp_chmap_is_lavc(&frame->chmap))
|
||||
return NULL;
|
||||
|
||||
return av_frame_clone(frame->av_frame);
|
||||
}
|
||||
|
||||
struct AVFrame *mp_aframe_to_avframe_and_unref(struct mp_aframe *frame)
|
||||
{
|
||||
AVFrame *av = mp_aframe_to_avframe(frame);
|
||||
talloc_free(frame);
|
||||
return av;
|
||||
}
|
||||
|
||||
// You must not use this.
|
||||
struct AVFrame *mp_aframe_get_raw_avframe(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->av_frame;
|
||||
}
|
||||
|
||||
// Return whether it has associated audio data. (If not, metadata only.)
|
||||
bool mp_aframe_is_allocated(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->av_frame->buf[0] || frame->av_frame->extended_data[0];
|
||||
}
|
||||
|
||||
// Clear dst, and then copy the configuration to it.
|
||||
void mp_aframe_config_copy(struct mp_aframe *dst, struct mp_aframe *src)
|
||||
{
|
||||
mp_aframe_reset(dst);
|
||||
|
||||
dst->chmap = src->chmap;
|
||||
dst->format = src->format;
|
||||
dst->pts = src->pts;
|
||||
|
||||
if (av_frame_copy_props(dst->av_frame, src->av_frame) < 0)
|
||||
abort();
|
||||
dst->av_frame->format = src->av_frame->format;
|
||||
dst->av_frame->channel_layout = src->av_frame->channel_layout;
|
||||
#if LIBAVUTIL_VERSION_MICRO >= 100
|
||||
// FFmpeg being a stupid POS again
|
||||
dst->av_frame->channels = src->av_frame->channels;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return whether a and b use the same physical audio format. Extra metadata
|
||||
// such as PTS, per-frame signalling, and AVFrame side data is not compared.
|
||||
bool mp_aframe_config_equals(struct mp_aframe *a, struct mp_aframe *b)
|
||||
{
|
||||
struct mp_chmap ca = {0}, cb = {0};
|
||||
mp_aframe_get_chmap(a, &ca);
|
||||
mp_aframe_get_chmap(b, &cb);
|
||||
return mp_chmap_equals(&ca, &cb) &&
|
||||
mp_aframe_get_rate(a) == mp_aframe_get_rate(b) &&
|
||||
mp_aframe_get_format(a) == mp_aframe_get_format(b);
|
||||
}
|
||||
|
||||
// Return whether all required format fields have been set.
|
||||
bool mp_aframe_config_is_valid(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->format && frame->chmap.num && frame->av_frame->sample_rate;
|
||||
}
|
||||
|
||||
// Return the pointer to the first sample for each plane. The pointers stay
|
||||
// valid until the next call that mutates frame somehow. You must not write to
|
||||
// the audio data. Returns NULL if no frame allocated.
|
||||
uint8_t **mp_aframe_get_data_ro(struct mp_aframe *frame)
|
||||
{
|
||||
return mp_aframe_is_allocated(frame) ? frame->av_frame->extended_data : NULL;
|
||||
}
|
||||
|
||||
// Like mp_aframe_get_data_ro(), but you can write to the audio data.
|
||||
// Additionally, it will return NULL if copy-on-write fails.
|
||||
uint8_t **mp_aframe_get_data_rw(struct mp_aframe *frame)
|
||||
{
|
||||
if (!mp_aframe_is_allocated(frame))
|
||||
return NULL;
|
||||
if (av_frame_make_writable(frame->av_frame) < 0)
|
||||
return NULL;
|
||||
return frame->av_frame->extended_data;
|
||||
}
|
||||
|
||||
int mp_aframe_get_format(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->format;
|
||||
}
|
||||
|
||||
bool mp_aframe_get_chmap(struct mp_aframe *frame, struct mp_chmap *out)
|
||||
{
|
||||
if (!mp_chmap_is_valid(&frame->chmap))
|
||||
return false;
|
||||
*out = frame->chmap;
|
||||
return true;
|
||||
}
|
||||
|
||||
int mp_aframe_get_channels(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->chmap.num;
|
||||
}
|
||||
|
||||
int mp_aframe_get_rate(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->av_frame->sample_rate;
|
||||
}
|
||||
|
||||
int mp_aframe_get_size(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->av_frame->nb_samples;
|
||||
}
|
||||
|
||||
double mp_aframe_get_pts(struct mp_aframe *frame)
|
||||
{
|
||||
return frame->pts;
|
||||
}
|
||||
|
||||
bool mp_aframe_set_format(struct mp_aframe *frame, int format)
|
||||
{
|
||||
if (mp_aframe_is_allocated(frame))
|
||||
return false;
|
||||
enum AVSampleFormat av_format = frame->av_frame->format;
|
||||
if (av_format == AV_SAMPLE_FMT_NONE && frame->format) {
|
||||
if (!af_fmt_is_spdif(format))
|
||||
return false;
|
||||
av_format = AV_SAMPLE_FMT_S16;
|
||||
}
|
||||
frame->format = format;
|
||||
frame->av_frame->format = av_format;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mp_aframe_set_chmap(struct mp_aframe *frame, struct mp_chmap *in)
|
||||
{
|
||||
if (!mp_chmap_is_valid(in) && !mp_chmap_is_empty(in))
|
||||
return false;
|
||||
if (mp_aframe_is_allocated(frame) && in->num != frame->chmap.num)
|
||||
return false;
|
||||
uint64_t lavc_layout = mp_chmap_to_lavc_unchecked(in);
|
||||
if (!lavc_layout && in->num)
|
||||
return false;
|
||||
frame->chmap = *in;
|
||||
frame->av_frame->channel_layout = lavc_layout;
|
||||
#if LIBAVUTIL_VERSION_MICRO >= 100
|
||||
// FFmpeg being a stupid POS again
|
||||
frame->av_frame->channels = frame->chmap.num;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mp_aframe_set_rate(struct mp_aframe *frame, int rate)
|
||||
{
|
||||
if (rate < 1 && rate > 10000000)
|
||||
return false;
|
||||
frame->av_frame->sample_rate = rate;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mp_aframe_set_size(struct mp_aframe *frame, int samples)
|
||||
{
|
||||
if (!mp_aframe_is_allocated(frame) || mp_aframe_get_size(frame) < samples)
|
||||
return false;
|
||||
frame->av_frame->nb_samples = MPMAX(samples, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mp_aframe_set_pts(struct mp_aframe *frame, double pts)
|
||||
{
|
||||
frame->pts = pts;
|
||||
}
|
||||
|
||||
// Return number of data pointers.
|
||||
int mp_aframe_get_planes(struct mp_aframe *frame)
|
||||
{
|
||||
return af_fmt_is_planar(mp_aframe_get_format(frame))
|
||||
? mp_aframe_get_channels(frame) : 1;
|
||||
}
|
||||
|
||||
// Return number of bytes between 2 consecutive samples on the same plane.
|
||||
size_t mp_aframe_get_sstride(struct mp_aframe *frame)
|
||||
{
|
||||
int format = mp_aframe_get_format(frame);
|
||||
return af_fmt_to_bytes(format) *
|
||||
(af_fmt_is_planar(format) ? 1 : mp_aframe_get_channels(frame));
|
||||
}
|
||||
|
||||
// Set data to the audio after the given number of samples (i.e. slice it).
|
||||
void mp_aframe_skip_samples(struct mp_aframe *f, int samples)
|
||||
{
|
||||
assert(samples >= 0 && samples <= mp_aframe_get_size(f));
|
||||
|
||||
int num_planes = mp_aframe_get_planes(f);
|
||||
size_t sstride = mp_aframe_get_sstride(f);
|
||||
for (int n = 0; n < num_planes; n++)
|
||||
f->av_frame->extended_data[n] += samples * sstride;
|
||||
|
||||
f->av_frame->nb_samples -= samples;
|
||||
|
||||
if (f->pts != MP_NOPTS_VALUE)
|
||||
f->pts += samples / (double)mp_aframe_get_rate(f);
|
||||
}
|
||||
|
||||
// Return the timestamp of the sample just after the end of this frame.
|
||||
double mp_aframe_end_pts(struct mp_aframe *f)
|
||||
{
|
||||
int rate = mp_aframe_get_rate(f);
|
||||
if (f->pts == MP_NOPTS_VALUE || rate < 1)
|
||||
return MP_NOPTS_VALUE;
|
||||
return f->pts + f->av_frame->nb_samples / (double)rate;
|
||||
}
|
||||
|
||||
// Return the duration in seconds of the frame (0 if invalid).
|
||||
double mp_aframe_duration(struct mp_aframe *f)
|
||||
{
|
||||
int rate = mp_aframe_get_rate(f);
|
||||
if (rate < 1)
|
||||
return 0;
|
||||
return f->av_frame->nb_samples / (double)rate;
|
||||
}
|
||||
|
||||
// Clip the given frame to the given timestamp range. Adjusts the frame size
|
||||
// and timestamp.
|
||||
void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end)
|
||||
{
|
||||
double f_end = mp_aframe_end_pts(f);
|
||||
int rate = mp_aframe_get_rate(f);
|
||||
if (f_end == MP_NOPTS_VALUE)
|
||||
return;
|
||||
if (end != MP_NOPTS_VALUE) {
|
||||
if (f_end >= end) {
|
||||
if (f->pts >= end) {
|
||||
f->av_frame->nb_samples = 0;
|
||||
} else {
|
||||
int new = (end - f->pts) * rate;
|
||||
f->av_frame->nb_samples = MPCLAMP(new, 0, f->av_frame->nb_samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start != MP_NOPTS_VALUE) {
|
||||
if (f->pts < start) {
|
||||
if (f_end <= start) {
|
||||
f->av_frame->nb_samples = 0;
|
||||
f->pts = f_end;
|
||||
} else {
|
||||
int skip = (start - f->pts) * rate;
|
||||
skip = MPCLAMP(skip, 0, f->av_frame->nb_samples);
|
||||
mp_aframe_skip_samples(f, skip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct mp_aframe_pool {
|
||||
AVBufferPool *avpool;
|
||||
int element_size;
|
||||
};
|
||||
|
||||
struct mp_aframe_pool *mp_aframe_pool_create(void *ta_parent)
|
||||
{
|
||||
return talloc_zero(ta_parent, struct mp_aframe_pool);
|
||||
}
|
||||
|
||||
static void mp_aframe_pool_destructor(void *p)
|
||||
{
|
||||
struct mp_aframe_pool *pool = p;
|
||||
av_buffer_pool_uninit(&pool->avpool);
|
||||
}
|
||||
|
||||
// Like mp_aframe_allocate(), but use the pool to allocate data.
|
||||
int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame,
|
||||
int samples)
|
||||
{
|
||||
int planes = mp_aframe_get_planes(frame);
|
||||
size_t sstride = mp_aframe_get_sstride(frame);
|
||||
int plane_size = MP_ALIGN_UP(sstride * MPMAX(samples, 1), 32);
|
||||
int size = plane_size * planes;
|
||||
|
||||
if (size <= 0 || mp_aframe_is_allocated(frame))
|
||||
return -1;
|
||||
|
||||
if (!pool->avpool || size > pool->element_size) {
|
||||
size_t alloc = ta_calc_prealloc_elems(size);
|
||||
if (alloc >= INT_MAX)
|
||||
return -1;
|
||||
av_buffer_pool_uninit(&pool->avpool);
|
||||
pool->element_size = alloc;
|
||||
pool->avpool = av_buffer_pool_init(pool->element_size, NULL);
|
||||
if (!pool->avpool)
|
||||
return -1;
|
||||
talloc_set_destructor(pool, mp_aframe_pool_destructor);
|
||||
}
|
||||
|
||||
// Yes, you have to do all this shit manually.
|
||||
// At least it's less stupid than av_frame_get_buffer(), which just wipes
|
||||
// the entire frame struct on error for no reason.
|
||||
AVFrame *av_frame = frame->av_frame;
|
||||
if (av_frame->extended_data != av_frame->data)
|
||||
av_freep(&av_frame->extended_data); // sigh
|
||||
av_frame->extended_data =
|
||||
av_mallocz_array(planes, sizeof(av_frame->extended_data[0]));
|
||||
if (!av_frame->extended_data)
|
||||
abort();
|
||||
av_frame->buf[0] = av_buffer_pool_get(pool->avpool);
|
||||
if (!av_frame->buf[0])
|
||||
return -1;
|
||||
av_frame->linesize[0] = samples * sstride;
|
||||
for (int n = 0; n < planes; n++)
|
||||
av_frame->extended_data[n] = av_frame->buf[0]->data + n * plane_size;
|
||||
av_frame->nb_samples = samples;
|
||||
|
||||
return 0;
|
||||
}
|
53
audio/aframe.h
Normal file
53
audio/aframe.h
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct mp_aframe;
|
||||
struct AVFrame;
|
||||
struct mp_chmap;
|
||||
|
||||
struct mp_aframe *mp_aframe_from_avframe(struct AVFrame *av_frame);
|
||||
struct mp_aframe *mp_aframe_create(void);
|
||||
struct mp_aframe *mp_aframe_new_ref(struct mp_aframe *frame);
|
||||
|
||||
void mp_aframe_reset(struct mp_aframe *frame);
|
||||
void mp_aframe_unref_data(struct mp_aframe *frame);
|
||||
|
||||
struct AVFrame *mp_aframe_to_avframe(struct mp_aframe *frame);
|
||||
struct AVFrame *mp_aframe_to_avframe_and_unref(struct mp_aframe *frame);
|
||||
struct AVFrame *mp_aframe_get_raw_avframe(struct mp_aframe *frame);
|
||||
|
||||
bool mp_aframe_is_allocated(struct mp_aframe *frame);
|
||||
|
||||
void mp_aframe_config_copy(struct mp_aframe *dst, struct mp_aframe *src);
|
||||
bool mp_aframe_config_equals(struct mp_aframe *a, struct mp_aframe *b);
|
||||
bool mp_aframe_config_is_valid(struct mp_aframe *frame);
|
||||
|
||||
uint8_t **mp_aframe_get_data_ro(struct mp_aframe *frame);
|
||||
uint8_t **mp_aframe_get_data_rw(struct mp_aframe *frame);
|
||||
|
||||
int mp_aframe_get_format(struct mp_aframe *frame);
|
||||
bool mp_aframe_get_chmap(struct mp_aframe *frame, struct mp_chmap *out);
|
||||
int mp_aframe_get_channels(struct mp_aframe *frame);
|
||||
int mp_aframe_get_rate(struct mp_aframe *frame);
|
||||
int mp_aframe_get_size(struct mp_aframe *frame);
|
||||
double mp_aframe_get_pts(struct mp_aframe *frame);
|
||||
|
||||
bool mp_aframe_set_format(struct mp_aframe *frame, int format);
|
||||
bool mp_aframe_set_chmap(struct mp_aframe *frame, struct mp_chmap *in);
|
||||
bool mp_aframe_set_rate(struct mp_aframe *frame, int rate);
|
||||
bool mp_aframe_set_size(struct mp_aframe *frame, int samples);
|
||||
void mp_aframe_set_pts(struct mp_aframe *frame, double pts);
|
||||
|
||||
int mp_aframe_get_planes(struct mp_aframe *frame);
|
||||
size_t mp_aframe_get_sstride(struct mp_aframe *frame);
|
||||
|
||||
void mp_aframe_skip_samples(struct mp_aframe *f, int samples);
|
||||
double mp_aframe_end_pts(struct mp_aframe *f);
|
||||
double mp_aframe_duration(struct mp_aframe *f);
|
||||
void mp_aframe_clip_timestamps(struct mp_aframe *f, double start, double end);
|
||||
|
||||
struct mp_aframe_pool;
|
||||
struct mp_aframe_pool *mp_aframe_pool_create(void *ta_parent);
|
||||
int mp_aframe_pool_allocate(struct mp_aframe_pool *pool, struct mp_aframe *frame,
|
||||
int samples);
|
@ -29,6 +29,7 @@
|
||||
#include "common/common.h"
|
||||
#include "fmt-conversion.h"
|
||||
#include "audio.h"
|
||||
#include "aframe.h"
|
||||
|
||||
static void update_redundant_info(struct mp_audio *mpa)
|
||||
{
|
||||
@ -403,6 +404,29 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe)
|
||||
{
|
||||
struct AVFrame *av = mp_aframe_get_raw_avframe(aframe);
|
||||
struct mp_audio *res = mp_audio_from_avframe(av);
|
||||
if (!res)
|
||||
return NULL;
|
||||
struct mp_chmap chmap = {0};
|
||||
mp_aframe_get_chmap(aframe, &chmap);
|
||||
mp_audio_set_channels(res, &chmap);
|
||||
mp_audio_set_format(res, mp_aframe_get_format(aframe));
|
||||
res->pts = mp_aframe_get_pts(aframe);
|
||||
return res;
|
||||
}
|
||||
|
||||
void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src)
|
||||
{
|
||||
struct mp_chmap chmap = {0};
|
||||
mp_aframe_get_chmap(src, &chmap);
|
||||
mp_audio_set_channels(dst, &chmap);
|
||||
mp_audio_set_format(dst, mp_aframe_get_format(src));
|
||||
dst->rate = mp_aframe_get_rate(src);
|
||||
}
|
||||
|
||||
int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe)
|
||||
{
|
||||
av_frame_unref(avframe);
|
||||
|
@ -83,6 +83,10 @@ struct mp_audio *mp_audio_from_avframe(struct AVFrame *avframe);
|
||||
struct AVFrame *mp_audio_to_avframe_and_unref(struct mp_audio *frame);
|
||||
int mp_audio_to_avframe(struct mp_audio *frame, struct AVFrame *avframe);
|
||||
|
||||
struct mp_aframe;
|
||||
struct mp_audio *mp_audio_from_aframe(struct mp_aframe *aframe);
|
||||
void mp_audio_config_from_aframe(struct mp_audio *dst, struct mp_aframe *src);
|
||||
|
||||
struct mp_audio_pool;
|
||||
struct mp_audio_pool *mp_audio_pool_create(void *ta_parent);
|
||||
struct mp_audio *mp_audio_pool_get(struct mp_audio_pool *pool,
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "demux/demux.h"
|
||||
|
||||
#include "audio/format.h"
|
||||
#include "audio/audio.h"
|
||||
#include "audio/aframe.h"
|
||||
#include "dec_audio.h"
|
||||
|
||||
struct mp_decoder_list;
|
||||
@ -39,7 +39,7 @@ struct ad_functions {
|
||||
bool (*send_packet)(struct dec_audio *da, struct demux_packet *pkt);
|
||||
// Return whether decoding is still going on (false if EOF was reached).
|
||||
// Never returns false & *out set, but can return true with !*out.
|
||||
bool (*receive_frame)(struct dec_audio *da, struct mp_audio **out);
|
||||
bool (*receive_frame)(struct dec_audio *da, struct mp_aframe **out);
|
||||
};
|
||||
|
||||
enum ad_ctrl {
|
||||
|
@ -40,7 +40,6 @@
|
||||
struct priv {
|
||||
AVCodecContext *avctx;
|
||||
AVFrame *avframe;
|
||||
struct mp_audio frame;
|
||||
bool force_channel_map;
|
||||
uint32_t skip_samples, trim_samples;
|
||||
bool preroll_done;
|
||||
@ -191,7 +190,7 @@ static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
|
||||
static bool receive_frame(struct dec_audio *da, struct mp_aframe **out)
|
||||
{
|
||||
struct priv *priv = da->priv;
|
||||
AVCodecContext *avctx = priv->avctx;
|
||||
@ -217,25 +216,18 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
|
||||
|
||||
double out_pts = mp_pts_from_av(priv->avframe->pts, &priv->codec_timebase);
|
||||
|
||||
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
|
||||
struct mp_aframe *mpframe = mp_aframe_from_avframe(priv->avframe);
|
||||
if (!mpframe)
|
||||
return true;
|
||||
|
||||
struct mp_chmap lavc_chmap = mpframe->channels;
|
||||
if (lavc_chmap.num != avctx->channels)
|
||||
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
|
||||
if (priv->force_channel_map) {
|
||||
if (lavc_chmap.num == da->codec->channels.num)
|
||||
lavc_chmap = da->codec->channels;
|
||||
}
|
||||
mp_audio_set_channels(mpframe, &lavc_chmap);
|
||||
if (priv->force_channel_map)
|
||||
mp_aframe_set_chmap(mpframe, &da->codec->channels);
|
||||
|
||||
mpframe->pts = out_pts;
|
||||
if (out_pts == MP_NOPTS_VALUE)
|
||||
out_pts = priv->next_pts;
|
||||
mp_aframe_set_pts(mpframe, out_pts);
|
||||
|
||||
if (mpframe->pts == MP_NOPTS_VALUE)
|
||||
mpframe->pts = priv->next_pts;
|
||||
if (mpframe->pts != MP_NOPTS_VALUE)
|
||||
priv->next_pts = mpframe->pts + mpframe->samples / (double)mpframe->rate;
|
||||
priv->next_pts = mp_aframe_end_pts(mpframe);
|
||||
|
||||
#if LIBAVCODEC_VERSION_MICRO >= 100
|
||||
AVFrameSideData *sd =
|
||||
@ -254,14 +246,14 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
|
||||
priv->preroll_done = true;
|
||||
}
|
||||
|
||||
uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
|
||||
uint32_t skip = MPMIN(priv->skip_samples, mp_aframe_get_size(mpframe));
|
||||
if (skip) {
|
||||
mp_audio_skip_samples(mpframe, skip);
|
||||
mp_aframe_skip_samples(mpframe, skip);
|
||||
priv->skip_samples -= skip;
|
||||
}
|
||||
uint32_t trim = MPMIN(priv->trim_samples, mpframe->samples);
|
||||
uint32_t trim = MPMIN(priv->trim_samples, mp_aframe_get_size(mpframe));
|
||||
if (trim) {
|
||||
mpframe->samples -= trim;
|
||||
mp_aframe_set_size(mpframe, mp_aframe_get_size(mpframe) - trim);
|
||||
priv->trim_samples -= trim;
|
||||
}
|
||||
|
||||
@ -269,7 +261,6 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
|
||||
|
||||
av_frame_unref(priv->avframe);
|
||||
|
||||
MP_DBG(da, "Decoded %d samples\n", mpframe->samples);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,9 @@ struct spdifContext {
|
||||
uint8_t out_buffer[OUTBUF_SIZE];
|
||||
bool need_close;
|
||||
bool use_dts_hd;
|
||||
struct mp_audio fmt;
|
||||
struct mp_audio_pool *pool;
|
||||
struct mp_aframe *fmt;
|
||||
int sstride;
|
||||
struct mp_aframe_pool *pool;
|
||||
bool got_eof;
|
||||
struct demux_packet *queued_packet;
|
||||
};
|
||||
@ -84,7 +85,7 @@ static int init(struct dec_audio *da, const char *decoder)
|
||||
da->priv = spdif_ctx;
|
||||
spdif_ctx->log = da->log;
|
||||
spdif_ctx->use_dts_hd = da->opts->dtshd;
|
||||
spdif_ctx->pool = mp_audio_pool_create(spdif_ctx);
|
||||
spdif_ctx->pool = mp_aframe_pool_create(spdif_ctx);
|
||||
|
||||
if (strcmp(decoder, "spdif_dts_hd") == 0)
|
||||
spdif_ctx->use_dts_hd = true;
|
||||
@ -198,6 +199,9 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
|
||||
|
||||
AVDictionary *format_opts = NULL;
|
||||
|
||||
spdif_ctx->fmt = mp_aframe_create();
|
||||
talloc_steal(spdif_ctx, spdif_ctx->fmt);
|
||||
|
||||
int num_channels = 0;
|
||||
int sample_format = 0;
|
||||
int samplerate = 0;
|
||||
@ -246,9 +250,14 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
mp_audio_set_num_channels(&spdif_ctx->fmt, num_channels);
|
||||
mp_audio_set_format(&spdif_ctx->fmt, sample_format);
|
||||
spdif_ctx->fmt.rate = samplerate;
|
||||
|
||||
struct mp_chmap chmap;
|
||||
mp_chmap_from_channels(&chmap, num_channels);
|
||||
mp_aframe_set_chmap(spdif_ctx->fmt, &chmap);
|
||||
mp_aframe_set_format(spdif_ctx->fmt, sample_format);
|
||||
mp_aframe_set_rate(spdif_ctx->fmt, samplerate);
|
||||
|
||||
spdif_ctx->sstride = mp_aframe_get_sstride(spdif_ctx->fmt);
|
||||
|
||||
if (avformat_write_header(lavf_ctx, &format_opts) < 0) {
|
||||
MP_FATAL(da, "libavformat spdif initialization failed.\n");
|
||||
@ -279,7 +288,7 @@ static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
|
||||
static bool receive_frame(struct dec_audio *da, struct mp_aframe **out)
|
||||
{
|
||||
struct spdifContext *spdif_ctx = da->priv;
|
||||
|
||||
@ -308,13 +317,21 @@ static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
|
||||
goto done;
|
||||
}
|
||||
|
||||
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
|
||||
*out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples);
|
||||
if (!*out)
|
||||
*out = mp_aframe_new_ref(spdif_ctx->fmt);
|
||||
int samples = spdif_ctx->out_buffer_len / spdif_ctx->sstride;
|
||||
if (mp_aframe_pool_allocate(spdif_ctx->pool, *out, samples) < 0) {
|
||||
TA_FREEP(out);
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
|
||||
(*out)->pts = pts;
|
||||
uint8_t **data = mp_aframe_get_data_rw(*out);
|
||||
if (!data) {
|
||||
TA_FREEP(out);
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(data[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
|
||||
mp_aframe_set_pts(*out, pts);
|
||||
|
||||
done:
|
||||
talloc_free(spdif_ctx->queued_packet);
|
||||
|
@ -38,8 +38,6 @@
|
||||
#include "dec_audio.h"
|
||||
#include "ad.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/audio.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
|
||||
#include "audio/filter/af.h"
|
||||
|
||||
@ -179,25 +177,24 @@ static void fix_audio_pts(struct dec_audio *da)
|
||||
if (!da->current_frame)
|
||||
return;
|
||||
|
||||
if (da->current_frame->pts != MP_NOPTS_VALUE) {
|
||||
double newpts = da->current_frame->pts;
|
||||
|
||||
double frame_pts = mp_aframe_get_pts(da->current_frame);
|
||||
if (frame_pts != MP_NOPTS_VALUE) {
|
||||
if (da->pts != MP_NOPTS_VALUE)
|
||||
MP_STATS(da, "value %f audio-pts-err", da->pts - newpts);
|
||||
MP_STATS(da, "value %f audio-pts-err", da->pts - frame_pts);
|
||||
|
||||
// Keep the interpolated timestamp if it doesn't deviate more
|
||||
// than 1 ms from the real one. (MKV rounded timestamps.)
|
||||
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001)
|
||||
da->pts = newpts;
|
||||
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - frame_pts) > 0.001)
|
||||
da->pts = frame_pts;
|
||||
}
|
||||
|
||||
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
|
||||
da->pts = 0;
|
||||
|
||||
da->current_frame->pts = da->pts;
|
||||
mp_aframe_set_pts(da->current_frame, da->pts);
|
||||
|
||||
if (da->pts != MP_NOPTS_VALUE)
|
||||
da->pts += da->current_frame->samples / (double)da->current_frame->rate;
|
||||
da->pts += mp_aframe_duration(da->current_frame);
|
||||
}
|
||||
|
||||
void audio_work(struct dec_audio *da)
|
||||
@ -228,11 +225,6 @@ void audio_work(struct dec_audio *da)
|
||||
|
||||
bool progress = da->ad_driver->receive_frame(da, &da->current_frame);
|
||||
|
||||
if (da->current_frame && !mp_audio_config_valid(da->current_frame)) {
|
||||
talloc_free(da->current_frame);
|
||||
da->current_frame = NULL;
|
||||
}
|
||||
|
||||
da->current_state = da->current_frame ? DATA_OK : DATA_AGAIN;
|
||||
if (!progress)
|
||||
da->current_state = DATA_EOF;
|
||||
@ -242,10 +234,11 @@ void audio_work(struct dec_audio *da)
|
||||
bool segment_end = da->current_state == DATA_EOF;
|
||||
|
||||
if (da->current_frame) {
|
||||
mp_audio_clip_timestamps(da->current_frame, da->start, da->end);
|
||||
if (da->current_frame->pts != MP_NOPTS_VALUE && da->start != MP_NOPTS_VALUE)
|
||||
segment_end = da->current_frame->pts >= da->end;
|
||||
if (da->current_frame->samples == 0) {
|
||||
mp_aframe_clip_timestamps(da->current_frame, da->start, da->end);
|
||||
double frame_pts = mp_aframe_get_pts(da->current_frame);
|
||||
if (frame_pts != MP_NOPTS_VALUE && da->start != MP_NOPTS_VALUE)
|
||||
segment_end = frame_pts >= da->end;
|
||||
if (mp_aframe_get_size(da->current_frame) == 0) {
|
||||
talloc_free(da->current_frame);
|
||||
da->current_frame = NULL;
|
||||
}
|
||||
@ -280,7 +273,7 @@ void audio_work(struct dec_audio *da)
|
||||
// DATA_WAIT: waiting for demuxer; will receive a wakeup signal
|
||||
// DATA_EOF: end of file, no more frames to be expected
|
||||
// DATA_AGAIN: dropped frame or something similar
|
||||
int audio_get_frame(struct dec_audio *da, struct mp_audio **out_frame)
|
||||
int audio_get_frame(struct dec_audio *da, struct mp_aframe **out_frame)
|
||||
{
|
||||
*out_frame = NULL;
|
||||
if (da->current_frame) {
|
||||
|
@ -19,11 +19,10 @@
|
||||
#define MPLAYER_DEC_AUDIO_H
|
||||
|
||||
#include "audio/chmap.h"
|
||||
#include "audio/audio.h"
|
||||
#include "audio/aframe.h"
|
||||
#include "demux/demux.h"
|
||||
#include "demux/stheader.h"
|
||||
|
||||
struct mp_audio_buffer;
|
||||
struct mp_decoder_list;
|
||||
|
||||
struct dec_audio {
|
||||
@ -48,7 +47,7 @@ struct dec_audio {
|
||||
double start, end;
|
||||
struct demux_packet *packet;
|
||||
struct demux_packet *new_segment;
|
||||
struct mp_audio *current_frame;
|
||||
struct mp_aframe *current_frame;
|
||||
int current_state;
|
||||
};
|
||||
|
||||
@ -57,7 +56,7 @@ int audio_init_best_codec(struct dec_audio *d_audio);
|
||||
void audio_uninit(struct dec_audio *d_audio);
|
||||
|
||||
void audio_work(struct dec_audio *d_audio);
|
||||
int audio_get_frame(struct dec_audio *d_audio, struct mp_audio **out_frame);
|
||||
int audio_get_frame(struct dec_audio *d_audio, struct mp_aframe **out_frame);
|
||||
|
||||
void audio_reset_decoding(struct dec_audio *d_audio);
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "ao.h"
|
||||
#include "internal.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/audio.h"
|
||||
|
||||
#include "options/options.h"
|
||||
#include "options/m_config.h"
|
||||
@ -480,12 +479,12 @@ bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
|
||||
|
||||
// --- The following functions just return immutable information.
|
||||
|
||||
void ao_get_format(struct ao *ao, struct mp_audio *format)
|
||||
void ao_get_format(struct ao *ao,
|
||||
int *samplerate, int *format, struct mp_chmap *channels)
|
||||
{
|
||||
*format = (struct mp_audio){0};
|
||||
mp_audio_set_format(format, ao->format);
|
||||
mp_audio_set_channels(format, &ao->channels);
|
||||
format->rate = ao->samplerate;
|
||||
*samplerate = ao->samplerate;
|
||||
*format = ao->format;
|
||||
*channels = ao->channels;
|
||||
}
|
||||
|
||||
const char *ao_get_name(struct ao *ao)
|
||||
|
@ -81,7 +81,6 @@ struct ao;
|
||||
struct mpv_global;
|
||||
struct input_ctx;
|
||||
struct encode_lavc_context;
|
||||
struct mp_audio;
|
||||
|
||||
struct ao *ao_init_best(struct mpv_global *global,
|
||||
int init_flags,
|
||||
@ -89,7 +88,8 @@ struct ao *ao_init_best(struct mpv_global *global,
|
||||
struct encode_lavc_context *encode_lavc_ctx,
|
||||
int samplerate, int format, struct mp_chmap channels);
|
||||
void ao_uninit(struct ao *ao);
|
||||
void ao_get_format(struct ao *ao, struct mp_audio *format);
|
||||
void ao_get_format(struct ao *ao,
|
||||
int *samplerate, int *format, struct mp_chmap *channels);
|
||||
const char *ao_get_name(struct ao *ao);
|
||||
const char *ao_get_description(struct ao *ao);
|
||||
bool ao_untimed(struct ao *ao);
|
||||
|
@ -352,6 +352,7 @@ static void ao_chain_uninit(struct ao_chain *ao_c)
|
||||
|
||||
af_destroy(ao_c->af);
|
||||
talloc_free(ao_c->input_frame);
|
||||
talloc_free(ao_c->input_format);
|
||||
talloc_free(ao_c->ao_buffer);
|
||||
talloc_free(ao_c);
|
||||
}
|
||||
@ -368,6 +369,18 @@ void uninit_audio_chain(struct MPContext *mpctx)
|
||||
}
|
||||
}
|
||||
|
||||
static void get_ao_format(struct ao *ao, struct mp_audio *aformat)
|
||||
{
|
||||
int samplerate;
|
||||
int format;
|
||||
struct mp_chmap channels;
|
||||
ao_get_format(ao, &samplerate, &format, &channels);
|
||||
*aformat = (struct mp_audio){0};
|
||||
mp_audio_set_format(aformat, format);
|
||||
mp_audio_set_channels(aformat, &channels);
|
||||
aformat->rate = samplerate;
|
||||
}
|
||||
|
||||
static void reinit_audio_filters_and_output(struct MPContext *mpctx)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
@ -377,11 +390,9 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
|
||||
struct af_stream *afs = ao_c->af;
|
||||
|
||||
if (ao_c->input_frame)
|
||||
mp_audio_copy_config(&ao_c->input_format, ao_c->input_frame);
|
||||
mp_aframe_config_copy(ao_c->input_format, ao_c->input_frame);
|
||||
|
||||
struct mp_audio in_format = ao_c->input_format;
|
||||
|
||||
if (!mp_audio_config_valid(&in_format)) {
|
||||
if (!mp_aframe_config_is_valid(ao_c->input_format)) {
|
||||
// We don't know the audio format yet - so configure it later as we're
|
||||
// resyncing. fill_audio_buffers() will call this function again.
|
||||
mp_wakeup_core(mpctx);
|
||||
@ -390,18 +401,20 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
|
||||
|
||||
// Weak gapless audio: drain AO on decoder format changes
|
||||
if (mpctx->ao_decoder_fmt && mpctx->ao && opts->gapless_audio < 0 &&
|
||||
!mp_audio_config_equals(mpctx->ao_decoder_fmt, &in_format))
|
||||
!mp_aframe_config_equals(mpctx->ao_decoder_fmt, ao_c->input_format))
|
||||
{
|
||||
uninit_audio_out(mpctx);
|
||||
}
|
||||
|
||||
struct mp_audio in_format;
|
||||
mp_audio_config_from_aframe(&in_format, ao_c->input_format);
|
||||
if (mpctx->ao && mp_audio_config_equals(&in_format, &afs->input))
|
||||
return;
|
||||
|
||||
afs->output = (struct mp_audio){0};
|
||||
if (mpctx->ao) {
|
||||
ao_get_format(mpctx->ao, &afs->output);
|
||||
} else if (af_fmt_is_pcm(in_format.format)) {
|
||||
get_ao_format(mpctx->ao, &afs->output);
|
||||
} else if (af_fmt_is_pcm(mp_aframe_get_format(ao_c->input_format))) {
|
||||
afs->output.rate = opts->force_srate;
|
||||
mp_audio_set_format(&afs->output, opts->audio_output_format);
|
||||
if (opts->audio_output_channels.num_chmaps == 1) {
|
||||
@ -455,7 +468,7 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
|
||||
|
||||
struct mp_audio fmt = {0};
|
||||
if (mpctx->ao)
|
||||
ao_get_format(mpctx->ao, &fmt);
|
||||
get_ao_format(mpctx->ao, &fmt);
|
||||
|
||||
// Verify passthrough format was not changed.
|
||||
if (mpctx->ao && af_fmt_is_spdif(afs->output.format)) {
|
||||
@ -477,7 +490,7 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
|
||||
if (!audio_init_best_codec(ao_c->audio_src))
|
||||
goto init_error;
|
||||
reset_audio_state(mpctx);
|
||||
ao_c->input_format = (struct mp_audio){0};
|
||||
mp_aframe_reset(ao_c->input_format);
|
||||
mp_wakeup_core(mpctx); // reinit with new format next time
|
||||
return;
|
||||
}
|
||||
@ -492,8 +505,8 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx)
|
||||
if (!mp_audio_config_equals(&afs->output, &afs->filter_output))
|
||||
afs->initialized = 0;
|
||||
|
||||
mpctx->ao_decoder_fmt = talloc(NULL, struct mp_audio);
|
||||
*mpctx->ao_decoder_fmt = in_format;
|
||||
mpctx->ao_decoder_fmt = mp_aframe_create();
|
||||
mp_aframe_config_copy(mpctx->ao_decoder_fmt, ao_c->input_format);
|
||||
|
||||
MP_INFO(mpctx, "AO: [%s] %s\n", ao_get_name(mpctx->ao),
|
||||
mp_audio_config_to_str(&fmt));
|
||||
@ -580,6 +593,7 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct track *track)
|
||||
ao_c->pts = MP_NOPTS_VALUE;
|
||||
ao_c->ao_buffer = mp_audio_buffer_create(NULL);
|
||||
ao_c->ao = mpctx->ao;
|
||||
ao_c->input_format = mp_aframe_create();
|
||||
|
||||
if (track) {
|
||||
ao_c->track = track;
|
||||
@ -593,7 +607,7 @@ void reinit_audio_chain_src(struct MPContext *mpctx, struct track *track)
|
||||
|
||||
if (mpctx->ao) {
|
||||
struct mp_audio fmt;
|
||||
ao_get_format(mpctx->ao, &fmt);
|
||||
get_ao_format(mpctx->ao, &fmt);
|
||||
mp_audio_buffer_reinit(ao_c->ao_buffer, &fmt);
|
||||
}
|
||||
|
||||
@ -614,9 +628,7 @@ double written_audio_pts(struct MPContext *mpctx)
|
||||
if (!ao_c)
|
||||
return MP_NOPTS_VALUE;
|
||||
|
||||
struct mp_audio in_format = ao_c->input_format;
|
||||
|
||||
if (!mp_audio_config_valid(&in_format) || ao_c->af->initialized < 1)
|
||||
if (ao_c->af->initialized < 1)
|
||||
return MP_NOPTS_VALUE;
|
||||
|
||||
// first calculate the end pts of audio that has been output by decoder
|
||||
@ -653,7 +665,7 @@ static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags
|
||||
return 0;
|
||||
struct ao *ao = mpctx->ao;
|
||||
struct mp_audio out_format;
|
||||
ao_get_format(ao, &out_format);
|
||||
get_ao_format(ao, &out_format);
|
||||
#if HAVE_ENCODING
|
||||
encode_lavc_set_audio_pts(mpctx->encode_lavc_ctx, playing_audio_pts(mpctx));
|
||||
#endif
|
||||
@ -706,7 +718,7 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
|
||||
return true;
|
||||
|
||||
struct mp_audio out_format = {0};
|
||||
ao_get_format(mpctx->ao, &out_format);
|
||||
get_ao_format(mpctx->ao, &out_format);
|
||||
double play_samplerate = out_format.rate / mpctx->audio_speed;
|
||||
|
||||
if (!opts->initial_audio_sync) {
|
||||
@ -879,14 +891,19 @@ static int filter_audio(struct MPContext *mpctx, struct mp_audio_buffer *outbuf,
|
||||
}
|
||||
|
||||
// On format change, make sure to drain the filter chain.
|
||||
if (!mp_audio_config_equals(&afs->input, ao_c->input_frame)) {
|
||||
struct mp_audio in_format;
|
||||
mp_audio_config_from_aframe(&in_format, ao_c->input_format);
|
||||
if (!mp_audio_config_equals(&afs->input, &in_format)) {
|
||||
copy_output(mpctx, outbuf, minsamples, endpts, true, &eof);
|
||||
res = AD_NEW_FMT;
|
||||
break;
|
||||
}
|
||||
|
||||
struct mp_audio *mpa = ao_c->input_frame;
|
||||
struct mp_audio *mpa = mp_audio_from_aframe(ao_c->input_frame);
|
||||
talloc_free(ao_c->input_frame);
|
||||
ao_c->input_frame = NULL;
|
||||
if (!mpa)
|
||||
abort();
|
||||
if (mpa->pts == MP_NOPTS_VALUE) {
|
||||
ao_c->pts = MP_NOPTS_VALUE;
|
||||
} else {
|
||||
@ -990,7 +1007,7 @@ void fill_audio_out_buffers(struct MPContext *mpctx)
|
||||
}
|
||||
|
||||
struct mp_audio out_format = {0};
|
||||
ao_get_format(mpctx->ao, &out_format);
|
||||
get_ao_format(mpctx->ao, &out_format);
|
||||
double play_samplerate = out_format.rate / mpctx->audio_speed;
|
||||
int align = af_format_sample_alignment(out_format.format);
|
||||
|
||||
|
@ -56,7 +56,7 @@
|
||||
#include "video/decode/vd.h"
|
||||
#include "video/out/vo.h"
|
||||
#include "video/csputils.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
#include "audio/aframe.h"
|
||||
#include "audio/out/ao.h"
|
||||
#include "audio/filter/af.h"
|
||||
#include "video/decode/dec_video.h"
|
||||
@ -2019,17 +2019,20 @@ static int mp_property_audio_codec(void *ctx, struct m_property *prop,
|
||||
return m_property_strdup_ro(action, arg, c);
|
||||
}
|
||||
|
||||
static int property_audiofmt(struct mp_audio a, int action, void *arg)
|
||||
static int property_audiofmt(struct mp_aframe *fmt, int action, void *arg)
|
||||
{
|
||||
if (!mp_audio_config_valid(&a))
|
||||
if (!fmt || !mp_aframe_config_is_valid(fmt))
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
struct mp_chmap chmap = {0};
|
||||
mp_aframe_get_chmap(fmt, &chmap);
|
||||
|
||||
struct m_sub_property props[] = {
|
||||
{"samplerate", SUB_PROP_INT(a.rate)},
|
||||
{"channel-count", SUB_PROP_INT(a.channels.num)},
|
||||
{"channels", SUB_PROP_STR(mp_chmap_to_str(&a.channels))},
|
||||
{"hr-channels", SUB_PROP_STR(mp_chmap_to_str_hr(&a.channels))},
|
||||
{"format", SUB_PROP_STR(af_fmt_to_str(a.format))},
|
||||
{"samplerate", SUB_PROP_INT(mp_aframe_get_rate(fmt))},
|
||||
{"channel-count", SUB_PROP_INT(chmap.num)},
|
||||
{"channels", SUB_PROP_STR(mp_chmap_to_str(&chmap))},
|
||||
{"hr-channels", SUB_PROP_STR(mp_chmap_to_str_hr(&chmap))},
|
||||
{"format", SUB_PROP_STR(af_fmt_to_str(mp_aframe_get_format(fmt)))},
|
||||
{0}
|
||||
};
|
||||
|
||||
@ -2040,20 +2043,28 @@ static int mp_property_audio_params(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct mp_audio fmt = {0};
|
||||
if (mpctx->ao_chain)
|
||||
fmt = mpctx->ao_chain->input_format;
|
||||
return property_audiofmt(fmt, action, arg);
|
||||
return property_audiofmt(mpctx->ao_chain ? mpctx->ao_chain->input_format : NULL,
|
||||
action, arg);
|
||||
}
|
||||
|
||||
static int mp_property_audio_out_params(void *ctx, struct m_property *prop,
|
||||
int action, void *arg)
|
||||
{
|
||||
MPContext *mpctx = ctx;
|
||||
struct mp_audio fmt = {0};
|
||||
if (mpctx->ao)
|
||||
ao_get_format(mpctx->ao, &fmt);
|
||||
return property_audiofmt(fmt, action, arg);
|
||||
struct mp_aframe *frame = NULL;
|
||||
if (mpctx->ao) {
|
||||
frame = mp_aframe_create();
|
||||
int samplerate;
|
||||
int format;
|
||||
struct mp_chmap channels;
|
||||
ao_get_format(mpctx->ao, &samplerate, &format, &channels);
|
||||
mp_aframe_set_rate(frame, samplerate);
|
||||
mp_aframe_set_format(frame, format);
|
||||
mp_aframe_set_chmap(frame, &channels);
|
||||
}
|
||||
int r = property_audiofmt(frame, action, arg);
|
||||
talloc_free(frame);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// Balance (RW)
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "common/common.h"
|
||||
#include "options/options.h"
|
||||
#include "sub/osd.h"
|
||||
#include "audio/audio.h"
|
||||
#include "audio/aframe.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "video/out/vo.h"
|
||||
|
||||
@ -205,10 +205,10 @@ struct ao_chain {
|
||||
double ao_resume_time;
|
||||
|
||||
// 1-element input frame queue.
|
||||
struct mp_audio *input_frame;
|
||||
struct mp_aframe *input_frame;
|
||||
|
||||
// Last known input_mpi format (so vf can be reinitialized any time).
|
||||
struct mp_audio input_format;
|
||||
// Last known input_mpi format (so af can be reinitialized any time).
|
||||
struct mp_aframe *input_format;
|
||||
|
||||
struct track *track;
|
||||
struct lavfi_pad *filter_src;
|
||||
@ -313,7 +313,7 @@ typedef struct MPContext {
|
||||
struct lavfi *lavfi;
|
||||
|
||||
struct ao *ao;
|
||||
struct mp_audio *ao_decoder_fmt; // for weak gapless audio check
|
||||
struct mp_aframe *ao_decoder_fmt; // for weak gapless audio check
|
||||
struct ao_chain *ao_chain;
|
||||
|
||||
struct vo_chain *vo_chain;
|
||||
|
@ -37,7 +37,8 @@
|
||||
#include "common/av_common.h"
|
||||
#include "common/msg.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/aframe.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
#include "video/fmt-conversion.h"
|
||||
@ -96,7 +97,7 @@ struct lavfi_pad {
|
||||
|
||||
// 1-frame queue (used for both input and output)
|
||||
struct mp_image *pending_v;
|
||||
struct mp_audio *pending_a;
|
||||
struct mp_aframe *pending_a;
|
||||
|
||||
// -- dir==LAVFI_IN
|
||||
|
||||
@ -107,7 +108,7 @@ struct lavfi_pad {
|
||||
|
||||
// used to check for format changes manually
|
||||
struct mp_image *in_fmt_v;
|
||||
struct mp_audio in_fmt_a;
|
||||
struct mp_aframe *in_fmt_a;
|
||||
|
||||
// -- dir==LAVFI_OUT
|
||||
|
||||
@ -199,7 +200,7 @@ static void free_graph(struct lavfi *c)
|
||||
pad->filter_pad = -1;
|
||||
pad->buffer = NULL;
|
||||
TA_FREEP(&pad->in_fmt_v);
|
||||
pad->in_fmt_a = (struct mp_audio){0};
|
||||
TA_FREEP(&pad->in_fmt_a);
|
||||
pad->buffer_is_eof = false;
|
||||
pad->input_needed = false;
|
||||
pad->input_waiting = false;
|
||||
@ -312,9 +313,14 @@ static void send_global_eof(struct lavfi *c)
|
||||
|
||||
// libavfilter allows changing some parameters on the fly, but not
|
||||
// others.
|
||||
static bool is_aformat_ok(struct mp_audio *a, struct mp_audio *b)
|
||||
static bool is_aformat_ok(struct mp_aframe *a, struct mp_aframe *b)
|
||||
{
|
||||
return mp_audio_config_equals(a, b);
|
||||
struct mp_chmap ca = {0}, cb = {0};
|
||||
mp_aframe_get_chmap(a, &ca);
|
||||
mp_aframe_get_chmap(b, &cb);
|
||||
return mp_chmap_equals(&ca, &cb) &&
|
||||
mp_aframe_get_rate(a) == mp_aframe_get_rate(b) &&
|
||||
mp_aframe_get_format(a) == mp_aframe_get_format(b);
|
||||
}
|
||||
static bool is_vformat_ok(struct mp_image *a, struct mp_image *b)
|
||||
{
|
||||
@ -331,9 +337,9 @@ static void check_format_changes(struct lavfi *c)
|
||||
if (!pad->buffer || pad->dir != LAVFI_IN)
|
||||
continue;
|
||||
|
||||
if (pad->type == STREAM_AUDIO && pad->pending_a && pad->in_fmt_a.format) {
|
||||
if (pad->type == STREAM_AUDIO && pad->pending_a && pad->in_fmt_a) {
|
||||
c->draining_new_format |= !is_aformat_ok(pad->pending_a,
|
||||
&pad->in_fmt_a);
|
||||
pad->in_fmt_a);
|
||||
}
|
||||
if (pad->type == STREAM_VIDEO && pad->pending_v && pad->in_fmt_v) {
|
||||
c->draining_new_format |= !is_vformat_ok(pad->pending_v,
|
||||
@ -389,7 +395,10 @@ static bool init_pads(struct lavfi *c)
|
||||
|
||||
if (pad->pending_a) {
|
||||
assert(pad->type == STREAM_AUDIO);
|
||||
mp_audio_copy_config(&pad->in_fmt_a, pad->pending_a);
|
||||
pad->in_fmt_a = mp_aframe_new_ref(pad->pending_a);
|
||||
if (!pad->in_fmt_a)
|
||||
goto error;
|
||||
mp_aframe_unref_data(pad->in_fmt_a);
|
||||
} else if (pad->pending_v) {
|
||||
assert(pad->type == STREAM_VIDEO);
|
||||
pad->in_fmt_v = mp_image_new_ref(pad->pending_v);
|
||||
@ -400,9 +409,11 @@ static bool init_pads(struct lavfi *c)
|
||||
// libavfilter makes this painful. Init it with a dummy config,
|
||||
// just so we can tell it the stream is EOF.
|
||||
if (pad->type == STREAM_AUDIO) {
|
||||
mp_audio_set_format(&pad->in_fmt_a, AF_FORMAT_FLOAT);
|
||||
mp_audio_set_num_channels(&pad->in_fmt_a, 2);
|
||||
pad->in_fmt_a.rate = 48000;
|
||||
pad->in_fmt_a = mp_aframe_create();
|
||||
mp_aframe_set_format(pad->in_fmt_a, AF_FORMAT_FLOAT);
|
||||
mp_aframe_set_chmap(pad->in_fmt_a,
|
||||
&(struct mp_chmap)MP_CHMAP_INIT_STEREO);
|
||||
mp_aframe_set_rate(pad->in_fmt_a, 48000);
|
||||
} else if (pad->type == STREAM_VIDEO) {
|
||||
pad->in_fmt_v = talloc_zero(NULL, struct mp_image);
|
||||
mp_image_setfmt(pad->in_fmt_v, IMGFMT_420P);
|
||||
@ -421,11 +432,13 @@ static bool init_pads(struct lavfi *c)
|
||||
char *filter_name = NULL;
|
||||
if (pad->type == STREAM_AUDIO) {
|
||||
params->time_base = pad->timebase =
|
||||
(AVRational){1, pad->in_fmt_a.rate};
|
||||
params->format = af_to_avformat(pad->in_fmt_a.format);
|
||||
params->sample_rate = pad->in_fmt_a.rate;
|
||||
params->channel_layout =
|
||||
mp_chmap_to_lavc(&pad->in_fmt_a.channels);
|
||||
(AVRational){1, mp_aframe_get_rate(pad->in_fmt_a)};
|
||||
params->format =
|
||||
af_to_avformat(mp_aframe_get_format(pad->in_fmt_a));
|
||||
params->sample_rate = mp_aframe_get_rate(pad->in_fmt_a);
|
||||
struct mp_chmap chmap = {0};
|
||||
mp_aframe_get_chmap(pad->in_fmt_a, &chmap);
|
||||
params->channel_layout = mp_chmap_to_lavc(&chmap);
|
||||
filter_name = "abuffer";
|
||||
} else if (pad->type == STREAM_VIDEO) {
|
||||
params->time_base = pad->timebase = AV_TIME_BASE_Q;
|
||||
@ -565,8 +578,8 @@ static void feed_input_pads(struct lavfi *c)
|
||||
frame = mp_image_to_av_frame_and_unref(pad->pending_v);
|
||||
pad->pending_v = NULL;
|
||||
} else if (pad->pending_a) {
|
||||
pts = pad->pending_a->pts;
|
||||
frame = mp_audio_to_avframe_and_unref(pad->pending_a);
|
||||
pts = mp_aframe_get_pts(pad->pending_a);
|
||||
frame = mp_aframe_to_avframe_and_unref(pad->pending_a);
|
||||
pad->pending_a = NULL;
|
||||
} else {
|
||||
if (!pad->input_eof) {
|
||||
@ -621,9 +634,9 @@ static void read_output_pads(struct lavfi *c)
|
||||
pad->output_needed = false;
|
||||
double pts = mp_pts_from_av(c->tmp_frame->pts, &pad->timebase);
|
||||
if (pad->type == STREAM_AUDIO) {
|
||||
pad->pending_a = mp_audio_from_avframe(c->tmp_frame);
|
||||
pad->pending_a = mp_aframe_from_avframe(c->tmp_frame);
|
||||
if (pad->pending_a)
|
||||
pad->pending_a->pts = pts;
|
||||
mp_aframe_set_pts(pad->pending_a, pts);
|
||||
} else if (pad->type == STREAM_VIDEO) {
|
||||
pad->pending_v = mp_image_from_av_frame(c->tmp_frame);
|
||||
if (pad->pending_v)
|
||||
@ -733,7 +746,7 @@ static int lavfi_request_frame(struct lavfi_pad *pad)
|
||||
// DATA_STARVE: needs more input data
|
||||
// DATA_WAIT: needs more input data, and all inputs in LAVFI_WAIT state
|
||||
// DATA_EOF: no more data
|
||||
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe)
|
||||
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_aframe **out_aframe)
|
||||
{
|
||||
int r = lavfi_request_frame(pad);
|
||||
*out_aframe = pad->pending_a;
|
||||
@ -787,7 +800,7 @@ static void lavfi_sent_frame(struct lavfi_pad *pad)
|
||||
}
|
||||
|
||||
// See lavfi_send_status() for remarks.
|
||||
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_audio *aframe)
|
||||
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_aframe *aframe)
|
||||
{
|
||||
assert(pad->type == STREAM_AUDIO);
|
||||
assert(!pad->pending_a);
|
||||
|
@ -5,7 +5,7 @@ struct mp_log;
|
||||
struct lavfi;
|
||||
struct lavfi_pad;
|
||||
struct mp_image;
|
||||
struct mp_audio;
|
||||
struct mp_aframe;
|
||||
|
||||
enum lavfi_direction {
|
||||
LAVFI_IN = 1,
|
||||
@ -25,11 +25,11 @@ bool lavfi_has_failed(struct lavfi *c);
|
||||
void lavfi_seek_reset(struct lavfi *c);
|
||||
void lavfi_pad_set_hwdec_devs(struct lavfi_pad *pad,
|
||||
struct mp_hwdec_devices *hwdevs);
|
||||
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_audio **out_aframe);
|
||||
int lavfi_request_frame_a(struct lavfi_pad *pad, struct mp_aframe **out_aframe);
|
||||
int lavfi_request_frame_v(struct lavfi_pad *pad, struct mp_image **out_vframe);
|
||||
bool lavfi_needs_input(struct lavfi_pad *pad);
|
||||
void lavfi_send_status(struct lavfi_pad *pad, int status);
|
||||
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_audio *aframe);
|
||||
void lavfi_send_frame_a(struct lavfi_pad *pad, struct mp_aframe *aframe);
|
||||
void lavfi_send_frame_v(struct lavfi_pad *pad, struct mp_image *vframe);
|
||||
|
||||
#endif
|
||||
|
@ -43,8 +43,6 @@
|
||||
#include "common/recorder.h"
|
||||
#include "input/input.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "audio/audio_buffer.h"
|
||||
#include "audio/decode/dec_audio.h"
|
||||
#include "audio/out/ao.h"
|
||||
#include "demux/demux.h"
|
||||
|
@ -1053,7 +1053,7 @@ static void handle_complex_filter_decoders(struct MPContext *mpctx)
|
||||
continue;
|
||||
if (track->d_audio) {
|
||||
audio_work(track->d_audio);
|
||||
struct mp_audio *fr;
|
||||
struct mp_aframe *fr;
|
||||
int res = audio_get_frame(track->d_audio, &fr);
|
||||
if (res == DATA_OK) {
|
||||
lavfi_send_frame_a(track->sink, fr);
|
||||
|
@ -1053,9 +1053,11 @@ static double find_best_speed(struct MPContext *mpctx, double vsync)
|
||||
static bool using_spdif_passthrough(struct MPContext *mpctx)
|
||||
{
|
||||
if (mpctx->ao_chain && mpctx->ao_chain->ao) {
|
||||
struct mp_audio out_format = {0};
|
||||
ao_get_format(mpctx->ao_chain->ao, &out_format);
|
||||
return !af_fmt_is_pcm(out_format.format);
|
||||
int samplerate;
|
||||
int format;
|
||||
struct mp_chmap channels;
|
||||
ao_get_format(mpctx->ao_chain->ao, &samplerate, &format, &channels);
|
||||
return !af_fmt_is_pcm(format);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -157,6 +157,7 @@ def build(ctx):
|
||||
( "audio/chmap_sel.c" ),
|
||||
( "audio/fmt-conversion.c" ),
|
||||
( "audio/format.c" ),
|
||||
( "audio/aframe.c" ),
|
||||
( "audio/decode/ad_lavc.c" ),
|
||||
( "audio/decode/ad_spdif.c" ),
|
||||
( "audio/decode/dec_audio.c" ),
|
||||
|
Loading…
Reference in New Issue
Block a user