Commit 76c7f1df authored by Sylvain Berfini's avatar Sylvain Berfini 🎩
Browse files

Merge branch 'release/4.5' into master

parents d9a72342 8f82fe20
......@@ -6,8 +6,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [4.5.0] - 2021-03-29
### Added
- Audio route API to choose which device to use for capture & playback on Android & iOS.
- New camera capture backend based on Windows MediaFoundation
- MSMediaPlayer enhancements.
- VideoConference API and engine prototype (active speaker switching only)
### Changed
- audio flow control algorithm improved, silent portions are dropped first.
- MKVPlayer supports seek in files without cue table.
- 'packetlosspercentage' is now configurable in opus encoder.
### Fixed
- misfunction in DTLS handshake over TURN
- fix arythmetic issue in clock skew computation, causing bad audio when the sound device outputs
audio fragments not multiple of 10ms.
- iOS AudioUnit configured with bad sample rate issue.
- wrong selection of ICE TURN candidates when the TURN server is behind a firewall
- unsent TURN SenderIndication packets on iOS
- fix video freeze in VP8, due to lack of retransmission of SLIs.
All these fix were backported to 4.4 branch.
## [4.4.0] - 2020-06-16
......
......@@ -21,7 +21,7 @@
############################################################################
cmake_minimum_required(VERSION 3.1)
project(mediastreamer2 VERSION 4.5.0 LANGUAGES C CXX)
project(mediastreamer2 VERSION 5.0.0 LANGUAGES C CXX)
if(WIN32)
if( NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
......
......@@ -20,9 +20,18 @@
#ifndef flowcontrol_h
#define flowcontrol_h
typedef enum _MSAudioFlowControlStrategy{
MSAudioFlowControlBasic, /**< Immediately drop requested number of samples */
MSAudioFlowControlSoft /**< Elimate silent frames first, use zero-crossing sample deletion */
}MSAudioFlowControlStrategy;
typedef struct _MSAudioFlowControlConfig{
MSAudioFlowControlStrategy strategy;
float silent_threshold; /**< threshold under which a frame is considered as silent (linear), used by "soft" strategy */
}MSAudioFlowControlConfig;
typedef struct _MSAudioFlowController {
MSAudioFlowControlConfig config;
uint32_t target_samples;
uint32_t total_samples;
uint32_t current_pos;
......@@ -36,6 +45,8 @@ extern "C"{
MS2_PUBLIC void ms_audio_flow_controller_init(MSAudioFlowController *ctl);
MS2_PUBLIC void ms_audio_flow_controller_set_config(MSAudioFlowController *ctl, const MSAudioFlowControlConfig *config);
MS2_PUBLIC void ms_audio_flow_controller_set_target(MSAudioFlowController *ctl, uint32_t samples_to_drop, uint32_t total_samples);
MS2_PUBLIC mblk_t *ms_audio_flow_controller_process(MSAudioFlowController *ctl, mblk_t *m);
......@@ -50,10 +61,22 @@ typedef struct _MSAudioFlowControlDropEvent{
} MSAudioFlowControlDropEvent;
/**
* Event sent by the filter each time some samples need to be dropped.
* Event than can be emitted by any filter each time some samples need to be dropped.
* @FIXME It is badly named. It should belong to MSFilter base class or to a specific audio interface.
**/
#define MS_AUDIO_FLOW_CONTROL_DROP_EVENT MS_FILTER_EVENT(MS_AUDIO_FLOW_CONTROL_ID, 0, MSAudioFlowControlDropEvent)
/**
* Set configuration of the flow controller. It can be changed at run-time.
*/
#define MS_AUDIO_FLOW_CONTROL_SET_CONFIG MS_FILTER_METHOD(MS_AUDIO_FLOW_CONTROL_ID, 0, MSAudioFlowControlConfig)
/**
* Request to drop samples.
*/
#define MS_AUDIO_FLOW_CONTROL_DROP MS_FILTER_METHOD(MS_AUDIO_FLOW_CONTROL_ID, 1, MSAudioFlowControlDropEvent)
#ifdef __cplusplus
}
......
......@@ -22,7 +22,23 @@
#include <mediastreamer2/msfilter.h>
/*#if defined(__APPLE__)
#include <sys/syslimits.h>
#define MAX_PATH PATH_MAX
#elif defined(__unix__)
#include <limits.h>
#define MAX_PATH PATH_MAX
#else
#include <limits.h>
#endif*/
#define MAX_PATH_TEMP 255
typedef struct _MSJpegWriteEventData {
char filePath[MAX_PATH_TEMP];
} MSJpegWriteEventData;
#define MS_JPEG_WRITER_TAKE_SNAPSHOT MS_FILTER_METHOD(MS_JPEG_WRITER_ID, 0, const char)
#define MS_JPEG_WRITER_SNAPSHOT_TAKEN MS_FILTER_EVENT(MS_JPEG_WRITER_ID, 0, const char)
#define MS_JPEG_WRITER_SNAPSHOT_TAKEN MS_FILTER_EVENT(MS_JPEG_WRITER_ID, 0, MSJpegWriteEventData)
#endif
......@@ -22,7 +22,11 @@
#include <mediastreamer2/msfilter.h>
#define MS_QRCODE_READER_QRCODE_FOUND MS_FILTER_EVENT(MS_QRCODE_READER_ID, 0, const char*)
typedef struct _MSQrCodeReaderEventData {
char data[255];
} MSQrCodeReaderEventData;
#define MS_QRCODE_READER_QRCODE_FOUND MS_FILTER_EVENT(MS_QRCODE_READER_ID, 0, MSQrCodeReaderEventData)
#define MS_QRCODE_READER_RESET_SEARCH MS_FILTER_METHOD_NO_ARG(MS_QRCODE_READER_ID, 0)
#define MS_QRCODE_READET_SET_DECODER_RECT MS_FILTER_METHOD(MS_QRCODE_READER_ID, 1, MSRect)
......
......@@ -102,6 +102,7 @@ typedef void (*MSSndCardUnloadFunc)(MSSndCardManager *obj);
typedef void (*MSSndCardAudioSessionFunc)(struct _MSSndCard *obj, bool_t actived);
typedef void (*MSSndCardCallKitFunc)(struct _MSSndCard *obj, bool_t enabled);
typedef void (*MSSndCardAudioRouteFunc)(struct _MSSndCard *obj);
typedef void (*MSSndCardConfigureFunc)(struct _MSSndCard *obj);
struct _MSSndCardDesc{
......@@ -122,6 +123,7 @@ struct _MSSndCardDesc{
MSSndCardAudioSessionFunc audio_session_activated;
MSSndCardCallKitFunc callkit_enabled;
MSSndCardAudioRouteFunc audio_route_changed;
MSSndCardConfigureFunc configure;
};
/**
......@@ -721,6 +723,12 @@ MS2_PUBLIC void ms_snd_card_notify_audio_route_changed(MSSndCard *obj);
*/
MS2_PUBLIC void ms_snd_card_app_notifies_activation(MSSndCard *obj, bool_t yesno);
/**
* Used to configure audio session with default settings. Callkit usage.
* @param obj A sound card object.
*/
MS2_PUBLIC void ms_snd_card_configure_audio_session(MSSndCard *obj);
/**
* Sets the stream type for this soundcard, default is VOICE
**/
......
......@@ -114,9 +114,10 @@ struct _MSTickerSynchronizer
{
uint64_t offset; /**<the default offset of ticker*/
double av_skew; /**< mean skew */
unsigned int external_time_count; /**< number of times ms_ticker_synchronizer_set_external_time() is called */
uint64_t origin_nsamples; /**< Sample count when ms_ticker_synchronizer_update() is called for the first time */
uint64_t current_nsamples; /**< The last number of samples read notified with ms_ticker_synchronizer_update*/
uint64_t current_ms; /**< The last time notified with ms_ticker_synchronizer_update*/
uint64_t last_log_time; /**< The last time the skew log was issued*/
unsigned int sample_rate;
};
/**
......@@ -290,6 +291,14 @@ MS2_PUBLIC double ms_ticker_synchronizer_update(MSTickerSynchronizer *ts, uint64
*/
MS2_PUBLIC uint64_t ms_ticker_synchronizer_get_corrected_time(MSTickerSynchronizer* ts);
/**
* Ask the ticker synchronizer to resynchronize. Use this when you are aware of an overrun/underrun and you think that the
* next update will report an errouneous sample count.
* @param ts A #MSTickerSynchronizer object.
* @return A corrected current time.
*/
MS2_PUBLIC void ms_ticker_synchronizer_resync(MSTickerSynchronizer* ts);
/**
* Destroy a ticker synchronizer.
* @param ts A #MSTickerSynchronizer object.
......
......@@ -170,6 +170,7 @@ typedef struct {
bool_t has_channel_number;
bool_t has_requested_transport;
bool_t has_requested_address_family;
bool_t own_data;
} MSStunMessage;
typedef enum {
......
......@@ -124,7 +124,9 @@ public class MediastreamerAndroidContext {
AudioManager audiomanager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE);
final AudioDeviceInfo[] devices = audiomanager.getDevices(flag);
for (AudioDeviceInfo device : devices) {
Log.i("[getAudioDevices] DEBUG Found device: name " + device.getProductName() + " ID " + device.getId() + " type " + device.getType() + " isSource " + device.isSource() + " isSink " + device.isSink() + ".");
int type = device.getType();
String stringType = getHumanReadableAudioDeviceType(type);
Log.i("[Audio Manager] Found device: name [" + device.getProductName() + "], ID [" + device.getId() + "], type [" + stringType + " (" + type + ")], isSource [" + device.isSource() + "], isSink [" + device.isSink() + "]");
}
return devices;
}
......@@ -207,4 +209,59 @@ public class MediastreamerAndroidContext {
//try { Thread.sleep(100); } catch (Exception e) { }
audioManager.adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE, AudioManager.STREAM_VOICE_CALL, 0);
}
private static String getHumanReadableAudioDeviceType(int type) {
if (type == 19/*AudioDeviceInfo.TYPE_AUX_LINE*/) {
return "Auxiliary line";
} else if (type == 8/*AudioDeviceInfo.TYPE_BLUETOOTH_A2DP*/) {
return "Bluetooth A2DP";
} else if (type == 7/*AudioDeviceInfo.TYPE_BLUETOOTH_SCO*/) {
return "Bluetooth SCO";
} else if (type == 1/*AudioDeviceInfo.TYPE_BUILTIN_EARPIECE*/) {
return "Built-in earpiece";
} else if (type == 15/*AudioDeviceInfo.TYPE_BUILTIN_MIC*/) {
return "Built-in microphone";
} else if (type == 2/*AudioDeviceInfo.TYPE_BUILTIN_SPEAKER*/) {
return "Built-in speaker";
} else if (type == 24/*AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE*/) {
return "Built-in speaker (safe)";
} else if (type == 21/*AudioDeviceInfo.TYPE_BUS*/) {
return "Type agnostic bus";
} else if (type == 13/*AudioDeviceInfo.TYPE_DOCK*/) {
return "Dock";
} else if (type == 14/*AudioDeviceInfo.TYPE_FM*/) {
return "FM";
} else if (type == 16/*AudioDeviceInfo.TYPE_FM_TUNER*/) {
return "FM tuner";
} else if (type == 9/*AudioDeviceInfo.TYPE_HDMI*/) {
return "HDMI";
} else if (type == 10/*AudioDeviceInfo.TYPE_HDMI_ARC*/) {
return "HDMI Audio Return Channel";
} else if (type == 23/*AudioDeviceInfo.TYPE_HEARING_AID*/) {
return "Hearing aid";
} else if (type == 20/*AudioDeviceInfo.TYPE_IP*/) {
return "IP";
} else if (type == 5/*AudioDeviceInfo.TYPE_LINE_ANALOG*/) {
return "Analog";
} else if (type == 6/*AudioDeviceInfo.TYPE_LINE_DIGITAL*/) {
return "Digital";
} else if (type == 18/*AudioDeviceInfo.TYPE_TELEPHONY*/) {
return "Telephony";
} else if (type == 17/*AudioDeviceInfo.TYPE_TV_TUNER*/) {
return "TV tuner";
} else if (type == 0/*AudioDeviceInfo.TYPE_UNKNOWN*/) {
return "Unknown";
} else if (type == 12/*AudioDeviceInfo.TYPE_USB_ACCESSORY*/) {
return "USB accessory";
} else if (type == 11/*AudioDeviceInfo.TYPE_USB_DEVICE*/) {
return "USB";
} else if (type == 22/*AudioDeviceInfo.TYPE_USB_HEADSET*/) {
return "USB headset";
} else if (type == 4/*AudioDeviceInfo.TYPE_WIRED_HEADPHONES*/) {
return "Headphones";
} else if (type == 3 /*AudioDeviceInfo.TYPE_WIRED_HEADSET*/) {
return "Headset";
}
return "UNEXPECTED";
}
}
......@@ -452,8 +452,9 @@ add_custom_target(ms2-voipdescs-header
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/voipdescs.h"
)
if(APPLE OR ANDROID OR OPENGL_FOUND OR GLX_FOUND OR QTANGLE_FOUND OR WIN32)
find_package(PythonInterp REQUIRED)
find_package(PythonInterp 3 REQUIRED)
find_program(SED_PROGRAM sed CMAKE_FIND_ROOT_PATH_BOTH)
if(NOT SED_PROGRAM)
message(FATAL_ERROR "You need the sed program to build mediastreamer2.")
......
......@@ -466,22 +466,6 @@ static SLresult opensles_recorder_init(OpenSLESInputContext *ictx) {
static void opensles_recorder_callback(SLAndroidSimpleBufferQueueItf bq, void *context) {
SLresult result;
OpenSLESInputContext *ictx = (OpenSLESInputContext *)context;
if (ictx->mTickerSynchronizer == NULL) {
MSFilter *obj = ictx->mFilter;
/*
* ABSOLUTE HORRIBLE HACK. We temporarily disable logs to prevent ms_ticker_set_synchronizer() to output a debug log.
* This is horrible because this also suspends logs for all concurrent threads during these two lines of code.
* Possible way to do better:
* 1) understand why AudioRecord thread doesn't detach.
* 2) disable logs just for this thread (using a TLS)
*/
int loglevel=bctbx_get_log_level_mask(BCTBX_LOG_DOMAIN);
bctbx_set_log_level_mask(BCTBX_LOG_DOMAIN, BCTBX_LOG_ERROR|BCTBX_LOG_FATAL);
ictx->mTickerSynchronizer = ms_ticker_synchronizer_new();
ms_ticker_set_synchronizer(obj->ticker, ictx->mTickerSynchronizer);
bctbx_set_log_level_mask(BCTBX_LOG_DOMAIN, loglevel);
}
ictx->read_samples += ictx->inBufSize / sizeof(int16_t);
mblk_t *m = allocb(ictx->inBufSize, 0);
......@@ -560,6 +544,12 @@ static void android_snd_read_preprocess(MSFilter *obj) {
ictx->inBufSize = DeviceFavoriteBufferSize * sizeof(int16_t) * ictx->opensles_context->nchannels;
ictx->recBuffer[0] = (uint8_t *) calloc(ictx->inBufSize, sizeof(uint8_t));
ictx->recBuffer[1] = (uint8_t *) calloc(ictx->inBufSize, sizeof(uint8_t));
if (ictx->mTickerSynchronizer == NULL) {
MSFilter *obj = ictx->mFilter;
ictx->mTickerSynchronizer = ms_ticker_synchronizer_new();
ms_ticker_set_synchronizer(obj->ticker, ictx->mTickerSynchronizer);
}
if (SL_RESULT_SUCCESS != opensles_recorder_init(ictx)) {
ms_error("[OpenSLES] Problem when initialization of opensles recorder");
......@@ -573,7 +563,6 @@ static void android_snd_read_preprocess(MSFilter *obj) {
if (ictx->opensles_context->builtin_aec) {
//android_snd_read_activate_hardware_aec(obj);
}
}
static void android_snd_read_process(MSFilter *obj) {
......@@ -634,8 +623,10 @@ static void android_snd_read_postprocess(MSFilter *obj) {
ms_ticker_set_synchronizer(obj->ticker, NULL);
ms_mutex_lock(&ictx->mutex);
ms_ticker_synchronizer_destroy(ictx->mTickerSynchronizer);
ictx->mTickerSynchronizer = NULL;
if (ictx->mTickerSynchronizer) {
ms_ticker_synchronizer_destroy(ictx->mTickerSynchronizer);
ictx->mTickerSynchronizer = NULL;
}
ms_mutex_unlock(&ictx->mutex);
}
......@@ -1215,7 +1206,10 @@ static void android_snd_card_device_create(JNIEnv *env, jobject deviceInfo, MSSn
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_BLUETOOTH) ||
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_EARPIECE) ||
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_SPEAKER) ||
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_MICROPHONE)
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_MICROPHONE) ||
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_HEADSET) ||
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_HEADPHONES) ||
(type == MSSndCardDeviceType::MS_SND_CARD_DEVICE_TYPE_GENERIC_USB)
) {
const char * name = ms_strdup(ms_android_get_device_product_name(env, deviceInfo));
......
......@@ -219,6 +219,7 @@ static SoundDeviceDescription devices[]={
{ "HUAWEI", "HUAWEI VNS-L31", "hi6250", DEVICE_MCH264ENC_NO_PIX_FMT_CONV, 0 },
{ "HUAWEI", "ALE-L21", "hi6210sft", DEVICE_MCH264ENC_NO_PIX_FMT_CONV, 0 },
{ "Huawei", "AOSP on angler", "msm8994", DEVICE_HAS_BUILTIN_AEC | DEVICE_HAS_BUILTIN_OPENSLES_AEC, 0, 16000},
{ "HUAWEI", "SLA-L02", "msm8937", DEVICE_HAS_BUILTIN_OPENSLES_AEC, 0, 16000 },
{ "WIKO", "HIGHWAY 4G", "tegra", DEVICE_HAS_UNSTANDARD_LIBMEDIA, 0 },
{ "Symphony", "HIGHWAY 4G", "", DEVICE_HAS_UNSTANDARD_LIBMEDIA, 0 },
......
......@@ -22,15 +22,26 @@
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/flowcontrol.h"
#include <math.h>
void ms_audio_flow_controller_init(MSAudioFlowController *ctl)
{
static void ms_audio_flow_controller_reset(MSAudioFlowController *ctl){
ctl->target_samples = 0;
ctl->total_samples = 0;
ctl->current_pos = 0;
ctl->current_dropped = 0;
}
void ms_audio_flow_controller_init(MSAudioFlowController *ctl){
ctl->config.strategy = MSAudioFlowControlSoft;
ctl->config.silent_threshold = 0.02f;
ms_audio_flow_controller_reset(ctl);
}
void ms_audio_flow_controller_set_config(MSAudioFlowController *ctl, const MSAudioFlowControlConfig *cfg) {
ctl->config = *cfg;
ms_message("MSAudioFlowControl: configured with strategy=[%i] and silent_threshold=[%f].", cfg->strategy, cfg->silent_threshold);
}
void ms_audio_flow_controller_set_target(MSAudioFlowController *ctl, uint32_t samples_to_drop, uint32_t total_samples)
{
ctl->target_samples = samples_to_drop;
......@@ -79,6 +90,18 @@ static bool_t ms_audio_flow_controller_running(MSAudioFlowController *ctl) {
return (ctl->total_samples > 0) && (ctl->target_samples > 0);
}
static const float max_e = (32768* 0.7f); /* 0.7 - is RMS factor **/
static float compute_frame_power(int16_t *samples, uint32_t nsamples){
float acc = 0;
uint32_t i;
for (i = 0; i < nsamples; ++i){
int sample = samples[i];
acc += (float) (sample*sample);
}
return sqrtf(acc / (float)nsamples) / max_e;
}
mblk_t *ms_audio_flow_controller_process(MSAudioFlowController *ctl, mblk_t *m){
if (ms_audio_flow_controller_running(ctl)) {
uint32_t nsamples = (uint32_t)((m->b_wptr - m->b_rptr) / 2);
......@@ -86,19 +109,35 @@ mblk_t *ms_audio_flow_controller_process(MSAudioFlowController *ctl, mblk_t *m){
uint32_t todrop;
ctl->current_pos += nsamples;
th_dropped = (uint32_t)(((uint64_t)ctl->target_samples * (uint64_t)ctl->current_pos) / (uint64_t)ctl->total_samples);
todrop = (th_dropped > ctl->current_dropped) ? (th_dropped - ctl->current_dropped) : 0;
if (todrop > 0) {
if ((todrop * 8) < nsamples) {
discard_well_choosed_samples(m, nsamples, todrop);
} else {
ms_warning("Too many samples to drop, dropping entire frame.");
if (ctl->config.strategy == MSAudioFlowControlBasic){
if (ctl->current_dropped + nsamples <= ctl->target_samples){
freemsg(m);
ctl->current_dropped += nsamples;
//ms_message("MSAudioFlowControl: dropped %i samples in basic strategy.", (int) nsamples);
m = NULL;
todrop = nsamples;
}
/*ms_message("th_dropped=%i, current_dropped=%i, %i samples dropped", th_dropped, ctl->current_dropped, todrop);*/
ctl->current_dropped += todrop;
}else{
th_dropped = (uint32_t)(((uint64_t)ctl->target_samples * (uint64_t)ctl->current_pos) / (uint64_t)ctl->total_samples);
todrop = (th_dropped > ctl->current_dropped) ? (th_dropped - ctl->current_dropped) : 0;
if (todrop > 0) {
if (nsamples <= ctl->target_samples && compute_frame_power((int16_t*)m->b_rptr, nsamples) < ctl->config.silent_threshold){
/* This frame is almost silent, let's drop it entirely, it won't be noticeable.*/
//ms_message("MSAudioFlowControl: dropping silent frame.");
freemsg(m);
m = NULL;
todrop = nsamples;
}else if ((todrop * 8) < nsamples) {
/* eliminate samples at zero-crossing */
discard_well_choosed_samples(m, nsamples, todrop);
} else {
ms_warning("MSAudioFlowControl: too many samples to drop, dropping entire frame.");
freemsg(m);
m = NULL;
todrop = nsamples;
}
/*ms_message("th_dropped=%i, current_dropped=%i, %i samples dropped", th_dropped, ctl->current_dropped, todrop);*/
ctl->current_dropped += todrop;
}
}
if (ctl->current_pos >= ctl->total_samples) ctl->target_samples = 0; /*stop discarding*/
}
......@@ -117,24 +156,27 @@ typedef struct MSAudioFlowControlState {
static void ms_audio_flow_control_init(MSFilter *f) {
MSAudioFlowControlState *s = ms_new0(MSAudioFlowControlState, 1);
ms_audio_flow_controller_init(&s->afc);
f->data = s;
}
static void ms_audio_flow_control_preprocess(MSFilter *f) {
MSAudioFlowControlState *s = (MSAudioFlowControlState *)f->data;
ms_audio_flow_controller_init(&s->afc);
ms_audio_flow_controller_reset(&s->afc);
}
static void ms_audio_flow_control_process(MSFilter *f) {
MSAudioFlowControlState *s = (MSAudioFlowControlState *)f->data;
mblk_t *m;
ms_filter_lock(f);
while((m = ms_queue_get(f->inputs[0])) != NULL) {
m = ms_audio_flow_controller_process(&s->afc, m);
if (m) {
ms_queue_put(f->outputs[0], m);
}
}
ms_filter_unlock(f);
}
static void ms_audio_flow_control_postprocess(MSFilter *f) {
......@@ -147,20 +189,25 @@ static void ms_audio_flow_control_uninit(MSFilter *f) {
}
void ms_audio_flow_control_event_handler(void *user_data, MSFilter *source, unsigned int event, void *eventdata) {
if (event == MS_AUDIO_FLOW_CONTROL_DROP_EVENT) {
MSFilter *f = (MSFilter *)user_data;
MSAudioFlowControlState *s = (MSAudioFlowControlState *)f->data;
MSAudioFlowControlDropEvent *ev = (MSAudioFlowControlDropEvent *)eventdata;
if (!ms_audio_flow_controller_running(&s->afc)) {
ms_warning("Too much buffered audio signal, throwing out %u ms", ev->drop_ms);
ms_audio_flow_controller_set_target(&s->afc,
(ev->drop_ms * s->samplerate * s->nchannels) / 1000,
(ev->flow_control_interval_ms * s->samplerate * s->nchannels) / 1000);
}
}
static int ms_audio_flow_control_set_config(MSFilter *f, void *arg) {
MSAudioFlowControlState *s = (MSAudioFlowControlState *)f->data;
ms_audio_flow_controller_set_config(&s->afc, (MSAudioFlowControlConfig*) arg);
return 0;
}
static int ms_audio_flow_control_drop(MSFilter *f, void *arg) {
MSAudioFlowControlState *s = (MSAudioFlowControlState *)f->data;
MSAudioFlowControlDropEvent *ctl = (MSAudioFlowControlDropEvent *)arg;
ms_filter_lock(f);
if (!ms_audio_flow_controller_running(&s->afc)) {
ms_message("MSAudioFlowControl: requested to drop %i ms ", (int)ctl->drop_ms);
ms_audio_flow_controller_set_target(&s->afc,
(ctl->drop_ms * s->samplerate * s->nchannels) / 1000,
(ctl->flow_control_interval_ms * s->samplerate * s->nchannels) / 1000);
}
ms_filter_unlock(f);
return 0;
}
static int ms_audio_flow_control_set_sample_rate(MSFilter *f, void *arg) {
MSAudioFlowControlState *s = (MSAudioFlowControlState *)f->data;
......@@ -187,6 +234,8 @@ static int ms_audio_flow_control_get_nchannels(MSFilter *f, void *arg) {
}
static MSFilterMethod ms_audio_flow_control_methods[] = {
{ MS_AUDIO_FLOW_CONTROL_SET_CONFIG, ms_audio_flow_control_set_config },
{ MS_AUDIO_FLOW_CONTROL_DROP, ms_audio_flow_control_drop },
{ MS_FILTER_SET_SAMPLE_RATE, ms_audio_flow_control_set_sample_rate },
{ MS_FILTER_GET_SAMPLE_RATE, ms_audio_flow_control_get_sample_rate },
{ MS_FILTER_SET_NCHANNELS, ms_audio_flow_control_set_nchannels },
......
......@@ -54,11 +54,13 @@ The BSD license below is for the original work.
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
//#include <CoreServices/CarbonCore/Debugging.h>
#include "mediastreamer2/mssndcard.h"
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/msticker.h"
#include "mediastreamer2/devices.h"
#if __LP64__
#define UINT32_PRINTF "u"
......@@ -190,6 +192,57 @@ static MSSndCard *au_card_duplicate(MSSndCard * obj)
return card;
}
static void ca_set_device_type(AudioDeviceID deviceId, MSSndCard * card){
AudioObjectPropertyAddress request;
UInt32 terminalType= 0;
UInt32 returnSize = 0;
// Check transport
AudioDevicePropertyID transportType;
request.mSelector = kAudioDevicePropertyTransportType;
request.mScope = kAudioObjectPropertyScopeGlobal;
request.mElement = kAudioObjectPropertyElementMaster;
returnSize = sizeof(AudioDevicePropertyID);
AudioObjectGetPropertyData(deviceId, &request, 0, 0, &returnSize, &transportType);
// Check Terminal Type
request.mSelector = kAudioDevicePropertyDataSource;
request.mScope = kAudioObjectPropertyScopeGlobal;
request.mElement = kAudioObjectPropertyElementMaster;
returnSize = sizeof(UInt32);
AudioObjectGetPropertyData(deviceId, &request, 0, NULL, &returnSize, &terminalType);
// Terminal type can be not set on same cases like in Builtin card. If unknown, check deeper in the source that depends of the capability
if( terminalType == kAudioStreamTerminalTypeUnknown ){
request.mSelector = kAudioDevicePropertyDataSource;
request.mElement = kAudioObjectPropertyElementMaster;
returnSize = sizeof(UInt32);
if( card->capabilities == MS_SND_CARD_CAP_CAPTURE){// Multiple capability is not supported
request.mScope = kAudioObjectPropertyScopeInput;
returnSize = sizeof(UInt32);
AudioObjectGetPropertyData(deviceId, &request, 0, NULL, &returnSize, &terminalType);
}else if( card->capabilities == MS_SND_CARD_CAP_PLAYBACK){
request.mScope = kAudioObjectPropertyScopeOutput;
AudioObjectGetPropertyData(deviceId, &request, 0, NULL, &returnSize, &terminalType);
}
}
// 'ispk', 'espk', 'imic' and 'emic' are undocumented terminalType on Mac. This seems to be IOS types but can be retrieve by the mac API
if( transportType == kAudioDeviceTransportTypeBluetooth || transportType == kAudioDeviceTransportTypeBluetoothLE)
card->device_type = MS_SND_CARD_DEVICE_TYPE_BLUETOOTH;
else if(terminalType == kAudioStreamTerminalTypeReceiverSpeaker)
card->device_type = MS_SND_CARD_DEVICE_TYPE_EARPIECE;
else if(terminalType == kAudioStreamTerminalTypeSpeaker || terminalType == 'ispk' || terminalType == 'espk')
card->device_type = MS_SND_CARD_DEVICE_TYPE_SPEAKER;
else if(terminalType == kAudioStreamTerminalTypeMicrophone || terminalType == kAudioStreamTerminalTypeReceiverMicrophone || terminalType== 'imic' || terminalType== 'emic')
card->device_type = MS_SND_CARD_DEVICE_TYPE_MICROPHONE;
else if(terminalType == kAudioStreamTerminalTypeHeadsetMicrophone)
card->device_type = MS_SND_CARD_DEVICE_TYPE_HEADSET;
else if(terminalType == kAudioStreamTerminalTypeHeadphones)
card->device_type = MS_SND_CARD_DEVICE_TYPE_HEADPHONES;
else if(transportType == kAudioDeviceTransportTypeUSB)
card->device_type = MS_SND_CARD_DEVICE_TYPE_GENERIC_USB;
else
card->device_type = MS_SND_CARD_DEVICE_TYPE_UNKNOWN;
}
static MSSndCard *ca_card_new(const char *name, const char * uidname, AudioDeviceID dev, unsigned cap)