Commit 0d3777bd authored by François Grisez's avatar François Grisez

Add MSPlayer class

This class enable to read some media files.
Supported format are WAVE and MATROSKA
Supported codec :
	+ WAVE: only PCM s16l
	+ MATROSKA: pcmu, opus and h264
parent dbded55e
#ifndef FILEPLAYER_H
#define FILEPLAYER_H
#include "mscommon.h"
#include "mssndcard.h"
typedef struct _FilePlayer FilePlayer;
typedef void (*FilePlayerEofCallback)(void *user_data);
MS2_PUBLIC FilePlayer *ms_file_player_new(MSSndCard *snd_card, const char *video_display_name);
MS2_PUBLIC void ms_file_player_free(FilePlayer *obj);
MS2_PUBLIC void ms_file_player_set_eof_callback(FilePlayer *obj, FilePlayerEofCallback cb, void *user_data);
MS2_PUBLIC bool_t ms_file_player_open(FilePlayer *obj, const char *filepath);
MS2_PUBLIC void ms_file_player_close(FilePlayer *obj);
MS2_PUBLIC bool_t ms_file_player_start(FilePlayer *obj);
MS2_PUBLIC void ms_file_player_stop(FilePlayer *obj);
MS2_PUBLIC void ms_file_player_pause(FilePlayer *obj);
MS2_PUBLIC void ms_file_player_seek(FilePlayer *obj, int seek_pos_ms);
#endif
......@@ -287,7 +287,8 @@ libmediastreamer_voip_la_SOURCES+= voip/rfc2429.h \
videofilters/extdisplay.c \
utils/bits_rw.c \
utils/x11_helper.c \
voip/layouts.c voip/layouts.h
voip/layouts.c voip/layouts.h \
voip/msfileplayer.c
if ORTP_ENABLED
libmediastreamer_voip_la_SOURCES+= voip/videostream.c
......
......@@ -117,7 +117,13 @@ typedef struct _wave_header_t
data_t data_chunk;
} wave_header_t;
#define WAVE_FORMAT_PCM 0x0001
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#define WAVE_FORMAT_ALAW 0x0006
#define WAVE_FORMAT_MULAW 0x0007
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#define wave_header_get_format_type(header) le_uint16((header)->format_chunk.type)
#define wave_header_get_rate(header) le_uint32((header)->format_chunk.rate)
#define wave_header_get_channel(header) le_uint16((header)->format_chunk.channel)
#define wave_header_get_bpsmpl(header) \
......
......@@ -445,13 +445,13 @@ static void wav_private_set(WavPrivate *data, const MSFmtDescriptor *obj) {
}
static void wav_private_serialize(const WavPrivate *obj, uint8_t **data, size_t *size) {
*size = 22;
*size = sizeof(WavPrivate);
*data = (uint8_t *)ms_new0(uint8_t, *size);
memcpy(*data, obj, *size);
}
static inline void wav_private_load(WavPrivate *obj, const uint8_t *data) {
memcpy(obj, data, 22);
memcpy(obj, data, sizeof(WavPrivate));
}
/* µLaw module */
......@@ -2405,12 +2405,12 @@ static int player_get_state(MSFilter *f, void *arg) {
static MSFilterMethod player_methods[]= {
{ MS_FILTER_GET_OUTPUT_FMT , player_get_output_fmt },
{ MS_PLAYER_OPEN , player_open_file },
{ MS_PLAYER_CLOSE , player_close },
{ MS_PLAYER_START , player_start },
{ MS_PLAYER_PAUSE , player_stop },
{ MS_PLAYER_GET_STATE , player_get_state },
{ 0 , NULL }
{ MS_PLAYER_OPEN , player_open_file },
{ MS_PLAYER_CLOSE , player_close },
{ MS_PLAYER_START , player_start },
{ MS_PLAYER_PAUSE , player_stop },
{ MS_PLAYER_GET_STATE , player_get_state },
{ 0 , NULL }
};
#ifdef _MSC_VER
......
#include "../../include/mediastreamer2/fileplayer.h"
#include "../../include/mediastreamer2/msfilter.h"
#include "../../include/mediastreamer2/msticker.h"
#include "../audiofilters/waveheader.h"
#define ms_filter_destroy_and_reset(obj) \
ms_filter_destroy(obj); \
obj = NULL
#define ms_filter_destroy_and_reset_if_not_null(obj) \
if(obj != NULL) { \
ms_filter_destroy(obj); \
obj = NULL; \
}
#define ms_free_if_not_null(obj) if(obj != NULL) ms_free(obj)
typedef enum _FileFormat {
FILE_FORMAT_UNKNOWN,
FILE_FORMAT_WAVE,
FILE_FORMAT_MATROSKA
} FileFormat;
typedef uint8_t FourCC[4];
typedef struct _FormatDesc {
FileFormat format;
FourCC four_cc;
} FormatDesc;
static const FormatDesc _format_desc_list[] = {
{ FILE_FORMAT_WAVE , { 'R', 'I', 'F', 'F' } },
{ FILE_FORMAT_MATROSKA , { 0x1a, 0x45, 0xdf, 0xa3 } },
{ FILE_FORMAT_UNKNOWN , { 0x0, 0x0, 0x0, 0x0 } },
};
static bool_t four_cc_compare(const FourCC arg1, const FourCC arg2);
static FileFormat four_cc_to_file_format(const FourCC four_cc);
struct _FilePlayer {
MSFilter *player;
MSFilter *audio_decoder;
MSFilter *audio_sink;
MSFilter *video_decoder;
MSFilter *video_sink;
MSFilter *resampler;
MSPinFormat audio_pin_fmt;
MSPinFormat video_pin_fmt;
MSTicker *ticker;
FileFormat format;
bool_t is_open;
char *filename;
FilePlayerEofCallback eof_cb;
void *user_data_cb;
ms_mutex_t cb_access;
MSSndCard *snd_card;
char *video_display;
};
static bool_t _get_format(const char *filepath, FileFormat *format);
static void _eof_filter_notify_cb(void *userdata, struct _MSFilter *f, unsigned int id, void *arg);
static void _create_decoders(FilePlayer *obj);
static void _create_sinks(FilePlayer *obj);
static void _destroy_graph(FilePlayer *obj);
static bool_t _link_all(FilePlayer *obj);
static void _unlink_all(FilePlayer *obj);
static bool_t four_cc_compare(const FourCC arg1, const FourCC arg2) {
return arg1[0] == arg2[0]
&& arg1[1] == arg2[1]
&& arg1[2] == arg2[2]
&& arg1[3] == arg2[3];
}
static FileFormat four_cc_to_file_format(const FourCC four_cc) {
int i;
for(i=0; _format_desc_list[i].format != FILE_FORMAT_UNKNOWN; i++) {
if(four_cc_compare(four_cc, _format_desc_list[i].four_cc)) {
return _format_desc_list[i].format;
}
}
return FILE_FORMAT_UNKNOWN;
}
FilePlayer *ms_file_player_new(MSSndCard *snd_card, const char *video_display_name) {
FilePlayer *obj = (FilePlayer *)ms_new0(FilePlayer, 1);
obj->ticker = ms_ticker_new();
ms_mutex_init(&obj->cb_access, NULL);
obj->snd_card = snd_card;
if(video_display_name != NULL && strlen(video_display_name) > 0) {
obj->video_display = ms_strdup(video_display_name);
}
return obj;
}
void ms_file_player_free(FilePlayer *obj) {
ms_file_player_close(obj);
ms_ticker_destroy(obj->ticker);
ms_free_if_not_null(obj->video_display);
}
bool_t ms_file_player_open(FilePlayer *obj, const char *filepath) {
wave_header_t header;
int fd;
char *tmp;
ms_message("Openning %s", filepath);
if(access(filepath, F_OK) != 0) {
ms_error("Cannot open %s. File does not exist", filepath);
return FALSE;
}
if(!_get_format(filepath, &obj->format)) {
ms_error("Fails to detect file format of %s", filepath);
return FALSE;
}
switch(obj->format) {
case FILE_FORMAT_WAVE:
fd = open(filepath, O_RDONLY);
if(fd == -1) {
ms_error("Cannot open %s", filepath);
return FALSE;
}
if(ms_read_wav_header_from_fd(&header, fd) == -1) {
ms_error("Cannot open %s. Invalid WAV format", filepath);
return FALSE;
}
close(fd);
if(wave_header_get_format_type(&header) != WAVE_FORMAT_PCM) {
ms_error("Cannot open %s. Codec not supported", filepath);
return FALSE;
}
obj->player = ms_filter_new(MS_FILE_PLAYER_ID);
break;
case FILE_FORMAT_MATROSKA:
if((obj->player = ms_filter_new(MS_MKV_PLAYER_ID)) == NULL) {
ms_error("Cannot open %s. Matroska file support is disabled", filepath);
return FALSE;
}
break;
case FILE_FORMAT_UNKNOWN:
ms_error("Cannot open %s. Unknown format", filepath);
return FALSE;
}
tmp = ms_strdup(filepath);
if(ms_filter_call_method(obj->player, MS_PLAYER_OPEN, tmp) == -1) {
ms_error("Cannot open %s", filepath);
ms_free(tmp);
ms_filter_destroy(obj->player);
return FALSE;
}
ms_free(tmp);
_create_decoders(obj);
_create_sinks(obj);
if(!_link_all(obj)) {
ms_error("Cannot open %s. Could not build playing graph", filepath);
_destroy_graph(obj);
return FALSE;
}
ms_filter_add_notify_callback(obj->player, _eof_filter_notify_cb, obj, FALSE);
ms_ticker_attach(obj->ticker, obj->player);
obj->is_open = TRUE;
obj->filename = ms_strdup(filepath);
return TRUE;
}
void ms_file_player_close(FilePlayer *obj) {
if(obj->is_open) {
ms_ticker_detach(obj->ticker, obj->player);
ms_filter_call_method_noarg(obj->player, MS_PLAYER_CLOSE);
_unlink_all(obj);
_destroy_graph(obj);
obj->is_open = FALSE;
ms_free(obj->filename); obj->filename = NULL;
}
}
bool_t ms_file_player_start(FilePlayer *obj) {
if(!obj->is_open) {
ms_error("Cannot start playing. No file has been opened");
return FALSE;
}
if(ms_filter_call_method_noarg(obj->player, MS_PLAYER_START) == -1) {
ms_error("Could not play %s. Playing filter failed to start", obj->filename);
return FALSE;
}
return TRUE;
}
void ms_file_player_stop(FilePlayer *obj) {
int seek_pos = 0;
if(obj->is_open) {
ms_filter_call_method_noarg(obj->player, MS_PLAYER_PAUSE);
ms_filter_call_method(obj->player, MS_PLAYER_SEEK_MS, &seek_pos);
}
}
void ms_file_player_pause(FilePlayer *obj) {
if(obj->is_open) {
ms_filter_call_method_noarg(obj->player, MS_PLAYER_PAUSE);
}
}
void ms_file_player_seek(FilePlayer *obj, int seek_pos_ms) {
if(obj->is_open) {
ms_filter_call_method(obj->player, MS_PLAYER_SEEK_MS, &seek_pos_ms);
}
}
void ms_file_player_set_eof_callback(FilePlayer *obj, FilePlayerEofCallback cb, void *user_data) {
ms_mutex_lock(&obj->cb_access);
obj->eof_cb = cb;
obj->user_data_cb = user_data;
ms_mutex_unlock(&obj->cb_access);
}
static bool_t _get_format(const char *filepath, FileFormat *format) {
FourCC four_cc;
size_t data_read;
FILE *file = fopen(filepath, "r");
if(file == NULL) {
ms_error("Cannot open %s", filepath);
*format = FILE_FORMAT_UNKNOWN;
return FALSE;
}
data_read = fread(four_cc, 4, 1, file);
fclose(file);
if(data_read < 1) {
*format = FILE_FORMAT_UNKNOWN;
return FALSE;
}
*format = four_cc_to_file_format(four_cc);
return TRUE;
}
static void _create_decoders(FilePlayer *obj) {
int sample_rate, nchannels;
switch(obj->format) {
case FILE_FORMAT_WAVE:
ms_filter_call_method(obj->player, MS_FILTER_GET_SAMPLE_RATE, &sample_rate);
ms_filter_call_method(obj->player, MS_FILTER_GET_NCHANNELS, &nchannels);
obj->audio_pin_fmt.pin = 0;
obj->audio_pin_fmt.fmt = ms_factory_get_audio_format(ms_factory_get_fallback(), "pcm", sample_rate, nchannels, NULL);
break;
case FILE_FORMAT_MATROSKA:
obj->audio_pin_fmt.pin = 1;
obj->video_pin_fmt.pin = 0;
ms_filter_call_method(obj->player, MS_FILTER_GET_OUTPUT_FMT, &obj->audio_pin_fmt);
ms_filter_call_method(obj->player, MS_FILTER_GET_OUTPUT_FMT, &obj->video_pin_fmt);
if(obj->audio_pin_fmt.fmt) {
obj->audio_decoder = ms_factory_create_decoder(ms_factory_get_fallback(), obj->audio_pin_fmt.fmt->encoding);
if(obj->audio_decoder == NULL) {
ms_error("Could not create audio decoder for %s", obj->audio_pin_fmt.fmt->encoding);
obj->audio_pin_fmt.fmt = NULL;
}
sample_rate = obj->audio_pin_fmt.fmt->rate;
nchannels = obj->audio_pin_fmt.fmt->nchannels;
ms_filter_call_method(obj->audio_decoder, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
ms_filter_call_method(obj->audio_decoder, MS_FILTER_SET_NCHANNELS, &nchannels);
}
if(obj->video_pin_fmt.fmt) {
obj->video_decoder = ms_factory_create_decoder(ms_factory_get_fallback(), obj->video_pin_fmt.fmt->encoding);
if(obj->video_decoder == NULL) {
ms_error("Could not create video decoder for %s", obj->video_pin_fmt.fmt->encoding);
obj->video_pin_fmt.fmt = NULL;
}
}
break;
default:
break;
}
}
static void _create_sinks(FilePlayer *obj) {
int sink_sample_rate, sample_rate, nchannels;
if(obj->audio_pin_fmt.fmt && obj->snd_card) {
sample_rate = obj->audio_pin_fmt.fmt->rate;
nchannels = obj->audio_pin_fmt.fmt->nchannels;
if((obj->audio_sink = ms_snd_card_create_writer(obj->snd_card))) {
if(ms_filter_call_method(obj->audio_sink, MS_FILTER_SET_SAMPLE_RATE, &sample_rate) == -1) {
ms_warning("The sound card (%s) does not support %dHz", obj->snd_card->name, sample_rate);
ms_filter_call_method(obj->audio_sink, MS_FILTER_GET_SAMPLE_RATE, &sink_sample_rate);
if(obj->audio_decoder == NULL) {
ms_message("Resampling to %dHz", sink_sample_rate);
obj->resampler = ms_filter_new(MS_RESAMPLE_ID);
ms_filter_call_method(obj->resampler, MS_FILTER_SET_SAMPLE_RATE, &sample_rate);
ms_filter_call_method(obj->resampler, MS_FILTER_SET_OUTPUT_SAMPLE_RATE, &sink_sample_rate);
ms_filter_call_method(obj->resampler, MS_FILTER_SET_NCHANNELS, &nchannels);
} else {
ms_message("Setting output sampling rate of the audio decoder to %dHz", sink_sample_rate);
ms_filter_call_method(obj->audio_decoder, MS_FILTER_SET_SAMPLE_RATE, &sink_sample_rate);
}
}
ms_filter_call_method(obj->audio_sink, MS_FILTER_SET_NCHANNELS, &nchannels);
} else {
ms_error("Could not create audio sink. Soundcard=%s", obj->snd_card->name);
}
}
if(obj->video_pin_fmt.fmt && obj->video_display) {
obj->video_sink = ms_filter_new_from_name(obj->video_display);
if(obj->video_sink == NULL) {
ms_error("Could not create video sink: %s", obj->video_display);
}
}
}
static void _destroy_graph(FilePlayer *obj) {
ms_filter_destroy_and_reset_if_not_null(obj->player);
ms_filter_destroy_and_reset_if_not_null(obj->audio_decoder);
ms_filter_destroy_and_reset_if_not_null(obj->video_decoder);
ms_filter_destroy_and_reset_if_not_null(obj->audio_sink);
ms_filter_destroy_and_reset_if_not_null(obj->video_sink);
ms_filter_destroy_and_reset_if_not_null(obj->resampler);
obj->audio_pin_fmt.fmt = NULL;
obj->video_pin_fmt.fmt = NULL;
}
static bool_t _link_all(FilePlayer *obj) {
MSConnectionHelper helper;
if(obj->player == NULL) {
ms_error("Could not link graph. There is no playing filter");
return FALSE;
}
if(obj->audio_sink == NULL && obj->video_sink == NULL) {
ms_error("Could not link graph. There is neither audio sink nor video sink");
return FALSE;
}
if(obj->audio_pin_fmt.fmt && obj->audio_sink) {
ms_connection_helper_start(&helper);
ms_connection_helper_link(&helper, obj->player, -1, obj->audio_pin_fmt.pin);
if(obj->audio_decoder) ms_connection_helper_link(&helper, obj->audio_decoder, 0, 0);
if(obj->resampler) ms_connection_helper_link(&helper, obj->resampler, 0, 0);
ms_connection_helper_link(&helper, obj->audio_sink, 0, -1);
}
if(obj->video_pin_fmt.fmt && obj->video_sink) {
ms_connection_helper_start(&helper);
ms_connection_helper_link(&helper, obj->player, -1, obj->video_pin_fmt.pin);
if(obj->video_decoder) ms_connection_helper_link(&helper, obj->video_decoder, 0 , 0);
ms_connection_helper_link(&helper, obj->video_sink, 0, -1);
}
return TRUE;
}
static void _unlink_all(FilePlayer *obj) {
MSConnectionHelper helper;
if(obj->audio_pin_fmt.fmt && obj->audio_sink) {
ms_connection_helper_start(&helper);
ms_connection_helper_unlink(&helper, obj->player, -1, obj->audio_pin_fmt.pin);
if(obj->audio_decoder) ms_connection_helper_unlink(&helper, obj->audio_decoder, 0, 0);
if(obj->resampler) ms_connection_helper_unlink(&helper, obj->resampler, 0, 0);
ms_connection_helper_unlink(&helper, obj->audio_sink, 0, -1);
}
if(obj->video_pin_fmt.fmt && obj->video_sink) {
ms_connection_helper_start(&helper);
ms_connection_helper_unlink(&helper, obj->player, -1, obj->video_pin_fmt.pin);
if(obj->video_decoder) ms_connection_helper_unlink(&helper, obj->video_decoder, 0 , 0);
ms_connection_helper_unlink(&helper, obj->video_sink, 0, -1);
}
}
static void _eof_filter_notify_cb(void *userdata, struct _MSFilter *f, unsigned int id, void *arg) {
FilePlayer *obj = (FilePlayer *)userdata;
ms_mutex_lock(&obj->cb_access);
if(f == obj->player && id == MS_PLAYER_EOF && obj->eof_cb != NULL) {
obj->eof_cb(obj->user_data_cb);
}
ms_mutex_unlock(&obj->cb_access);
}
......@@ -15,7 +15,8 @@ mediastreamer2_tester_SOURCES= \
mediastreamer2_sound_card_tester.c \
mediastreamer2_adaptive_tester.c \
mediastreamer2_audio_stream_tester.c \
mediastreamer2_framework_tester.c
mediastreamer2_framework_tester.c \
mediastreamer2_player_tester.c
AM_CPPFLAGS=\
-I$(top_srcdir)/ \
......@@ -44,24 +45,20 @@ endif BUILD_MACOSX
if BUILD_VIDEO
mediastreamer2_tester_SOURCES+= mediastreamer2_video_stream_tester.c
if BUILD_MATROSKA
mediastreamer2_tester_SOURCES+= mediastreamer2_player_recorder_tester.c
endif
endif
AM_LDFLAGS=-no-undefined -export-dynamic
AM_CFLAGS=$(STRICT_OPTIONS) $(STRICT_OPTIONS_CC) $(ORTP_CFLAGS) $(CUNIT_CFLAGS)
if BUILD_VIDEO
AM_CFLAGS+=$(VIDEO_CFLAGS) $(GLEW_CFLAGS)
endif
if BUILD_MATROSKA
AM_CFLAGS+=-DHAVE_MATROSKA
endif
endif
test: mediastreamer2_tester
......
#include "mediastreamer2_tester.h"
#include "mediastreamer2_tester_private.h"
#include "../include/mediastreamer2/mediastream.h"
#include "../include/mediastreamer2/msfileplayer.h"
typedef struct {
MSFilter *audioSource;
MSFilter *audioEnc;
MSFilter *videoSource;
MSFilter *pixConverter;
MSFilter *tee;
MSFilter *videoSink;
MSFilter *videoEnc;
MSFilter *recorder;
MSTicker *ticker;
char *filename;
bool_t isMute[2];
} RecordStream;
static void recorder_stream_init(RecordStream *obj, MSFilterId recorderId, const char *filename) {
MSSndCardManager *sndCardManager;
MSWebCamManager *webcamManager;
MSSndCard *sndCard;
MSWebCam *webcam;
const char *videoRendererName;
MSPixFmt pixFmt;
MSVideoSize vsize;
memset(obj, 0, sizeof(RecordStream));
sndCardManager = ms_snd_card_manager_get();
sndCard = ms_snd_card_manager_get_default_capture_card(sndCardManager);
obj->audioSource = ms_snd_card_create_reader(sndCard);
webcamManager = ms_web_cam_manager_get();
webcam = ms_web_cam_manager_get_default_cam(webcamManager);
obj->videoSource = ms_web_cam_create_reader(webcam);
ms_filter_call_method(obj->videoSource, MS_FILTER_GET_PIX_FMT, &pixFmt);
ms_filter_call_method(obj->videoSource, MS_FILTER_GET_VIDEO_SIZE, &vsize);
videoRendererName = video_stream_get_default_video_renderer();
obj->videoSink = ms_filter_new_from_name(videoRendererName);
obj->tee = ms_filter_new(MS_TEE_ID);
if(pixFmt == MS_MJPEG) {
obj->pixConverter = ms_filter_new(MS_MJPEG_DEC_ID);
} else {
obj->pixConverter = ms_filter_new(MS_PIX_CONV_ID);
ms_filter_call_method(obj->pixConverter, MS_FILTER_SET_PIX_FMT, &pixFmt);
ms_filter_call_method(obj->pixConverter, MS_FILTER_SET_VIDEO_SIZE, &vsize);
}
obj->recorder = ms_filter_new(recorderId);
obj->ticker = ms_ticker_new();
obj->filename = strdup(filename);
}
static void recorder_stream_uninit(RecordStream *obj) {
ms_filter_destroy(obj->audioSource);
ms_filter_destroy(obj->videoSource);
ms_filter_destroy(obj->pixConverter);
ms_filter_destroy(obj->tee);
ms_filter_destroy(obj->videoSink);
ms_filter_destroy(obj->recorder);
if(obj->audioEnc != NULL) ms_filter_destroy(obj->audioEnc);
if(obj->videoEnc != NULL) ms_filter_destroy(obj->videoEnc);
ms_ticker_destroy(obj->ticker);
ms_free(obj->filename);
}
static void recorder_stream_set_video_codec(RecordStream *obj, const char *mime) {
MSVideoSize vsize;
MSPinFormat pinFmt;
if(obj->videoEnc != NULL) {
ms_filter_destroy(obj->videoEnc);
}
obj->videoEnc = ms_factory_create_encoder(ms_factory_get_fallback(), mime);
ms_filter_call_method(obj->videoSource, MS_FILTER_GET_VIDEO_SIZE, &vsize);
ms_filter_call_method(obj->videoEnc, MS_FILTER_SET_VIDEO_SIZE, &vsize);
pinFmt.pin = 0;
pinFmt.fmt = ms_factory_get_video_format(ms_factory_get_fallback(), mime, &vsize, NULL);
ms_filter_call_method(obj->recorder, MS_FILTER_SET_INPUT_FMT, &pinFmt);
}
static void recorder_stream_set_audio_codec(RecordStream *obj, const char *mime) {
int samplerate;
int nchannels;
MSPinFormat pinFmt;
if(obj->audioEnc != NULL) {
ms_filter_destroy(obj->audioEnc);
}
obj->audioEnc = ms_factory_create_encoder(ms_factory_get_fallback(), mime);
ms_filter_call_method(obj->audioSource, MS_FILTER_GET_SAMPLE_RATE, &samplerate);
ms_filter_call_method(obj->audioSource, MS_FILTER_GET_NCHANNELS, &nchannels);
ms_filter_call_method(obj->audioEnc, MS_FILTER_SET_SAMPLE_RATE, &samplerate);
ms_filter_call_method(obj->audioEnc, MS_FILTER_SET_NCHANNELS, &nchannels);
pinFmt.pin = 1;
if(strcmp(mime, "opus") == 0) {
pinFmt.fmt = ms_factory_get_audio_format(ms_factory_get_fallback(), mime, 48000, nchannels, NULL);
} else {
pinFmt.fmt = ms_factory_get_audio_format(ms_factory_get_fallback(), mime, samplerate, nchannels, NULL);
}
ms_filter_call_method(obj->recorder, MS_FILTER_SET_INPUT_FMT, &pinFmt);
}
//static inline void recorder_stream_mute(RecordStream *obj, MSFormatType type) {
// obj->isMute[type] = TRUE;
//}
static void recorder_stream_start(RecordStream *obj) {
ms_filter_link(obj->videoSource, 0, obj->pixConverter, 0);
ms_filter_link(obj->pixConverter, 0, obj->tee, 0);
ms_filter_link(obj->tee, 0, obj->videoSink, 0);