mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-26 12:59:19 -05:00
[xcode] Make sample rate + channels variable
This change is preparation to use ffmpeg's resampling capabilities to keep local audio in sync (by up/downsampling slightly). This requires that sample rates are not fixed for a transcode profile. Added benefit of this is that we don't need quite as many xcode profiles.
This commit is contained in:
parent
413ce25ec6
commit
53780a7ef3
@ -408,7 +408,7 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *inbuf, int max_
|
|||||||
|
|
||||||
DPRINTF(E_SPAM, L_ART, "Getting artwork (max destination width %d height %d)\n", max_w, max_h);
|
DPRINTF(E_SPAM, L_ART, "Getting artwork (max destination width %d height %d)\n", max_w, max_h);
|
||||||
|
|
||||||
xcode_decode = transcode_decode_setup(XCODE_JPEG, DATA_KIND_FILE, path, inbuf, 0); // Covers XCODE_PNG too
|
xcode_decode = transcode_decode_setup(XCODE_JPEG, NULL, DATA_KIND_FILE, path, inbuf, 0); // Covers XCODE_PNG too
|
||||||
if (!xcode_decode)
|
if (!xcode_decode)
|
||||||
{
|
{
|
||||||
if (path)
|
if (path)
|
||||||
@ -462,9 +462,9 @@ artwork_get(struct evbuffer *evbuf, char *path, struct evbuffer *inbuf, int max_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (format_ok == ART_FMT_JPEG)
|
if (format_ok == ART_FMT_JPEG)
|
||||||
xcode_encode = transcode_encode_setup(XCODE_JPEG, xcode_decode, NULL, target_w, target_h);
|
xcode_encode = transcode_encode_setup(XCODE_JPEG, NULL, xcode_decode, NULL, target_w, target_h);
|
||||||
else
|
else
|
||||||
xcode_encode = transcode_encode_setup(XCODE_PNG, xcode_decode, NULL, target_w, target_h);
|
xcode_encode = transcode_encode_setup(XCODE_PNG, NULL, xcode_decode, NULL, target_w, target_h);
|
||||||
|
|
||||||
if (!xcode_encode)
|
if (!xcode_encode)
|
||||||
{
|
{
|
||||||
|
@ -80,6 +80,11 @@
|
|||||||
"<h1>%s</h1>\n" \
|
"<h1>%s</h1>\n" \
|
||||||
"</body>\n</html>\n"
|
"</body>\n</html>\n"
|
||||||
|
|
||||||
|
#define HTTPD_STREAM_SAMPLE_RATE 44100
|
||||||
|
#define HTTPD_STREAM_BPS 16
|
||||||
|
#define HTTPD_STREAM_CHANNELS 2
|
||||||
|
|
||||||
|
|
||||||
struct content_type_map {
|
struct content_type_map {
|
||||||
char *ext;
|
char *ext;
|
||||||
char *ctype;
|
char *ctype;
|
||||||
@ -1029,6 +1034,7 @@ httpd_request_parse(struct evhttp_request *req, struct httpd_uri_parsed *uri_par
|
|||||||
void
|
void
|
||||||
httpd_stream_file(struct evhttp_request *req, int id)
|
httpd_stream_file(struct evhttp_request *req, int id)
|
||||||
{
|
{
|
||||||
|
struct media_quality quality = { HTTPD_STREAM_SAMPLE_RATE, HTTPD_STREAM_BPS, HTTPD_STREAM_CHANNELS };
|
||||||
struct media_file_info *mfi;
|
struct media_file_info *mfi;
|
||||||
struct stream_ctx *st;
|
struct stream_ctx *st;
|
||||||
void (*stream_cb)(int fd, short event, void *arg);
|
void (*stream_cb)(int fd, short event, void *arg);
|
||||||
@ -1128,7 +1134,7 @@ httpd_stream_file(struct evhttp_request *req, int id)
|
|||||||
|
|
||||||
stream_cb = stream_chunk_xcode_cb;
|
stream_cb = stream_chunk_xcode_cb;
|
||||||
|
|
||||||
st->xcode = transcode_setup(XCODE_PCM16_HEADER, mfi->data_kind, mfi->path, mfi->song_length, &st->size);
|
st->xcode = transcode_setup(XCODE_PCM16_HEADER, &quality, mfi->data_kind, mfi->path, mfi->song_length, &st->size);
|
||||||
if (!st->xcode)
|
if (!st->xcode)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_HTTPD, "Transcoding setup failed, aborting streaming\n");
|
DPRINTF(E_WARN, L_HTTPD, "Transcoding setup failed, aborting streaming\n");
|
||||||
|
@ -47,6 +47,11 @@ extern struct event_base *evbase_httpd;
|
|||||||
// How many bytes we try to read at a time from the httpd pipe
|
// How many bytes we try to read at a time from the httpd pipe
|
||||||
#define STREAMING_READ_SIZE STOB(352, 16, 2)
|
#define STREAMING_READ_SIZE STOB(352, 16, 2)
|
||||||
|
|
||||||
|
#define STREAMING_MP3_SAMPLE_RATE 44100
|
||||||
|
#define STREAMING_MP3_BPS 16
|
||||||
|
#define STREAMING_MP3_CHANNELS 2
|
||||||
|
|
||||||
|
|
||||||
// Linked list of mp3 streaming requests
|
// Linked list of mp3 streaming requests
|
||||||
struct streaming_session {
|
struct streaming_session {
|
||||||
struct evhttp_request *req;
|
struct evhttp_request *req;
|
||||||
@ -140,6 +145,7 @@ streaming_end(void)
|
|||||||
static void
|
static void
|
||||||
streaming_meta_cb(evutil_socket_t fd, short event, void *arg)
|
streaming_meta_cb(evutil_socket_t fd, short event, void *arg)
|
||||||
{
|
{
|
||||||
|
struct media_quality mp3_quality = { STREAMING_MP3_SAMPLE_RATE, STREAMING_MP3_BPS, STREAMING_MP3_CHANNELS };
|
||||||
struct media_quality quality;
|
struct media_quality quality;
|
||||||
struct decode_ctx *decode_ctx;
|
struct decode_ctx *decode_ctx;
|
||||||
int ret;
|
int ret;
|
||||||
@ -151,19 +157,17 @@ streaming_meta_cb(evutil_socket_t fd, short event, void *arg)
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
decode_ctx = NULL;
|
decode_ctx = NULL;
|
||||||
if (quality.sample_rate == 44100 && quality.bits_per_sample == 16)
|
if (quality.bits_per_sample == 16)
|
||||||
decode_ctx = transcode_decode_setup_raw(XCODE_PCM16_44100);
|
decode_ctx = transcode_decode_setup_raw(XCODE_PCM16, &quality);
|
||||||
else if (quality.sample_rate == 44100 && quality.bits_per_sample == 24)
|
else if (quality.bits_per_sample == 24)
|
||||||
decode_ctx = transcode_decode_setup_raw(XCODE_PCM24_44100);
|
decode_ctx = transcode_decode_setup_raw(XCODE_PCM24, &quality);
|
||||||
else if (quality.sample_rate == 48000 && quality.bits_per_sample == 16)
|
else if (quality.bits_per_sample == 32)
|
||||||
decode_ctx = transcode_decode_setup_raw(XCODE_PCM16_48000);
|
decode_ctx = transcode_decode_setup_raw(XCODE_PCM32, &quality);
|
||||||
else if (quality.sample_rate == 48000 && quality.bits_per_sample == 24)
|
|
||||||
decode_ctx = transcode_decode_setup_raw(XCODE_PCM24_48000);
|
|
||||||
|
|
||||||
if (!decode_ctx)
|
if (!decode_ctx)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
streaming_encode_ctx = transcode_encode_setup(XCODE_MP3, decode_ctx, NULL, 0, 0);
|
streaming_encode_ctx = transcode_encode_setup(XCODE_MP3, &mp3_quality, decode_ctx, NULL, 0, 0);
|
||||||
transcode_decode_cleanup(&decode_ctx);
|
transcode_decode_cleanup(&decode_ctx);
|
||||||
if (!streaming_encode_ctx)
|
if (!streaming_encode_ctx)
|
||||||
{
|
{
|
||||||
@ -198,7 +202,7 @@ encode_buffer(uint8_t *buffer, size_t size)
|
|||||||
|
|
||||||
samples = BTOS(size, streaming_quality.bits_per_sample, streaming_quality.channels);
|
samples = BTOS(size, streaming_quality.bits_per_sample, streaming_quality.channels);
|
||||||
|
|
||||||
frame = transcode_frame_new(buffer, size, samples, streaming_quality.sample_rate, streaming_quality.bits_per_sample);
|
frame = transcode_frame_new(buffer, size, samples, &streaming_quality);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_STREAMING, "Could not convert raw PCM to frame\n");
|
DPRINTF(E_LOG, L_STREAMING, "Could not convert raw PCM to frame\n");
|
||||||
|
@ -34,7 +34,7 @@ setup(struct input_source *source)
|
|||||||
{
|
{
|
||||||
struct transcode_ctx *ctx;
|
struct transcode_ctx *ctx;
|
||||||
|
|
||||||
ctx = transcode_setup(XCODE_PCM_NATIVE, source->data_kind, source->path, source->len_ms, NULL);
|
ctx = transcode_setup(XCODE_PCM_NATIVE, NULL, source->data_kind, source->path, source->len_ms, NULL);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@
|
|||||||
#define ARRAY_SIZE(x) ((unsigned int)(sizeof(x) / sizeof((x)[0])))
|
#define ARRAY_SIZE(x) ((unsigned int)(sizeof(x) / sizeof((x)[0])))
|
||||||
|
|
||||||
#ifndef MIN
|
#ifndef MIN
|
||||||
# define MIN(a, b) ((a < b) ? a : b)
|
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MAX
|
#ifndef MAX
|
||||||
#define MAX(a, b) ((a > b) ? a : b)
|
# define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -250,24 +250,12 @@ device_stop_cb(struct output_device *device, enum output_device_state status)
|
|||||||
static enum transcode_profile
|
static enum transcode_profile
|
||||||
quality_to_xcode(struct media_quality *quality)
|
quality_to_xcode(struct media_quality *quality)
|
||||||
{
|
{
|
||||||
if (quality->sample_rate == 44100 && quality->bits_per_sample == 16)
|
if (quality->bits_per_sample == 16)
|
||||||
return XCODE_PCM16_44100;
|
return XCODE_PCM16;
|
||||||
if (quality->sample_rate == 44100 && quality->bits_per_sample == 24)
|
if (quality->bits_per_sample == 24)
|
||||||
return XCODE_PCM24_44100;
|
return XCODE_PCM24;
|
||||||
if (quality->sample_rate == 44100 && quality->bits_per_sample == 32)
|
if (quality->bits_per_sample == 32)
|
||||||
return XCODE_PCM32_44100;
|
return XCODE_PCM32;
|
||||||
if (quality->sample_rate == 48000 && quality->bits_per_sample == 16)
|
|
||||||
return XCODE_PCM16_48000;
|
|
||||||
if (quality->sample_rate == 48000 && quality->bits_per_sample == 24)
|
|
||||||
return XCODE_PCM24_48000;
|
|
||||||
if (quality->sample_rate == 48000 && quality->bits_per_sample == 32)
|
|
||||||
return XCODE_PCM32_48000;
|
|
||||||
if (quality->sample_rate == 96000 && quality->bits_per_sample == 16)
|
|
||||||
return XCODE_PCM16_96000;
|
|
||||||
if (quality->sample_rate == 96000 && quality->bits_per_sample == 24)
|
|
||||||
return XCODE_PCM24_96000;
|
|
||||||
if (quality->sample_rate == 96000 && quality->bits_per_sample == 32)
|
|
||||||
return XCODE_PCM32_96000;
|
|
||||||
|
|
||||||
return XCODE_UNKNOWN;
|
return XCODE_UNKNOWN;
|
||||||
}
|
}
|
||||||
@ -288,7 +276,7 @@ encoding_reset(struct media_quality *quality)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
decode_ctx = transcode_decode_setup_raw(profile);
|
decode_ctx = transcode_decode_setup_raw(profile, quality);
|
||||||
if (!decode_ctx)
|
if (!decode_ctx)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_PLAYER, "Could not create subscription decoding context (profile %d)\n", profile);
|
DPRINTF(E_LOG, L_PLAYER, "Could not create subscription decoding context (profile %d)\n", profile);
|
||||||
@ -306,7 +294,7 @@ encoding_reset(struct media_quality *quality)
|
|||||||
|
|
||||||
profile = quality_to_xcode(&subscription->quality);
|
profile = quality_to_xcode(&subscription->quality);
|
||||||
if (profile != XCODE_UNKNOWN)
|
if (profile != XCODE_UNKNOWN)
|
||||||
subscription->encode_ctx = transcode_encode_setup(profile, decode_ctx, NULL, 0, 0);
|
subscription->encode_ctx = transcode_encode_setup(profile, &subscription->quality, decode_ctx, NULL, 0, 0);
|
||||||
else
|
else
|
||||||
DPRINTF(E_LOG, L_PLAYER, "Could not setup resampling to %d/%d/%d for output\n",
|
DPRINTF(E_LOG, L_PLAYER, "Could not setup resampling to %d/%d/%d for output\n",
|
||||||
subscription->quality.sample_rate, subscription->quality.bits_per_sample, subscription->quality.channels);
|
subscription->quality.sample_rate, subscription->quality.bits_per_sample, subscription->quality.channels);
|
||||||
@ -354,7 +342,7 @@ buffer_fill(struct output_buffer *obuf, void *buf, size_t bufsize, struct media_
|
|||||||
if (!output_quality_subscriptions[i].encode_ctx)
|
if (!output_quality_subscriptions[i].encode_ctx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
frame = transcode_frame_new(buf, bufsize, nsamples, quality->sample_rate, quality->bits_per_sample);
|
frame = transcode_frame_new(buf, bufsize, nsamples, quality);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1842,7 +1842,7 @@ payload_encode(struct evbuffer *evbuf, uint8_t *rawbuf, size_t rawbuf_size, int
|
|||||||
transcode_frame *frame;
|
transcode_frame *frame;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
frame = transcode_frame_new(rawbuf, rawbuf_size, nsamples, quality->sample_rate, quality->bits_per_sample);
|
frame = transcode_frame_new(rawbuf, rawbuf_size, nsamples, quality);
|
||||||
if (!frame)
|
if (!frame)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_CAST, "Could not convert raw PCM to frame (bufsize=%zu)\n", rawbuf_size);
|
DPRINTF(E_LOG, L_CAST, "Could not convert raw PCM to frame (bufsize=%zu)\n", rawbuf_size);
|
||||||
@ -2204,14 +2204,14 @@ cast_init(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
decode_ctx = transcode_decode_setup_raw(XCODE_PCM16_48000);
|
decode_ctx = transcode_decode_setup_raw(XCODE_PCM16, &cast_quality_default);
|
||||||
if (!decode_ctx)
|
if (!decode_ctx)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_CAST, "Could not create decoding context\n");
|
DPRINTF(E_LOG, L_CAST, "Could not create decoding context\n");
|
||||||
goto out_tls_deinit;
|
goto out_tls_deinit;
|
||||||
}
|
}
|
||||||
|
|
||||||
cast_encode_ctx = transcode_encode_setup(XCODE_OPUS, decode_ctx, NULL, 0, 0);
|
cast_encode_ctx = transcode_encode_setup(XCODE_OPUS, &cast_quality_default, decode_ctx, NULL, 0, 0);
|
||||||
transcode_decode_cleanup(&decode_ctx);
|
transcode_decode_cleanup(&decode_ctx);
|
||||||
if (!cast_encode_ctx)
|
if (!cast_encode_ctx)
|
||||||
{
|
{
|
||||||
|
175
src/transcode.c
175
src/transcode.c
@ -181,7 +181,7 @@ struct encode_ctx
|
|||||||
/* -------------------------- PROFILE CONFIGURATION ------------------------ */
|
/* -------------------------- PROFILE CONFIGURATION ------------------------ */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
init_settings(struct settings_ctx *settings, enum transcode_profile profile)
|
init_settings(struct settings_ctx *settings, enum transcode_profile profile, struct media_quality *quality)
|
||||||
{
|
{
|
||||||
memset(settings, 0, sizeof(struct settings_ctx));
|
memset(settings, 0, sizeof(struct settings_ctx));
|
||||||
|
|
||||||
@ -189,100 +189,29 @@ init_settings(struct settings_ctx *settings, enum transcode_profile profile)
|
|||||||
{
|
{
|
||||||
case XCODE_PCM_NATIVE: // Sample rate and bit depth determined by source
|
case XCODE_PCM_NATIVE: // Sample rate and bit depth determined by source
|
||||||
settings->encode_audio = 1;
|
settings->encode_audio = 1;
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->icy = 1;
|
settings->icy = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XCODE_PCM16_HEADER:
|
case XCODE_PCM16_HEADER:
|
||||||
settings->wavheader = 1;
|
settings->wavheader = 1;
|
||||||
case XCODE_PCM16_44100:
|
case XCODE_PCM16:
|
||||||
settings->encode_audio = 1;
|
settings->encode_audio = 1;
|
||||||
settings->format = "s16le";
|
settings->format = "s16le";
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S16LE;
|
settings->audio_codec = AV_CODEC_ID_PCM_S16LE;
|
||||||
settings->sample_rate = 44100;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S16;
|
settings->sample_format = AV_SAMPLE_FMT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XCODE_PCM16_48000:
|
case XCODE_PCM24:
|
||||||
settings->encode_audio = 1;
|
|
||||||
settings->format = "s16le";
|
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S16LE;
|
|
||||||
settings->sample_rate = 48000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XCODE_PCM16_96000:
|
|
||||||
settings->encode_audio = 1;
|
|
||||||
settings->format = "s16le";
|
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S16LE;
|
|
||||||
settings->sample_rate = 96000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XCODE_PCM24_44100:
|
|
||||||
settings->encode_audio = 1;
|
settings->encode_audio = 1;
|
||||||
settings->format = "s24le";
|
settings->format = "s24le";
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S24LE;
|
settings->audio_codec = AV_CODEC_ID_PCM_S24LE;
|
||||||
settings->sample_rate = 44100;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S32;
|
settings->sample_format = AV_SAMPLE_FMT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XCODE_PCM24_48000:
|
case XCODE_PCM32:
|
||||||
settings->encode_audio = 1;
|
|
||||||
settings->format = "s24le";
|
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S24LE;
|
|
||||||
settings->sample_rate = 48000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S32;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XCODE_PCM24_96000:
|
|
||||||
settings->encode_audio = 1;
|
|
||||||
settings->format = "s24le";
|
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S24LE;
|
|
||||||
settings->sample_rate = 96000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S32;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XCODE_PCM32_44100:
|
|
||||||
settings->encode_audio = 1;
|
settings->encode_audio = 1;
|
||||||
settings->format = "s32le";
|
settings->format = "s32le";
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S32LE;
|
settings->audio_codec = AV_CODEC_ID_PCM_S32LE;
|
||||||
settings->sample_rate = 44100;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S32;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XCODE_PCM32_48000:
|
|
||||||
settings->encode_audio = 1;
|
|
||||||
settings->format = "s32le";
|
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S32LE;
|
|
||||||
settings->sample_rate = 48000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S32;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XCODE_PCM32_96000:
|
|
||||||
settings->encode_audio = 1;
|
|
||||||
settings->format = "s32le";
|
|
||||||
settings->audio_codec = AV_CODEC_ID_PCM_S32LE;
|
|
||||||
settings->sample_rate = 96000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S32;
|
settings->sample_format = AV_SAMPLE_FMT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -290,9 +219,6 @@ init_settings(struct settings_ctx *settings, enum transcode_profile profile)
|
|||||||
settings->encode_audio = 1;
|
settings->encode_audio = 1;
|
||||||
settings->format = "mp3";
|
settings->format = "mp3";
|
||||||
settings->audio_codec = AV_CODEC_ID_MP3;
|
settings->audio_codec = AV_CODEC_ID_MP3;
|
||||||
settings->sample_rate = 44100;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S16P;
|
settings->sample_format = AV_SAMPLE_FMT_S16P;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -300,9 +226,6 @@ init_settings(struct settings_ctx *settings, enum transcode_profile profile)
|
|||||||
settings->encode_audio = 1;
|
settings->encode_audio = 1;
|
||||||
settings->format = "data"; // Means we get the raw packet from the encoder, no muxing
|
settings->format = "data"; // Means we get the raw packet from the encoder, no muxing
|
||||||
settings->audio_codec = AV_CODEC_ID_OPUS;
|
settings->audio_codec = AV_CODEC_ID_OPUS;
|
||||||
settings->sample_rate = 48000;
|
|
||||||
settings->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
settings->channels = 2;
|
|
||||||
settings->sample_format = AV_SAMPLE_FMT_S16; // Only libopus support
|
settings->sample_format = AV_SAMPLE_FMT_S16; // Only libopus support
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -326,6 +249,23 @@ init_settings(struct settings_ctx *settings, enum transcode_profile profile)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quality && quality->sample_rate)
|
||||||
|
{
|
||||||
|
settings->sample_rate = quality->sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quality && quality->channels)
|
||||||
|
{
|
||||||
|
settings->channels = quality->channels;
|
||||||
|
settings->channel_layout = av_get_default_channel_layout(quality->channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quality && quality->bits_per_sample && (quality->bits_per_sample != 8 * av_get_bytes_per_sample(settings->sample_format)))
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_XCODE, "Bug! Mismatch between profile and media quality\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +292,19 @@ stream_settings_set(struct stream_ctx *s, struct settings_ctx *settings, enum AV
|
|||||||
|
|
||||||
/* -------------------------------- HELPERS -------------------------------- */
|
/* -------------------------------- HELPERS -------------------------------- */
|
||||||
|
|
||||||
|
static enum AVSampleFormat
|
||||||
|
bitdepth2format(int bits_per_sample)
|
||||||
|
{
|
||||||
|
if (bits_per_sample == 16)
|
||||||
|
return AV_SAMPLE_FMT_S16;
|
||||||
|
else if (bits_per_sample == 24)
|
||||||
|
return AV_SAMPLE_FMT_S32;
|
||||||
|
else if (bits_per_sample == 32)
|
||||||
|
return AV_SAMPLE_FMT_S32;
|
||||||
|
else
|
||||||
|
return AV_SAMPLE_FMT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static inline char *
|
static inline char *
|
||||||
err2str(int errnum)
|
err2str(int errnum)
|
||||||
{
|
{
|
||||||
@ -1209,7 +1162,7 @@ close_filters(struct encode_ctx *ctx)
|
|||||||
/* Setup */
|
/* Setup */
|
||||||
|
|
||||||
struct decode_ctx *
|
struct decode_ctx *
|
||||||
transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length)
|
transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length)
|
||||||
{
|
{
|
||||||
struct decode_ctx *ctx;
|
struct decode_ctx *ctx;
|
||||||
|
|
||||||
@ -1220,7 +1173,7 @@ transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind,
|
|||||||
ctx->duration = song_length;
|
ctx->duration = song_length;
|
||||||
ctx->data_kind = data_kind;
|
ctx->data_kind = data_kind;
|
||||||
|
|
||||||
if ((init_settings(&ctx->settings, profile) < 0) || (open_input(ctx, path, evbuf) < 0))
|
if ((init_settings(&ctx->settings, profile, quality) < 0) || (open_input(ctx, path, evbuf) < 0))
|
||||||
goto fail_free;
|
goto fail_free;
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
@ -1233,7 +1186,7 @@ transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct encode_ctx *
|
struct encode_ctx *
|
||||||
transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ctx, off_t *est_size, int width, int height)
|
transcode_encode_setup(enum transcode_profile profile, struct media_quality *quality, struct decode_ctx *src_ctx, off_t *est_size, int width, int height)
|
||||||
{
|
{
|
||||||
struct encode_ctx *ctx;
|
struct encode_ctx *ctx;
|
||||||
int bps;
|
int bps;
|
||||||
@ -1242,17 +1195,19 @@ transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ct
|
|||||||
CHECK_NULL(L_XCODE, ctx->filt_frame = av_frame_alloc());
|
CHECK_NULL(L_XCODE, ctx->filt_frame = av_frame_alloc());
|
||||||
CHECK_NULL(L_XCODE, ctx->encoded_pkt = av_packet_alloc());
|
CHECK_NULL(L_XCODE, ctx->encoded_pkt = av_packet_alloc());
|
||||||
|
|
||||||
if (init_settings(&ctx->settings, profile) < 0)
|
if (init_settings(&ctx->settings, profile, quality) < 0)
|
||||||
goto fail_free;
|
goto fail_free;
|
||||||
|
|
||||||
ctx->settings.width = width;
|
ctx->settings.width = width;
|
||||||
ctx->settings.height = height;
|
ctx->settings.height = height;
|
||||||
|
|
||||||
// Profile does not specify a sample rate -> use same as source
|
// Caller did not specify a sample rate -> use same as source
|
||||||
if (!ctx->settings.sample_rate && ctx->settings.encode_audio)
|
if (!ctx->settings.sample_rate && ctx->settings.encode_audio)
|
||||||
ctx->settings.sample_rate = src_ctx->audio_stream.codec->sample_rate;
|
{
|
||||||
|
ctx->settings.sample_rate = src_ctx->audio_stream.codec->sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
// Profile does not specify a sample format -> use same as source
|
// Caller did not specify a sample format -> use same as source
|
||||||
if (!ctx->settings.sample_format && ctx->settings.encode_audio)
|
if (!ctx->settings.sample_format && ctx->settings.encode_audio)
|
||||||
{
|
{
|
||||||
bps = av_get_bytes_per_sample(src_ctx->audio_stream.codec->sample_fmt);
|
bps = av_get_bytes_per_sample(src_ctx->audio_stream.codec->sample_fmt);
|
||||||
@ -1270,6 +1225,13 @@ transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Caller did not specify channels -> use same as source
|
||||||
|
if (!ctx->settings.channels && ctx->settings.encode_audio)
|
||||||
|
{
|
||||||
|
ctx->settings.channels = src_ctx->audio_stream.codec->channels;
|
||||||
|
ctx->settings.channel_layout = src_ctx->audio_stream.codec->channel_layout;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->settings.wavheader)
|
if (ctx->settings.wavheader)
|
||||||
make_wav_header(ctx, src_ctx, est_size);
|
make_wav_header(ctx, src_ctx, est_size);
|
||||||
|
|
||||||
@ -1297,20 +1259,20 @@ transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ct
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct transcode_ctx *
|
struct transcode_ctx *
|
||||||
transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, uint32_t song_length, off_t *est_size)
|
transcode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, uint32_t song_length, off_t *est_size)
|
||||||
{
|
{
|
||||||
struct transcode_ctx *ctx;
|
struct transcode_ctx *ctx;
|
||||||
|
|
||||||
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct transcode_ctx)));
|
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct transcode_ctx)));
|
||||||
|
|
||||||
ctx->decode_ctx = transcode_decode_setup(profile, data_kind, path, NULL, song_length);
|
ctx->decode_ctx = transcode_decode_setup(profile, quality, data_kind, path, NULL, song_length);
|
||||||
if (!ctx->decode_ctx)
|
if (!ctx->decode_ctx)
|
||||||
{
|
{
|
||||||
free(ctx);
|
free(ctx);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->encode_ctx = transcode_encode_setup(profile, ctx->decode_ctx, est_size, 0, 0);
|
ctx->encode_ctx = transcode_encode_setup(profile, quality, ctx->decode_ctx, est_size, 0, 0);
|
||||||
if (!ctx->encode_ctx)
|
if (!ctx->encode_ctx)
|
||||||
{
|
{
|
||||||
transcode_decode_cleanup(&ctx->decode_ctx);
|
transcode_decode_cleanup(&ctx->decode_ctx);
|
||||||
@ -1322,7 +1284,7 @@ transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct decode_ctx *
|
struct decode_ctx *
|
||||||
transcode_decode_setup_raw(enum transcode_profile profile)
|
transcode_decode_setup_raw(enum transcode_profile profile, struct media_quality *quality)
|
||||||
{
|
{
|
||||||
const AVCodecDescriptor *codec_desc;
|
const AVCodecDescriptor *codec_desc;
|
||||||
struct decode_ctx *ctx;
|
struct decode_ctx *ctx;
|
||||||
@ -1331,7 +1293,7 @@ transcode_decode_setup_raw(enum transcode_profile profile)
|
|||||||
|
|
||||||
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct decode_ctx)));
|
CHECK_NULL(L_XCODE, ctx = calloc(1, sizeof(struct decode_ctx)));
|
||||||
|
|
||||||
if (init_settings(&ctx->settings, profile) < 0)
|
if (init_settings(&ctx->settings, profile, quality) < 0)
|
||||||
{
|
{
|
||||||
goto out_free_ctx;
|
goto out_free_ctx;
|
||||||
}
|
}
|
||||||
@ -1614,7 +1576,7 @@ transcode(struct evbuffer *evbuf, int *icy_timer, struct transcode_ctx *ctx, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
transcode_frame *
|
transcode_frame *
|
||||||
transcode_frame_new(void *data, size_t size, int nsamples, int sample_rate, int bits_per_sample)
|
transcode_frame_new(void *data, size_t size, int nsamples, struct media_quality *quality)
|
||||||
{
|
{
|
||||||
AVFrame *f;
|
AVFrame *f;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1626,30 +1588,19 @@ transcode_frame_new(void *data, size_t size, int nsamples, int sample_rate, int
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bits_per_sample == 16)
|
f->format = bitdepth2format(quality->bits_per_sample);
|
||||||
|
if (f->format == AV_SAMPLE_FMT_NONE)
|
||||||
{
|
{
|
||||||
f->format = AV_SAMPLE_FMT_S16;
|
DPRINTF(E_LOG, L_XCODE, "transcode_frame_new() called with unsupported bps (%d)\n", quality->bits_per_sample);
|
||||||
}
|
|
||||||
else if (bits_per_sample == 24)
|
|
||||||
{
|
|
||||||
f->format = AV_SAMPLE_FMT_S32;
|
|
||||||
}
|
|
||||||
else if (bits_per_sample == 32)
|
|
||||||
{
|
|
||||||
f->format = AV_SAMPLE_FMT_S32;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_XCODE, "transcode_frame_new() called with unsupported bps (%d)\n", bits_per_sample);
|
|
||||||
av_frame_free(&f);
|
av_frame_free(&f);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->sample_rate = sample_rate;
|
f->sample_rate = quality->sample_rate;
|
||||||
f->nb_samples = nsamples;
|
f->nb_samples = nsamples;
|
||||||
f->channel_layout = AV_CH_LAYOUT_STEREO;
|
f->channel_layout = av_get_default_channel_layout(quality->channels);
|
||||||
#ifdef HAVE_FFMPEG
|
#ifdef HAVE_FFMPEG
|
||||||
f->channels = 2;
|
f->channels = quality->channels;
|
||||||
#endif
|
#endif
|
||||||
f->pts = AV_NOPTS_VALUE;
|
f->pts = AV_NOPTS_VALUE;
|
||||||
|
|
||||||
@ -1658,7 +1609,7 @@ transcode_frame_new(void *data, size_t size, int nsamples, int sample_rate, int
|
|||||||
ret = avcodec_fill_audio_frame(f, 2, f->format, data, size, 1);
|
ret = avcodec_fill_audio_frame(f, 2, f->format, data, size, 1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_XCODE, "Error filling frame with rawbuf, size %zu, samples %d (%d/%d/2): %s\n", size, nsamples, sample_rate, bits_per_sample, err2str(ret));
|
DPRINTF(E_LOG, L_XCODE, "Error filling frame with rawbuf, size %zu, samples %d (%d/%d/2): %s\n", size, nsamples, quality->sample_rate, quality->bits_per_sample, err2str(ret));
|
||||||
av_frame_free(&f);
|
av_frame_free(&f);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <event2/buffer.h>
|
#include <event2/buffer.h>
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
enum transcode_profile
|
enum transcode_profile
|
||||||
{
|
{
|
||||||
@ -12,18 +13,12 @@ enum transcode_profile
|
|||||||
XCODE_UNKNOWN = 0,
|
XCODE_UNKNOWN = 0,
|
||||||
// Decodes the best audio stream into PCM16 or PCM24, no resampling (does not add wav header)
|
// Decodes the best audio stream into PCM16 or PCM24, no resampling (does not add wav header)
|
||||||
XCODE_PCM_NATIVE,
|
XCODE_PCM_NATIVE,
|
||||||
// Decodes/resamples the best audio stream into 44100 PCM16 (with wav header)
|
// Decodes/resamples the best audio stream into PCM16 (with wav header)
|
||||||
XCODE_PCM16_HEADER,
|
XCODE_PCM16_HEADER,
|
||||||
// Decodes/resamples the best audio stream (no wav headers)
|
// Decodes/resamples the best audio stream into PCM16/24/32 (no wav headers)
|
||||||
XCODE_PCM16_44100,
|
XCODE_PCM16,
|
||||||
XCODE_PCM16_48000,
|
XCODE_PCM24,
|
||||||
XCODE_PCM16_96000,
|
XCODE_PCM32,
|
||||||
XCODE_PCM24_44100,
|
|
||||||
XCODE_PCM24_48000,
|
|
||||||
XCODE_PCM24_96000,
|
|
||||||
XCODE_PCM32_44100,
|
|
||||||
XCODE_PCM32_48000,
|
|
||||||
XCODE_PCM32_96000,
|
|
||||||
// Transcodes the best audio stream into MP3
|
// Transcodes the best audio stream into MP3
|
||||||
XCODE_MP3,
|
XCODE_MP3,
|
||||||
// Transcodes the best audio stream into OPUS
|
// Transcodes the best audio stream into OPUS
|
||||||
@ -45,16 +40,16 @@ typedef void transcode_frame;
|
|||||||
|
|
||||||
// Setting up
|
// Setting up
|
||||||
struct decode_ctx *
|
struct decode_ctx *
|
||||||
transcode_decode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length);
|
transcode_decode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, struct evbuffer *evbuf, uint32_t song_length);
|
||||||
|
|
||||||
struct encode_ctx *
|
struct encode_ctx *
|
||||||
transcode_encode_setup(enum transcode_profile profile, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);
|
transcode_encode_setup(enum transcode_profile profile, struct media_quality *quality, struct decode_ctx *src_ctx, off_t *est_size, int width, int height);
|
||||||
|
|
||||||
struct transcode_ctx *
|
struct transcode_ctx *
|
||||||
transcode_setup(enum transcode_profile profile, enum data_kind data_kind, const char *path, uint32_t song_length, off_t *est_size);
|
transcode_setup(enum transcode_profile profile, struct media_quality *quality, enum data_kind data_kind, const char *path, uint32_t song_length, off_t *est_size);
|
||||||
|
|
||||||
struct decode_ctx *
|
struct decode_ctx *
|
||||||
transcode_decode_setup_raw(enum transcode_profile profile);
|
transcode_decode_setup_raw(enum transcode_profile profile, struct media_quality *quality);
|
||||||
|
|
||||||
int
|
int
|
||||||
transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype);
|
transcode_needed(const char *user_agent, const char *client_codecs, char *file_codectype);
|
||||||
@ -113,14 +108,11 @@ transcode(struct evbuffer *evbuf, int *icy_timer, struct transcode_ctx *ctx, int
|
|||||||
* @in data Buffer with raw data
|
* @in data Buffer with raw data
|
||||||
* @in size Size of buffer
|
* @in size Size of buffer
|
||||||
* @in nsamples Number of samples in the buffer
|
* @in nsamples Number of samples in the buffer
|
||||||
* @in sample_rate
|
* @in quality Sample rate, bits per sample and channels
|
||||||
* Sample rate
|
|
||||||
* @in bits_per_sample
|
|
||||||
* BPS must be either 16 or 24
|
|
||||||
* @return Opaque pointer to frame if OK, otherwise NULL
|
* @return Opaque pointer to frame if OK, otherwise NULL
|
||||||
*/
|
*/
|
||||||
transcode_frame *
|
transcode_frame *
|
||||||
transcode_frame_new(void *data, size_t size, int nsamples, int sample_rate, int bits_per_sample);
|
transcode_frame_new(void *data, size_t size, int nsamples, struct media_quality *quality);
|
||||||
void
|
void
|
||||||
transcode_frame_free(transcode_frame *frame);
|
transcode_frame_free(transcode_frame *frame);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user