mirror of
https://github.com/mpv-player/mpv
synced 2025-02-19 22:36:55 +00:00
ao_lavc: support non-interleaved audio
This commit is contained in:
parent
95ed81c329
commit
933fbf7333
@ -22,6 +22,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libavutil/common.h>
|
||||
#include <libavutil/audioconvert.h>
|
||||
@ -31,22 +32,17 @@
|
||||
#include "mpvcore/options.h"
|
||||
#include "mpvcore/mp_common.h"
|
||||
#include "audio/format.h"
|
||||
#include "audio/reorder_ch.h"
|
||||
#include "audio/fmt-conversion.h"
|
||||
#include "talloc.h"
|
||||
#include "ao.h"
|
||||
#include "mpvcore/mp_msg.h"
|
||||
|
||||
#include "mpvcore/encode_lavc.h"
|
||||
|
||||
static const char *sample_padding_signed = "\x00\x00\x00\x00";
|
||||
static const char *sample_padding_u8 = "\x80";
|
||||
static const char *sample_padding_float = "\x00\x00\x00\x00";
|
||||
|
||||
struct priv {
|
||||
uint8_t *buffer;
|
||||
size_t buffer_size;
|
||||
AVStream *stream;
|
||||
bool planarize;
|
||||
int pcmhack;
|
||||
int aframesize;
|
||||
int aframecount;
|
||||
@ -103,143 +99,34 @@ static int init(struct ao *ao)
|
||||
ac->stream->codec->channel_layout = mp_chmap_to_lavc(&ao->channels);
|
||||
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_NONE;
|
||||
ao->format = af_fmt_from_planar(ao->format);
|
||||
|
||||
{
|
||||
// first check if the selected format is somewhere in the list of
|
||||
// supported formats by the codec
|
||||
for (sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat) {
|
||||
switch (*sampleformat) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
if (ao->format == AF_FORMAT_U8)
|
||||
goto out_search;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
if (ao->format == AF_FORMAT_S16_BE)
|
||||
goto out_search;
|
||||
if (ao->format == AF_FORMAT_S16_LE)
|
||||
goto out_search;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
if (ao->format == AF_FORMAT_S32_BE)
|
||||
goto out_search;
|
||||
if (ao->format == AF_FORMAT_S32_LE)
|
||||
goto out_search;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
if (ao->format == AF_FORMAT_FLOAT_BE)
|
||||
goto out_search;
|
||||
if (ao->format == AF_FORMAT_FLOAT_LE)
|
||||
goto out_search;
|
||||
break;
|
||||
// FIXME do we need support for AV_SAMPLE_FORMAT_DBL/DBLP?
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_search:
|
||||
;
|
||||
// first check if the selected format is somewhere in the list of
|
||||
// supported formats by the codec
|
||||
for (sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat) {
|
||||
if (ao->format == af_from_avformat(*sampleformat))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sampleformat || *sampleformat == AV_SAMPLE_FMT_NONE) {
|
||||
// if the selected format is not supported, we have to pick the first
|
||||
// one we CAN support
|
||||
// note: not needing to select endianness here, as the switch() below
|
||||
// does that anyway for us
|
||||
for (sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat) {
|
||||
switch (*sampleformat) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
ao->format = AF_FORMAT_U8;
|
||||
goto out_takefirst;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
ao->format = AF_FORMAT_S16_NE;
|
||||
goto out_takefirst;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
ao->format = AF_FORMAT_S32_NE;
|
||||
goto out_takefirst;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
ao->format = AF_FORMAT_FLOAT_NE;
|
||||
goto out_takefirst;
|
||||
// FIXME do we need support for AV_SAMPLE_FORMAT_DBL/DBLP?
|
||||
default:
|
||||
int format = af_from_avformat(*sampleformat);
|
||||
if (format) {
|
||||
ao->format = format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_takefirst:
|
||||
;
|
||||
}
|
||||
|
||||
switch (ao->format) {
|
||||
// now that we have chosen a format, set up the fields for it, boldly
|
||||
// switching endianness if needed (mplayer code will convert for us
|
||||
// anyway, but ffmpeg always expects native endianness)
|
||||
case AF_FORMAT_U8:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_U8;
|
||||
ac->sample_size = 1;
|
||||
ac->sample_padding = sample_padding_u8;
|
||||
ao->format = AF_FORMAT_U8;
|
||||
break;
|
||||
default:
|
||||
case AF_FORMAT_S16_BE:
|
||||
case AF_FORMAT_S16_LE:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
ac->sample_size = 2;
|
||||
ac->sample_padding = sample_padding_signed;
|
||||
ao->format = AF_FORMAT_S16_NE;
|
||||
break;
|
||||
case AF_FORMAT_S32_BE:
|
||||
case AF_FORMAT_S32_LE:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_S32;
|
||||
ac->sample_size = 4;
|
||||
ac->sample_padding = sample_padding_signed;
|
||||
ao->format = AF_FORMAT_S32_NE;
|
||||
break;
|
||||
case AF_FORMAT_FLOAT_BE:
|
||||
case AF_FORMAT_FLOAT_LE:
|
||||
ac->stream->codec->sample_fmt = AV_SAMPLE_FMT_FLT;
|
||||
ac->sample_size = 4;
|
||||
ac->sample_padding = sample_padding_float;
|
||||
ao->format = AF_FORMAT_FLOAT_NE;
|
||||
break;
|
||||
}
|
||||
|
||||
// detect if we have to planarize
|
||||
ac->planarize = false;
|
||||
{
|
||||
bool found_format = false;
|
||||
bool found_planar_format = false;
|
||||
for (sampleformat = codec->sample_fmts;
|
||||
sampleformat && *sampleformat != AV_SAMPLE_FMT_NONE;
|
||||
++sampleformat) {
|
||||
if (*sampleformat == ac->stream->codec->sample_fmt)
|
||||
found_format = true;
|
||||
if (*sampleformat ==
|
||||
av_get_planar_sample_fmt(ac->stream->codec->sample_fmt))
|
||||
found_planar_format = true;
|
||||
}
|
||||
if (!found_format && found_planar_format) {
|
||||
ac->stream->codec->sample_fmt =
|
||||
av_get_planar_sample_fmt(ac->stream->codec->sample_fmt);
|
||||
ac->planarize = true;
|
||||
}
|
||||
if (!found_format && !found_planar_format) {
|
||||
// shouldn't happen
|
||||
MP_ERR(ao, "sample format not found\n");
|
||||
}
|
||||
if (!sampleformat)
|
||||
MP_ERR(ao, "sample format not found\n"); // shouldn't happen
|
||||
}
|
||||
|
||||
ac->sample_size = af_fmt2bits(ao->format) / 8;
|
||||
ac->stream->codec->sample_fmt = af_to_avformat(ao->format);
|
||||
ac->stream->codec->bits_per_raw_sample = ac->sample_size * 8;
|
||||
|
||||
if (encode_lavc_open_codec(ao->encode_lavc_ctx, ac->stream) < 0)
|
||||
@ -273,23 +160,9 @@ out_takefirst:
|
||||
ao->untimed = true;
|
||||
ao->priv = ac;
|
||||
|
||||
if (ac->planarize)
|
||||
MP_WARN(ao, "need to planarize audio data\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fill_with_padding(void *buf, int cnt, int sz, const void *padding)
|
||||
{
|
||||
int i;
|
||||
if (sz == 1) {
|
||||
memset(buf, cnt, *(char *)padding);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < cnt; ++i)
|
||||
memcpy((char *) buf + i * sz, padding, sz);
|
||||
}
|
||||
|
||||
// close audio device
|
||||
static void uninit(struct ao *ao, bool cut_audio)
|
||||
{
|
||||
@ -312,7 +185,7 @@ static int get_space(struct ao *ao)
|
||||
}
|
||||
|
||||
// must get exactly ac->aframesize amount of data
|
||||
static int encode(struct ao *ao, double apts, void *data)
|
||||
static int encode(struct ao *ao, double apts, void **data)
|
||||
{
|
||||
AVFrame *frame;
|
||||
AVPacket packet;
|
||||
@ -335,22 +208,11 @@ static int encode(struct ao *ao, double apts, void *data)
|
||||
frame = avcodec_alloc_frame();
|
||||
frame->nb_samples = ac->aframesize;
|
||||
|
||||
if (ac->planarize) {
|
||||
void *data2 = talloc_size(ao, ac->aframesize * ao->channels.num *
|
||||
ac->sample_size);
|
||||
reorder_to_planar(data2, data, ac->sample_size, ao->channels.num,
|
||||
ac->aframesize);
|
||||
data = data2;
|
||||
}
|
||||
assert(ao->channels.num <= AV_NUM_DATA_POINTERS);
|
||||
for (int n = 0; n < ao->channels.num; n++)
|
||||
frame->extended_data[n] = data[n];
|
||||
|
||||
size_t audiolen = ac->aframesize * ao->channels.num * ac->sample_size;
|
||||
if (avcodec_fill_audio_frame(frame, ao->channels.num,
|
||||
ac->stream->codec->sample_fmt, data,
|
||||
audiolen, 1))
|
||||
{
|
||||
MP_ERR(ao, "error filling\n");
|
||||
return -1;
|
||||
}
|
||||
frame->linesize[0] = frame->nb_samples * ao->sstride;
|
||||
|
||||
if (ectx->options->rawts || ectx->options->copyts) {
|
||||
// real audio pts
|
||||
@ -380,11 +242,6 @@ static int encode(struct ao *ao, double apts, void *data)
|
||||
}
|
||||
|
||||
avcodec_free_frame(&frame);
|
||||
|
||||
if (ac->planarize) {
|
||||
talloc_free(data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -437,23 +294,16 @@ static int encode(struct ao *ao, double apts, void *data)
|
||||
return packet.size;
|
||||
}
|
||||
|
||||
// plays 'samples' samples of 'ni_data[0]'
|
||||
// it should round it down to frame sizes
|
||||
// this should round samples down to frame sizes
|
||||
// return: number of samples played
|
||||
static int play(struct ao *ao, void **ni_data, int samples, int flags)
|
||||
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;
|
||||
void *paddingbuf = NULL;
|
||||
double nextpts;
|
||||
double pts = ao->pts;
|
||||
double outpts;
|
||||
void *data = ni_data[0];
|
||||
int len = samples * ao->sstride;
|
||||
int bytelen = len;
|
||||
|
||||
len /= ac->sample_size * ao->channels.num;
|
||||
|
||||
if (!encode_lavc_start(ectx)) {
|
||||
MP_WARN(ao, "not ready yet for encoding audio\n");
|
||||
@ -462,22 +312,22 @@ static int play(struct ao *ao, void **ni_data, int samples, int flags)
|
||||
|
||||
if (flags & AOPLAY_FINAL_CHUNK) {
|
||||
int written = 0;
|
||||
if (len > 0) {
|
||||
size_t extralen =
|
||||
(ac->aframesize - 1) * ao->channels.num * ac->sample_size;
|
||||
paddingbuf = talloc_size(NULL, bytelen + extralen);
|
||||
memcpy(paddingbuf, data, bytelen);
|
||||
fill_with_padding((char *) paddingbuf + bytelen,
|
||||
extralen / ac->sample_size,
|
||||
ac->sample_size, ac->sample_padding);
|
||||
if (samples > 0) {
|
||||
void *tmp = talloc_new(NULL);
|
||||
size_t bytelen = samples * ao->sstride;
|
||||
size_t extralen = (ac->aframesize - 1) * ao->sstride;
|
||||
void *padded[MP_NUM_CHANNELS];
|
||||
for (int n = 0; n < ao->channels.num; n++) {
|
||||
padded[n] = talloc_size(tmp, bytelen + extralen);
|
||||
memcpy(padded[n], data[n], bytelen);
|
||||
af_fill_silence((char *)padded[n] + bytelen, extralen, ao->format);
|
||||
}
|
||||
// No danger of recursion, because AOPLAY_FINAL_CHUNK not set
|
||||
written =
|
||||
play(ao, &paddingbuf, (bytelen + extralen) / ao->sstride, 0);
|
||||
if (written * ao->sstride < bytelen) {
|
||||
written = play(ao, padded, (bytelen + extralen) / ao->sstride, 0);
|
||||
if (written < samples) {
|
||||
MP_ERR(ao, "did not write enough data at the end\n");
|
||||
}
|
||||
talloc_free(paddingbuf);
|
||||
paddingbuf = NULL;
|
||||
talloc_free(tmp);
|
||||
}
|
||||
|
||||
outpts = ac->expected_next_pts;
|
||||
@ -487,7 +337,7 @@ static int play(struct ao *ao, void **ni_data, int samples, int flags)
|
||||
|
||||
while (encode(ao, outpts, NULL) > 0) ;
|
||||
|
||||
return (FFMIN(written, bytelen)) / ao->sstride;
|
||||
return FFMIN(written, samples);
|
||||
}
|
||||
|
||||
if (pts == MP_NOPTS_VALUE) {
|
||||
@ -559,15 +409,14 @@ static int play(struct ao *ao, void **ni_data, int samples, int flags)
|
||||
// Shift pts by the pts offset first.
|
||||
outpts += encode_lavc_getoffset(ectx, ac->stream);
|
||||
|
||||
while (len - bufpos >= ac->aframesize) {
|
||||
encode(ao,
|
||||
outpts + bufpos / (double) ao->samplerate,
|
||||
(char *) data + ac->sample_size * bufpos * ao->channels.num);
|
||||
while (samples - bufpos >= ac->aframesize) {
|
||||
void *start[MP_NUM_CHANNELS];
|
||||
for (int n = 0; n < ao->channels.num; n++)
|
||||
start[n] = (char *)data[n] + bufpos * ao->sstride;
|
||||
encode(ao, outpts + bufpos / (double) ao->samplerate, start);
|
||||
bufpos += ac->aframesize;
|
||||
}
|
||||
|
||||
talloc_free(paddingbuf);
|
||||
|
||||
// Calculate expected pts of next audio frame (input side).
|
||||
ac->expected_next_pts = pts + bufpos / (double) ao->samplerate;
|
||||
|
||||
|
@ -27,42 +27,6 @@
|
||||
#include "chmap.h"
|
||||
#include "reorder_ch.h"
|
||||
|
||||
static inline void reorder_to_planar_(void *restrict out, const void *restrict in,
|
||||
size_t size, size_t nchan, size_t nmemb)
|
||||
{
|
||||
size_t i, c;
|
||||
char *outptr = (char *) out;
|
||||
size_t instep = nchan * size;
|
||||
|
||||
for (c = 0; c < nchan; ++c) {
|
||||
const char *inptr = ((const char *) in) + c * size;
|
||||
for (i = 0; i < nmemb; ++i, inptr += instep, outptr += size) {
|
||||
memcpy(outptr, inptr, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reorder_to_planar(void *restrict out, const void *restrict in,
|
||||
size_t size, size_t nchan, size_t nmemb)
|
||||
{
|
||||
// special case for mono (nothing to do...)
|
||||
if (nchan == 1)
|
||||
memcpy(out, in, size * nchan * nmemb);
|
||||
// these calls exist to insert an inline copy of to_planar_ here with known
|
||||
// value of size to help the compiler replace the memcpy calls by mov
|
||||
// instructions
|
||||
else if (size == 1)
|
||||
reorder_to_planar_(out, in, 1, nchan, nmemb);
|
||||
else if (size == 2)
|
||||
reorder_to_planar_(out, in, 2, nchan, nmemb);
|
||||
else if (size == 4)
|
||||
reorder_to_planar_(out, in, 4, nchan, nmemb);
|
||||
// general case (calls memcpy a lot, should actually never happen, but
|
||||
// stays here for correctness purposes)
|
||||
else
|
||||
reorder_to_planar_(out, in, size, nchan, nmemb);
|
||||
}
|
||||
|
||||
#define MAX_SAMPLESIZE 8
|
||||
|
||||
static void reorder_channels_(uint8_t *restrict data, int *restrict ch_order,
|
||||
|
@ -25,9 +25,6 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void reorder_to_planar(void *restrict out, const void *restrict in,
|
||||
size_t size, size_t nchan, size_t nmemb);
|
||||
|
||||
void reorder_channels(void *restrict data, int *restrict ch_order,
|
||||
size_t sample_size, size_t num_ch, size_t num_frames);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user