2014-05-22 03:07:17 -07:00
|
|
|
#include <obs-module.h>
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2019-06-22 22:13:45 -07:00
|
|
|
#ifndef _DEBUG
|
|
|
|
#define _DEBUG
|
|
|
|
#endif
|
|
|
|
#undef DEBUG
|
2014-05-22 03:07:17 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <fdk-aac/aacenc_lib.h>
|
|
|
|
|
|
|
|
static const char *libfdk_get_error(AACENC_ERROR err)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
switch (err) {
|
2014-05-22 03:07:17 -07:00
|
|
|
case AACENC_OK:
|
|
|
|
return "No error";
|
|
|
|
case AACENC_INVALID_HANDLE:
|
|
|
|
return "Invalid handle";
|
|
|
|
case AACENC_MEMORY_ERROR:
|
|
|
|
return "Memory allocation error";
|
|
|
|
case AACENC_UNSUPPORTED_PARAMETER:
|
|
|
|
return "Unsupported parameter";
|
|
|
|
case AACENC_INVALID_CONFIG:
|
|
|
|
return "Invalid config";
|
|
|
|
case AACENC_INIT_ERROR:
|
|
|
|
return "Initialization error";
|
|
|
|
case AACENC_INIT_AAC_ERROR:
|
|
|
|
return "AAC library initialization error";
|
|
|
|
case AACENC_INIT_SBR_ERROR:
|
|
|
|
return "SBR library initialization error";
|
|
|
|
case AACENC_INIT_TP_ERROR:
|
|
|
|
return "Transport library initialization error";
|
|
|
|
case AACENC_INIT_META_ERROR:
|
|
|
|
return "Metadata library initialization error";
|
|
|
|
case AACENC_ENCODE_ERROR:
|
|
|
|
return "Encoding error";
|
|
|
|
case AACENC_ENCODE_EOF:
|
|
|
|
return "End of file";
|
|
|
|
default:
|
|
|
|
return "Unknown error";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct libfdk_encoder {
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_encoder_t *encoder;
|
2014-05-22 03:07:17 -07:00
|
|
|
|
|
|
|
int channels, sample_rate;
|
|
|
|
|
|
|
|
HANDLE_AACENCODER fdkhandle;
|
|
|
|
AACENC_InfoStruct info;
|
|
|
|
|
|
|
|
uint64_t total_samples;
|
|
|
|
|
|
|
|
int frame_size_bytes;
|
|
|
|
|
|
|
|
uint8_t *packet_buffer;
|
|
|
|
int packet_buffer_size;
|
|
|
|
} libfdk_encoder_t;
|
|
|
|
|
2015-09-16 01:30:51 -07:00
|
|
|
static const char *libfdk_getname(void *unused)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
2015-09-16 01:30:51 -07:00
|
|
|
UNUSED_PARAMETER(unused);
|
2014-07-09 22:12:57 -07:00
|
|
|
return obs_module_text("LibFDK");
|
2014-05-22 03:07:17 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 08:36:13 -07:00
|
|
|
static obs_properties_t *libfdk_properties(void *unused)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
2014-09-29 08:36:13 -07:00
|
|
|
UNUSED_PARAMETER(unused);
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_properties_t *props = obs_properties_create();
|
2014-05-22 03:07:17 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_properties_add_int(props, "bitrate", obs_module_text("Bitrate"), 32,
|
|
|
|
1024, 32);
|
2014-07-09 22:12:57 -07:00
|
|
|
obs_properties_add_bool(props, "afterburner",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_module_text("Afterburner"));
|
2014-05-22 03:07:17 -07:00
|
|
|
|
|
|
|
return props;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void libfdk_defaults(obs_data_t *settings)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
|
|
|
obs_data_set_default_int(settings, "bitrate", 128);
|
|
|
|
obs_data_set_default_bool(settings, "afterburner", true);
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
#define CHECK_LIBFDK(r) \
|
|
|
|
if ((err = (r)) != AACENC_OK) { \
|
2014-05-22 03:07:17 -07:00
|
|
|
blog(LOG_ERROR, #r " failed: %s", libfdk_get_error(err)); \
|
2019-06-22 22:13:45 -07:00
|
|
|
goto fail; \
|
2014-05-22 03:07:17 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void *libfdk_create(obs_data_t *settings, obs_encoder_t *encoder)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
|
|
|
bool hasFdkHandle = false;
|
|
|
|
libfdk_encoder_t *enc = 0;
|
2014-08-05 11:09:29 -07:00
|
|
|
int bitrate = (int)obs_data_get_int(settings, "bitrate") * 1000;
|
|
|
|
int afterburner = obs_data_get_bool(settings, "afterburner") ? 1 : 0;
|
2014-09-25 17:44:05 -07:00
|
|
|
audio_t *audio = obs_encoder_audio(encoder);
|
2014-05-22 03:07:17 -07:00
|
|
|
int mode = 0;
|
|
|
|
AACENC_ERROR err;
|
|
|
|
|
|
|
|
if (!bitrate) {
|
|
|
|
blog(LOG_ERROR, "Invalid bitrate");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
enc = bzalloc(sizeof(libfdk_encoder_t));
|
|
|
|
enc->encoder = encoder;
|
|
|
|
|
2014-08-05 15:07:54 -07:00
|
|
|
enc->channels = (int)audio_output_get_channels(audio);
|
|
|
|
enc->sample_rate = audio_output_get_sample_rate(audio);
|
2014-05-22 03:07:17 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
switch (enc->channels) {
|
2014-05-22 03:07:17 -07:00
|
|
|
case 1:
|
|
|
|
mode = MODE_1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
mode = MODE_2;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
mode = MODE_1_2;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
mode = MODE_1_2_1;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
mode = MODE_1_2_2;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
mode = MODE_1_2_2_1;
|
|
|
|
break;
|
libobs: Add surround sound audio support
(This commit also modifies the following modules: UI,
deps/media-playback, coreaudio-encoder, decklink, linux-alsa,
linux-pulseaudio, mac-capture, obs-ffmpeg, obs-filters, obs-libfdk,
obs-outputs, win-dshow, and win-wasapi)
Adds surround sound audio support to the core, core plugins, and user
interface.
Compatible streaming services: Twitch, FB 360 live
Compatible protocols: rtmp / mpeg-ts tcp udp
Compatible file formats: mkv mp4 ts (others untested)
Compatible codecs: ffmpeg aac, fdk_aac, CoreAudio aac,
opus, vorbis, pcm (others untested).
Tested streaming servers: wowza, nginx
HLS, mpeg-dash : surround passthrough
Html5 players tested with live surround:
videojs, mediaelement, viblast (hls+dash), hls.js
Decklink: on win32, swap channels order for 5.1 7.1
(due to different channel mapping on wav, mpeg, ffmpeg)
Audio filters: surround working.
Monitoring: surround working (win macOs linux (pulse-audio)).
VST: stereo plugins keep in general only the first two channels.
surround plugins should work (e.g. mcfx does).
OS: win, macOs, linux (alsa, pulse-audio).
Misc: larger audio bitrates unlocked to accommodate more channels
NB: mf-aac only supports mono and stereo + 5.1 on win 10
(not implemented due to lack of usefulness)
Closes jp9000/obs-studio#968
2017-05-26 17:15:54 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
/* lib_fdk-aac > 1.3 required for 7.1 surround;
|
libobs: Add surround sound audio support
(This commit also modifies the following modules: UI,
deps/media-playback, coreaudio-encoder, decklink, linux-alsa,
linux-pulseaudio, mac-capture, obs-ffmpeg, obs-filters, obs-libfdk,
obs-outputs, win-dshow, and win-wasapi)
Adds surround sound audio support to the core, core plugins, and user
interface.
Compatible streaming services: Twitch, FB 360 live
Compatible protocols: rtmp / mpeg-ts tcp udp
Compatible file formats: mkv mp4 ts (others untested)
Compatible codecs: ffmpeg aac, fdk_aac, CoreAudio aac,
opus, vorbis, pcm (others untested).
Tested streaming servers: wowza, nginx
HLS, mpeg-dash : surround passthrough
Html5 players tested with live surround:
videojs, mediaelement, viblast (hls+dash), hls.js
Decklink: on win32, swap channels order for 5.1 7.1
(due to different channel mapping on wav, mpeg, ffmpeg)
Audio filters: surround working.
Monitoring: surround working (win macOs linux (pulse-audio)).
VST: stereo plugins keep in general only the first two channels.
surround plugins should work (e.g. mcfx does).
OS: win, macOs, linux (alsa, pulse-audio).
Misc: larger audio bitrates unlocked to accommodate more channels
NB: mf-aac only supports mono and stereo + 5.1 on win 10
(not implemented due to lack of usefulness)
Closes jp9000/obs-studio#968
2017-05-26 17:15:54 -07:00
|
|
|
* uncomment if available on linux build
|
|
|
|
*/
|
|
|
|
#ifndef __linux__
|
|
|
|
case 8:
|
|
|
|
mode = MODE_7_1_REAR_SURROUND;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2014-05-22 03:07:17 -07:00
|
|
|
default:
|
|
|
|
blog(LOG_ERROR, "Invalid channel count");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK_LIBFDK(aacEncOpen(&enc->fdkhandle, 0, enc->channels));
|
|
|
|
hasFdkHandle = true;
|
|
|
|
|
|
|
|
CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AOT,
|
2019-06-22 22:13:45 -07:00
|
|
|
2)); // MPEG-4 AAC-LC
|
2014-05-22 03:07:17 -07:00
|
|
|
CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_SAMPLERATE,
|
2019-06-22 22:13:45 -07:00
|
|
|
enc->sample_rate));
|
|
|
|
CHECK_LIBFDK(
|
|
|
|
aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELMODE, mode));
|
|
|
|
CHECK_LIBFDK(
|
|
|
|
aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELORDER, 1));
|
|
|
|
CHECK_LIBFDK(
|
|
|
|
aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0));
|
|
|
|
CHECK_LIBFDK(
|
|
|
|
aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate));
|
2014-05-22 03:07:17 -07:00
|
|
|
CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0));
|
|
|
|
CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER,
|
2019-06-22 22:13:45 -07:00
|
|
|
afterburner));
|
2014-05-22 03:07:17 -07:00
|
|
|
|
|
|
|
CHECK_LIBFDK(aacEncEncode(enc->fdkhandle, NULL, NULL, NULL, NULL));
|
|
|
|
|
|
|
|
CHECK_LIBFDK(aacEncInfo(enc->fdkhandle, &enc->info));
|
|
|
|
|
|
|
|
enc->frame_size_bytes = enc->info.frameLength * 2 * enc->channels;
|
|
|
|
|
|
|
|
enc->packet_buffer_size = enc->channels * 768;
|
2019-06-22 22:13:45 -07:00
|
|
|
if (enc->packet_buffer_size < 8192)
|
2014-05-22 03:07:17 -07:00
|
|
|
enc->packet_buffer_size = 8192;
|
|
|
|
|
|
|
|
enc->packet_buffer = bmalloc(enc->packet_buffer_size);
|
|
|
|
|
|
|
|
blog(LOG_INFO, "libfdk_aac encoder created");
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_INFO, "libfdk_aac bitrate: %d, channels: %d", bitrate / 1000,
|
|
|
|
enc->channels);
|
2014-07-13 03:12:54 -07:00
|
|
|
|
2014-05-22 03:07:17 -07:00
|
|
|
return enc;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if (hasFdkHandle)
|
2014-05-22 03:07:17 -07:00
|
|
|
aacEncClose(&enc->fdkhandle);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if (enc->packet_buffer)
|
2014-05-22 03:07:17 -07:00
|
|
|
bfree(enc->packet_buffer);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if (enc)
|
2014-05-22 03:07:17 -07:00
|
|
|
bfree(enc);
|
|
|
|
|
|
|
|
blog(LOG_WARNING, "libfdk_aac encoder creation failed");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void libfdk_destroy(void *data)
|
|
|
|
{
|
|
|
|
libfdk_encoder_t *enc = data;
|
|
|
|
|
|
|
|
aacEncClose(&enc->fdkhandle);
|
|
|
|
|
|
|
|
bfree(enc->packet_buffer);
|
|
|
|
bfree(enc);
|
|
|
|
|
|
|
|
blog(LOG_INFO, "libfdk_aac encoder destroyed");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool libfdk_encode(void *data, struct encoder_frame *frame,
|
2019-06-22 22:13:45 -07:00
|
|
|
struct encoder_packet *packet, bool *received_packet)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
|
|
|
libfdk_encoder_t *enc = data;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
AACENC_BufDesc in_buf = {0};
|
|
|
|
AACENC_BufDesc out_buf = {0};
|
|
|
|
AACENC_InArgs in_args = {0};
|
|
|
|
AACENC_OutArgs out_args = {0};
|
2014-05-22 03:07:17 -07:00
|
|
|
int in_identifier = IN_AUDIO_DATA;
|
|
|
|
int in_size, in_elem_size;
|
|
|
|
int out_identifier = OUT_BITSTREAM_DATA;
|
|
|
|
int out_size, out_elem_size;
|
|
|
|
void *in_ptr;
|
|
|
|
void *out_ptr;
|
|
|
|
AACENC_ERROR err;
|
2018-11-29 12:10:45 -08:00
|
|
|
int64_t encoderDelay;
|
2014-05-22 03:07:17 -07:00
|
|
|
|
|
|
|
in_ptr = frame->data[0];
|
|
|
|
in_size = enc->frame_size_bytes;
|
|
|
|
in_elem_size = 2;
|
|
|
|
|
|
|
|
in_args.numInSamples = enc->info.frameLength * enc->channels;
|
|
|
|
in_buf.numBufs = 1;
|
|
|
|
in_buf.bufs = &in_ptr;
|
|
|
|
in_buf.bufferIdentifiers = &in_identifier;
|
|
|
|
in_buf.bufSizes = &in_size;
|
|
|
|
in_buf.bufElSizes = &in_elem_size;
|
|
|
|
|
|
|
|
out_ptr = enc->packet_buffer;
|
|
|
|
out_size = enc->packet_buffer_size;
|
|
|
|
out_elem_size = 1;
|
|
|
|
|
|
|
|
out_buf.numBufs = 1;
|
|
|
|
out_buf.bufs = &out_ptr;
|
|
|
|
out_buf.bufferIdentifiers = &out_identifier;
|
|
|
|
out_buf.bufSizes = &out_size;
|
|
|
|
out_buf.bufElSizes = &out_elem_size;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if ((err = aacEncEncode(enc->fdkhandle, &in_buf, &out_buf, &in_args,
|
|
|
|
&out_args)) != AACENC_OK) {
|
|
|
|
blog(LOG_ERROR, "Failed to encode frame: %s",
|
|
|
|
libfdk_get_error(err));
|
2014-05-22 03:07:17 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
enc->total_samples += enc->info.frameLength;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
if (out_args.numOutBytes == 0) {
|
2014-05-22 03:07:17 -07:00
|
|
|
*received_packet = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
*received_packet = true;
|
2018-11-29 12:10:45 -08:00
|
|
|
#if (AACENCODER_LIB_VL0 >= 4)
|
2019-06-22 22:13:45 -07:00
|
|
|
encoderDelay = enc->info.nDelay;
|
2018-11-29 12:10:45 -08:00
|
|
|
#else
|
2019-06-22 22:13:45 -07:00
|
|
|
encoderDelay = enc->info.encoderDelay;
|
2018-11-29 12:10:45 -08:00
|
|
|
#endif
|
2019-06-22 22:13:45 -07:00
|
|
|
packet->pts = enc->total_samples - encoderDelay;
|
|
|
|
packet->dts = enc->total_samples - encoderDelay;
|
2014-05-22 03:07:17 -07:00
|
|
|
packet->data = enc->packet_buffer;
|
|
|
|
packet->size = out_args.numOutBytes;
|
|
|
|
packet->type = OBS_ENCODER_AUDIO;
|
|
|
|
packet->timebase_num = 1;
|
|
|
|
packet->timebase_den = enc->sample_rate;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool libfdk_extra_data(void *data, uint8_t **extra_data, size_t *size)
|
|
|
|
{
|
|
|
|
libfdk_encoder_t *enc = data;
|
|
|
|
|
|
|
|
*size = enc->info.confSize;
|
|
|
|
*extra_data = enc->info.confBuf;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-04-17 01:34:10 -07:00
|
|
|
static void libfdk_audio_info(void *data, struct audio_convert_info *info)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(data);
|
|
|
|
info->format = AUDIO_FORMAT_16BIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t libfdk_frame_size(void *data)
|
|
|
|
{
|
|
|
|
libfdk_encoder_t *enc = data;
|
|
|
|
|
|
|
|
return enc->info.frameLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct obs_encoder_info obs_libfdk_encoder = {
|
2019-06-22 22:13:45 -07:00
|
|
|
.id = "libfdk_aac",
|
|
|
|
.type = OBS_ENCODER_AUDIO,
|
|
|
|
.codec = "AAC",
|
|
|
|
.get_name = libfdk_getname,
|
|
|
|
.create = libfdk_create,
|
|
|
|
.destroy = libfdk_destroy,
|
|
|
|
.encode = libfdk_encode,
|
2014-08-04 21:27:52 -07:00
|
|
|
.get_frame_size = libfdk_frame_size,
|
2019-06-22 22:13:45 -07:00
|
|
|
.get_defaults = libfdk_defaults,
|
2014-08-04 21:27:52 -07:00
|
|
|
.get_properties = libfdk_properties,
|
|
|
|
.get_extra_data = libfdk_extra_data,
|
2019-06-22 22:13:45 -07:00
|
|
|
.get_audio_info = libfdk_audio_info,
|
2014-05-22 03:07:17 -07:00
|
|
|
};
|
|
|
|
|
2014-07-27 12:42:43 -07:00
|
|
|
bool obs_module_load(void)
|
2014-05-22 03:07:17 -07:00
|
|
|
{
|
|
|
|
obs_register_encoder(&obs_libfdk_encoder);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
OBS_DECLARE_MODULE()
|
2014-07-09 22:12:57 -07:00
|
|
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-libfdk", "en-US")
|