handle multiple different sample types

fixes #306
pull/317/head
Richard Fuchs 9 years ago
parent 1d5668d793
commit fb81332e80

@ -23,7 +23,7 @@ LDFLAGS+= `pkg-config --libs libavfilter`
include ../lib/lib.Makefile
SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c recaux.c packet.c \
decoder.c output.c mix.c
decoder.c output.c mix.c resample.c
LIBSRCS= loglib.c auxlib.c rtplib.c
OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o)

@ -4,6 +4,7 @@
#include <libavutil/audio_fifo.h>
#include <libavutil/channel_layout.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <glib.h>
#include <stdint.h>
#include <libavresample/avresample.h>
@ -13,17 +14,15 @@
#include "str.h"
#include "output.h"
#include "mix.h"
#include "resample.h"
struct decoder_s {
// format params
int channels;
int in_clockrate;
int out_clockrate;
format_t in_format,
out_format;
AVAudioResampleContext *avresample;
AVFrame *swr_frame;
int swr_buffers;
resample_t mix_resample,
output_resample;
AVCodecContext *avcctx;
AVPacket avpkt;
@ -126,9 +125,14 @@ decoder_t *decoder_new(const char *payload_str) {
decoder_t *ret = g_slice_alloc0(sizeof(*ret));
ret->channels = channels;
ret->in_clockrate = clockrate;
ret->out_clockrate = resample_audio ? : clockrate;
format_init(&ret->in_format);
ret->in_format.channels = channels;
ret->in_format.clockrate = clockrate;
// output defaults to same as input
ret->out_format = ret->in_format;
if (resample_audio)
ret->out_format.clockrate = resample_audio;
// sample format to be determined later when decoded frames arrive
AVCodec *codec = NULL;
if (def->avcodec_name)
@ -151,6 +155,9 @@ decoder_t *decoder_new(const char *payload_str) {
if (i)
goto err;
for (const enum AVSampleFormat *sfmt = codec->sample_fmts; sfmt && *sfmt != -1; sfmt++)
dbg("supported sample format for input codec %s: %s", codec->name, av_get_sample_fmt_name(*sfmt));
av_init_packet(&ret->avpkt);
ret->frame = av_frame_alloc();
err = "failed to alloc av frame";
@ -171,100 +178,37 @@ err:
}
static AVFrame *decoder_resample_frame(decoder_t *dec) {
const char *err;
if (dec->in_clockrate == dec->out_clockrate)
return dec->frame;
if (!dec->avresample) {
dec->avresample = avresample_alloc_context();
err = "failed to alloc resample context";
if (!dec->avresample)
goto err;
av_opt_set_int(dec->avresample, "in_channel_layout",
av_get_default_channel_layout(dec->channels), 0);
av_opt_set_int(dec->avresample, "in_sample_fmt",
AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(dec->avresample, "in_sample_rate",
dec->in_clockrate, 0);
av_opt_set_int(dec->avresample, "out_channel_layout",
av_get_default_channel_layout(dec->channels), 0);
av_opt_set_int(dec->avresample, "out_sample_fmt",
AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(dec->avresample, "out_sample_rate",
dec->out_clockrate, 0);
// av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ?
err = "failed to init resample context";
if (avresample_open(dec->avresample) < 0)
goto err;
}
// get a large enough buffer for resampled audio - this should be enough so we don't
// have to loop
int dst_samples = avresample_available(dec->avresample) +
av_rescale_rnd(avresample_get_delay(dec->avresample) + dec->frame->nb_samples,
dec->out_clockrate, dec->in_clockrate, AV_ROUND_UP);
if (!dec->swr_frame || dec->swr_buffers < dst_samples) {
av_frame_free(&dec->swr_frame);
dec->swr_frame = av_frame_alloc();
err = "failed to alloc resampling frame";
if (!dec->swr_frame)
goto err;
av_frame_copy_props(dec->swr_frame, dec->frame);
dec->swr_frame->format = dec->frame->format;
dec->swr_frame->channel_layout = dec->frame->channel_layout;
dec->swr_frame->nb_samples = dst_samples;
dec->swr_frame->sample_rate = dec->out_clockrate;
err = "failed to get resample buffers";
if (av_frame_get_buffer(dec->swr_frame, 0) < 0)
goto err;
dec->swr_buffers = dst_samples;
}
dec->swr_frame->nb_samples = dst_samples;
int ret_samples = avresample_convert(dec->avresample, dec->swr_frame->extended_data,
dec->swr_frame->linesize[0], dst_samples,
dec->frame->extended_data,
dec->frame->linesize[0], dec->frame->nb_samples);
err = "failed to resample audio";
if (ret_samples < 0)
goto err;
dec->swr_frame->nb_samples = ret_samples;
dec->swr_frame->pts = av_rescale(dec->frame->pts, dec->out_clockrate, dec->in_clockrate);
return dec->swr_frame;
err:
ilog(LOG_ERR, "Error resampling: %s", err);
return NULL;
}
static int decoder_got_frame(decoder_t *dec, output_t *output, metafile_t *metafile) {
// do we need to resample?
AVFrame *dec_frame = decoder_resample_frame(dec);
if (!dec_frame)
return -1;
// determine and save sample type
if (G_UNLIKELY(dec->in_format.format == -1))
dec->in_format.format = dec->out_format.format = dec->frame->format;
// handle mix output
pthread_mutex_lock(&metafile->mix_lock);
if (metafile->mix_out) {
if (G_UNLIKELY(dec->mixer_idx == (unsigned int) -1))
dec->mixer_idx = mix_get_index(metafile->mix);
output_config(metafile->mix_out, dec->out_clockrate, dec->channels);
mix_config(metafile->mix, dec->out_clockrate, dec->channels);
format_t actual_format;
output_config(metafile->mix_out, &dec->out_format, &actual_format);
mix_config(metafile->mix, &actual_format);
AVFrame *dec_frame = resample_frame(&dec->mix_resample, dec->frame, &actual_format);
if (!dec_frame) {
pthread_mutex_unlock(&metafile->mix_lock);
return -1;
}
AVFrame *clone = av_frame_clone(dec_frame);
clone->pts = dec_frame->pts;
if (mix_add(metafile->mix, clone, dec->mixer_idx, metafile->mix_out))
ilog(LOG_ERR, "Failed to add decoded packet to mixed output");
}
pthread_mutex_unlock(&metafile->mix_lock);
if (output) {
output_config(output, dec->out_clockrate, dec->channels);
// XXX might be a second resampling to same format
format_t actual_format;
output_config(output, &dec->out_format, &actual_format);
AVFrame *dec_frame = resample_frame(&dec->output_resample, dec->frame, &actual_format);
if (!dec_frame)
return -1;
if (output_add(output, dec_frame))
ilog(LOG_ERR, "Failed to add decoded packet to individual output");
}
@ -388,7 +332,7 @@ void decoder_close(decoder_t *dec) {
av_free(dec->avcctx);
#endif
av_frame_free(&dec->frame);
av_frame_free(&dec->swr_frame);
avresample_free(&dec->avresample);
resample_shutdown(&dec->mix_resample);
resample_shutdown(&dec->output_resample);
g_slice_free1(sizeof(*dec), dec);
}

@ -11,15 +11,14 @@
#include "types.h"
#include "log.h"
#include "output.h"
#include "resample.h"
#define NUM_INPUTS 4
struct mix_s {
// format params
int clockrate;
int channels;
format_t format;
AVFilterGraph *graph;
AVFilterContext *src_ctxs[NUM_INPUTS];
@ -30,9 +29,8 @@ struct mix_s {
unsigned int next_idx;
AVFrame *sink_frame;
AVAudioResampleContext *avresample;
AVFrame *swr_frame;
int swr_buffers;
resample_t resample;
uint64_t out_pts; // starting at zero
AVFrame *silence_frame;
@ -54,8 +52,10 @@ static void mix_shutdown(mix_t *mix) {
mix->src_ctxs[i] = NULL;
}
avresample_free(&mix->avresample);
resample_shutdown(&mix->resample);
avfilter_graph_free(&mix->graph);
format_init(&mix->format);
}
@ -64,7 +64,6 @@ void mix_destroy(mix_t *mix) {
return;
mix_shutdown(mix);
av_frame_free(&mix->sink_frame);
av_frame_free(&mix->swr_frame);
av_frame_free(&mix->silence_frame);
g_slice_free1(sizeof(*mix), mix);
}
@ -75,25 +74,16 @@ unsigned int mix_get_index(mix_t *mix) {
}
int mix_config(mix_t *mix, unsigned int clockrate, unsigned int channels) {
int mix_config(mix_t *mix, const format_t *format) {
const char *err;
char args[512];
// anything to do?
if (G_UNLIKELY(mix->clockrate != clockrate))
goto format_mismatch;
if (G_UNLIKELY(mix->channels != channels))
goto format_mismatch;
// all good
return 0;
if (format_eq(format, &mix->format))
return 0;
format_mismatch:
mix_shutdown(mix);
// copy params
mix->clockrate = clockrate;
mix->channels = channels;
mix->format = *format;
// filter graph
err = "failed to alloc filter graph";
@ -123,9 +113,9 @@ format_mismatch:
snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:"
"channel_layout=0x%" PRIx64,
1, mix->clockrate, mix->clockrate,
av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
av_get_default_channel_layout(mix->channels));
1, mix->format.clockrate, mix->format.clockrate,
av_get_sample_fmt_name(mix->format.format),
av_get_default_channel_layout(mix->format.channels));
err = "failed to create abuffer filter context";
if (avfilter_graph_create_filter(&mix->src_ctxs[i], flt, NULL, args, NULL, mix->graph))
@ -166,8 +156,7 @@ err:
mix_t *mix_new() {
mix_t *mix = g_slice_alloc0(sizeof(*mix));
mix->clockrate = -1;
mix->channels = -1;
format_init(&mix->format);
mix->sink_frame = av_frame_alloc();
for (int i = 0; i < NUM_INPUTS; i++)
@ -177,94 +166,24 @@ mix_t *mix_new() {
}
static AVFrame *mix_resample_frame(mix_t *mix, AVFrame *frame) {
const char *err;
if (frame->format == AV_SAMPLE_FMT_S16)
return frame;
if (!mix->avresample) {
mix->avresample = avresample_alloc_context();
err = "failed to alloc resample context";
if (!mix->avresample)
goto err;
av_opt_set_int(mix->avresample, "in_channel_layout",
av_get_default_channel_layout(mix->channels), 0);
av_opt_set_int(mix->avresample, "in_sample_fmt",
frame->format, 0);
av_opt_set_int(mix->avresample, "in_sample_rate",
mix->clockrate, 0);
av_opt_set_int(mix->avresample, "out_channel_layout",
av_get_default_channel_layout(mix->channels), 0);
av_opt_set_int(mix->avresample, "out_sample_fmt",
AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(mix->avresample, "out_sample_rate",
mix->clockrate, 0);
// av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ?
err = "failed to init resample context";
if (avresample_open(mix->avresample) < 0)
goto err;
}
// get a large enough buffer for resampled audio - this should be enough so we don't
// have to loop
int dst_samples = avresample_available(mix->avresample) +
av_rescale_rnd(avresample_get_delay(mix->avresample) + frame->nb_samples,
mix->clockrate, mix->clockrate, AV_ROUND_UP);
if (!mix->swr_frame || mix->swr_buffers < dst_samples) {
av_frame_free(&mix->swr_frame);
mix->swr_frame = av_frame_alloc();
err = "failed to alloc resampling frame";
if (!mix->swr_frame)
goto err;
av_frame_copy_props(mix->swr_frame, frame);
mix->swr_frame->format = frame->format;
mix->swr_frame->channel_layout = frame->channel_layout;
mix->swr_frame->nb_samples = dst_samples;
mix->swr_frame->sample_rate = mix->clockrate;
err = "failed to get resample buffers";
if (av_frame_get_buffer(mix->swr_frame, 0) < 0)
goto err;
mix->swr_buffers = dst_samples;
}
mix->swr_frame->nb_samples = dst_samples;
int ret_samples = avresample_convert(mix->avresample, mix->swr_frame->extended_data,
mix->swr_frame->linesize[0], dst_samples,
frame->extended_data,
frame->linesize[0], frame->nb_samples);
err = "failed to resample audio";
if (ret_samples < 0)
goto err;
mix->swr_frame->nb_samples = ret_samples;
mix->swr_frame->pts = av_rescale(frame->pts, mix->clockrate, mix->clockrate);
return mix->swr_frame;
err:
ilog(LOG_ERR, "Error resampling: %s", err);
return NULL;
}
static void mix_silence_fill_idx_upto(mix_t *mix, unsigned int idx, uint64_t upto) {
unsigned int silence_samples = mix->clockrate / 100;
unsigned int silence_samples = mix->format.clockrate / 100;
while (mix->in_pts[idx] < upto) {
if (G_UNLIKELY(!mix->silence_frame)) {
mix->silence_frame = av_frame_alloc();
mix->silence_frame->format = AV_SAMPLE_FMT_S16;
mix->silence_frame->format = mix->format.format;
mix->silence_frame->channel_layout =
av_get_default_channel_layout(mix->channels);
av_get_default_channel_layout(mix->format.channels);
mix->silence_frame->nb_samples = silence_samples;
mix->silence_frame->sample_rate = mix->clockrate;
mix->silence_frame->sample_rate = mix->format.clockrate;
if (av_frame_get_buffer(mix->silence_frame, 0) < 0) {
ilog(LOG_ERR, "Failed to get silence frame buffers");
return;
}
memset(mix->silence_frame->extended_data[0], 0, mix->silence_frame->linesize[0]);
int planes = av_sample_fmt_is_planar(mix->silence_frame->format) ? mix->format.channels : 1;
for (int i = 0; i < planes; i++)
memset(mix->silence_frame->extended_data[i], 0, mix->silence_frame->linesize[0]);
}
dbg("pushing silence frame into stream %i (%lli < %llu)", idx,
@ -282,19 +201,18 @@ static void mix_silence_fill_idx_upto(mix_t *mix, unsigned int idx, uint64_t upt
static void mix_silence_fill(mix_t *mix) {
if (mix->out_pts < mix->clockrate)
if (mix->out_pts < mix->format.clockrate)
return;
for (int i = 0; i < NUM_INPUTS; i++) {
// check the pts of each input and give them max 1 second of delay.
// if they fall behind too much, fill input with silence. otherwise
// output stalls and won't produce media
mix_silence_fill_idx_upto(mix, i, mix->out_pts - mix->clockrate);
mix_silence_fill_idx_upto(mix, i, mix->out_pts - mix->format.clockrate);
}
}
// frees the frame passed to it
int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output) {
const char *err;
@ -347,7 +265,7 @@ int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output) {
else
goto err;
}
frame = mix_resample_frame(mix, mix->sink_frame);
frame = resample_frame(&mix->resample, mix->sink_frame, &mix->format);
ret = output_add(output, frame);

@ -8,7 +8,7 @@
mix_t *mix_new(void);
void mix_destroy(mix_t *mix);
int mix_config(mix_t *, unsigned int clockrate, unsigned int channels);
int mix_config(mix_t *, const format_t *format);
int mix_add(mix_t *mix, AVFrame *frame, unsigned int idx, output_t *output);
unsigned int mix_get_index(mix_t *);

@ -3,6 +3,7 @@
#include <libavformat/avformat.h>
#include <libavutil/audio_fifo.h>
#include <libavutil/channel_layout.h>
#include <libavutil/samplefmt.h>
#include <limits.h>
#include <string.h>
#include <stdint.h>
@ -13,9 +14,8 @@
struct output_s {
char filename[PATH_MAX];
// format params
int clockrate;
int channels;
format_t requested_format,
actual_format;
AVCodecContext *avcctx;
AVFormatContext *fmtctx;
@ -150,30 +150,22 @@ int output_add(output_t *output, AVFrame *frame) {
output_t *output_new(const char *filename) {
output_t *ret = g_slice_alloc0(sizeof(*ret));
g_strlcpy(ret->filename, filename, sizeof(ret->filename));
ret->clockrate = -1;
ret->channels = -1;
format_init(&ret->requested_format);
format_init(&ret->actual_format);
return ret;
}
int output_config(output_t *output, unsigned int clockrate, unsigned int channels) {
int output_config(output_t *output, const format_t *requested_format, format_t *actual_format) {
const char *err;
// anything to do?
if (G_UNLIKELY(output->clockrate != clockrate))
goto format_mismatch;
if (G_UNLIKELY(output->channels != channels))
goto format_mismatch;
if (G_LIKELY(format_eq(requested_format, &output->requested_format)))
goto done;
// all good
return 0;
format_mismatch:
output_shutdown(output);
// copy params
output->clockrate = clockrate;
output->channels = channels;
output->requested_format = *requested_format;
err = "failed to alloc format context";
output->fmtctx = avformat_alloc_context();
@ -201,11 +193,25 @@ format_mismatch:
output->avcctx = output->avst->codec;
#endif
output->avcctx->channels = output->channels;
output->avcctx->channel_layout = av_get_default_channel_layout(output->channels);
output->avcctx->sample_rate = output->clockrate;
output->avcctx->sample_fmt = AV_SAMPLE_FMT_S16;
output->avcctx->time_base = (AVRational){1,output->clockrate};
// copy all format params
output->actual_format = output->requested_format;
// determine sample format to use
output->actual_format.format = -1;
for (const enum AVSampleFormat *sfmt = codec->sample_fmts; sfmt && *sfmt != -1; sfmt++) {
dbg("supported sample format for output codec %s: %s", codec->name, av_get_sample_fmt_name(*sfmt));
if (*sfmt == requested_format->format)
output->actual_format.format = *sfmt;
}
if (output->actual_format.format == -1 && codec->sample_fmts)
output->actual_format.format = codec->sample_fmts[0];
dbg("using output sample format %s for codec %s", av_get_sample_fmt_name(output->actual_format.format), codec->name);
output->avcctx->channels = output->actual_format.channels;
output->avcctx->channel_layout = av_get_default_channel_layout(output->actual_format.channels);
output->avcctx->sample_rate = output->actual_format.clockrate;
output->avcctx->sample_fmt = output->actual_format.format;
output->avcctx->time_base = (AVRational){1,output->actual_format.clockrate};
output->avcctx->bit_rate = mp3_bitrate;
output->avst->time_base = output->avcctx->time_base;
@ -255,6 +261,8 @@ got_fn:
output->fifo = av_audio_fifo_alloc(output->avcctx->sample_fmt, output->avcctx->channels,
output->frame->nb_samples);
done:
*actual_format = output->actual_format;
return 0;
err:
@ -286,6 +294,9 @@ static void output_shutdown(output_t *output) {
output->fifo_pts = 0;
output->mux_dts = 0;
format_init(&output->requested_format);
format_init(&output->actual_format);
}

@ -13,7 +13,7 @@ void output_init(const char *format);
output_t *output_new(const char *filename);
void output_close(output_t *);
int output_config(output_t *output, unsigned int clockrate, unsigned int channels);
int output_config(output_t *output, const format_t *requested_format, format_t *actual_format);
int output_add(output_t *output, AVFrame *frame);

@ -0,0 +1,104 @@
#include "resample.h"
#include <glib.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
#include <libavutil/channel_layout.h>
#include <libavutil/mathematics.h>
#include <inttypes.h>
#include <libavresample/avresample.h>
#include <libavutil/opt.h>
#include <libavutil/frame.h>
#include "log.h"
#include "types.h"
AVFrame *resample_frame(resample_t *resample, AVFrame *frame, const format_t *to_format) {
const char *err;
uint64_t to_channel_layout = av_get_default_channel_layout(to_format->channels);
if (frame->format != to_format->format)
goto resample;
if (frame->sample_rate != to_format->clockrate)
goto resample;
if (frame->channel_layout != to_channel_layout)
goto resample;
return frame;
resample:
if (G_UNLIKELY(!resample->avresample)) {
resample->avresample = avresample_alloc_context();
err = "failed to alloc resample context";
if (!resample->avresample)
goto err;
av_opt_set_int(resample->avresample, "in_channel_layout",
frame->channel_layout, 0);
av_opt_set_int(resample->avresample, "in_sample_fmt",
frame->format, 0);
av_opt_set_int(resample->avresample, "in_sample_rate",
frame->sample_rate, 0);
av_opt_set_int(resample->avresample, "out_channel_layout",
to_channel_layout, 0);
av_opt_set_int(resample->avresample, "out_sample_fmt",
to_format->format, 0);
av_opt_set_int(resample->avresample, "out_sample_rate",
to_format->clockrate, 0);
// av_opt_set_int(dec->avresample, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); // ?
err = "failed to init resample context";
if (avresample_open(resample->avresample) < 0)
goto err;
}
// get a large enough buffer for resampled audio - this should be enough so we don't
// have to loop
int dst_samples = avresample_available(resample->avresample) +
av_rescale_rnd(avresample_get_delay(resample->avresample) + frame->nb_samples,
to_format->clockrate, frame->sample_rate, AV_ROUND_UP);
if (G_UNLIKELY(!resample->swr_frame || resample->swr_buffers < dst_samples)) {
av_frame_free(&resample->swr_frame);
dbg("allocating resampling frame for %i/%i/%i", to_format->format, (int) to_channel_layout,
to_format->clockrate);
resample->swr_frame = av_frame_alloc();
err = "failed to alloc resampling frame";
if (!resample->swr_frame)
goto err;
av_frame_copy_props(resample->swr_frame, frame);
resample->swr_frame->format = to_format->format;
resample->swr_frame->channel_layout = to_channel_layout;
resample->swr_frame->nb_samples = dst_samples;
resample->swr_frame->sample_rate = to_format->clockrate;
err = "failed to get resample buffers";
if (av_frame_get_buffer(resample->swr_frame, 0) < 0)
goto err;
resample->swr_buffers = dst_samples;
}
resample->swr_frame->nb_samples = dst_samples;
int ret_samples = avresample_convert(resample->avresample, resample->swr_frame->extended_data,
resample->swr_frame->linesize[0], dst_samples,
frame->extended_data,
frame->linesize[0], frame->nb_samples);
err = "failed to resample audio";
if (ret_samples < 0)
goto err;
resample->swr_frame->nb_samples = ret_samples;
resample->swr_frame->pts = av_rescale(frame->pts, to_format->clockrate, frame->sample_rate);
return resample->swr_frame;
err:
ilog(LOG_ERR, "Error resampling: %s", err);
return NULL;
}
void resample_shutdown(resample_t *resample) {
av_frame_free(&resample->swr_frame);
avresample_free(&resample->avresample);
}

@ -0,0 +1,13 @@
#ifndef _RESAMPLE_H_
#define _RESAMPLE_H_
#include "types.h"
#include <libavutil/frame.h>
AVFrame *resample_frame(resample_t *resample, AVFrame *frame, const format_t *to_format);
void resample_shutdown(resample_t *resample);
#endif

@ -5,6 +5,8 @@
#include <pthread.h>
#include <sys/types.h>
#include <glib.h>
#include <libavutil/frame.h>
#include <libavresample/avresample.h>
#include "str.h"
@ -24,6 +26,8 @@ struct output_s;
typedef struct output_s output_t;
struct mix_s;
typedef struct mix_s mix_t;
struct resample_s;
typedef struct resample_s resample_t;
typedef void handler_func(handler_t *);
@ -93,4 +97,34 @@ struct metafile_s {
};
struct resample_s {
AVAudioResampleContext *avresample;
AVFrame *swr_frame;
int swr_buffers;
};
struct format_s {
int clockrate;
int channels;
int format; // enum AVSampleFormat
};
typedef struct format_s format_t;
INLINE int format_eq(const format_t *a, const format_t *b) {
if (G_UNLIKELY(a->clockrate != b->clockrate))
return 0;
if (G_UNLIKELY(a->channels != b->channels))
return 0;
if (G_UNLIKELY(a->format != b->format))
return 0;
return 1;
}
INLINE void format_init(format_t *f) {
f->clockrate = -1;
f->channels = -1;
f->format = -1;
}
#endif

Loading…
Cancel
Save