mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 22:25:56 -05:00
Add the ffmpeg transcoding plugin
This commit is contained in:
parent
27f4765408
commit
b03942bfba
33
configure.in
33
configure.in
@ -38,6 +38,7 @@ AC_DEFINE_UNQUOTED(CONFFILE,"${CONFFILE}",Where the config file is)
|
||||
rend_posix=true
|
||||
db_sqlite=false
|
||||
db_sqlite3=false
|
||||
use_ffmpeg=false;
|
||||
|
||||
STATIC_LIBS=no
|
||||
CPPFLAGS="${CPPFLAGS} -g -Wall"
|
||||
@ -98,6 +99,8 @@ AC_ARG_ENABLE(gdbm,[ --enable-gdbm Enable gdbm support],
|
||||
use_gdbm=true;
|
||||
CPPFLAGS="${CPPFLAGS} -DGDBM")
|
||||
|
||||
AC_ARG_ENABLE(ffmpeg,[ --enable-ffmpeg Enable ffmpeg transcode support],
|
||||
use_ffmpeg=true;)
|
||||
|
||||
AM_CONDITIONAL(COND_REND_HOWL, test x$rend_howl = xtrue)
|
||||
AM_CONDITIONAL(COND_REND_POSIX, test x$rend_posix = xtrue)
|
||||
@ -107,9 +110,11 @@ AM_CONDITIONAL(COND_MUSEPACK, test x$use_musepack = xtrue)
|
||||
AM_CONDITIONAL(COND_SQLITE,test x$db_sqlite = xtrue)
|
||||
AM_CONDITIONAL(COND_SQLITE3,test x$db_sqlite3 = xtrue)
|
||||
AM_CONDITIONAL(COND_GDBM,test x$use_gdbm = xtrue)
|
||||
AM_CONDITIONAL(COND_FFMPEG,test x$use_ffmpeg = xtrue)
|
||||
AM_CONDITIONAL(COND_NEED_STRCASESTR,false)
|
||||
AM_CONDITIONAL(COND_NEED_STRSEP,false)
|
||||
|
||||
|
||||
if test x$have_sql = xtrue; then
|
||||
CPPFLAGS="${CPPFLAGS} -DHAVE_SQL"
|
||||
fi
|
||||
@ -226,6 +231,22 @@ AC_ARG_WITH(sqlite3-libs,
|
||||
fi
|
||||
])
|
||||
|
||||
AC_ARG_WITH(ffmpeg-includes,
|
||||
[--with-ffmpeg-includes[[=DIR]] use ffmpeg include files in DIR],[
|
||||
if test "$withval" != "no" -a "$withval" != "yes"; then
|
||||
Z_DIR=$withval
|
||||
CPPFLAGS="${CPPFLAGS} -I$withval"
|
||||
fi
|
||||
])
|
||||
|
||||
AC_ARG_WITH(ffmpeg-libs,
|
||||
[--with-ffmpeg-libs[[=DIR]] use ffmpeg lib files in DIR],[
|
||||
if test "$withval" != "no" -a "$withval" != "yes"; then
|
||||
Z_DIR=$withval
|
||||
LDFLAGS="${LDFLAGS} -L$withval -R$withval"
|
||||
fi
|
||||
])
|
||||
|
||||
AC_ARG_WITH(id3tag,
|
||||
[--with-id3tag[[=DIR]] use id3tag in DIR],[
|
||||
if test "$withval" != "no" -a "$withval" != "yes"; then
|
||||
@ -330,6 +351,18 @@ if test x$use_flac = xtrue; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$use_ffmpeg = xtrue; then
|
||||
AC_CHECK_HEADERS(avcodec.h,, [
|
||||
AC_MSG_ERROR([avcodec.h not found... Must have ffmpeg installed])])
|
||||
dnl AC_CHECK_LIB(avcodec,avcodec_find_decoder,,echo "Must have libavcodec";exit)
|
||||
|
||||
if test x"$STATIC_LIBS" != x"no"; then
|
||||
LIBS="${LIBS} ${STATIC_LIBS}/libavcodec.a ${STATIC_LIBS}/libavformat.a ${STATIC_LIBS}/libavutil.a"
|
||||
else
|
||||
LIBS="${LIBS} -lavcodec -lavformat -lavutil"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x$use_musepack = xtrue; then
|
||||
AC_PATH_PROG(TAGLIB_CONFIG, taglib-config, no)
|
||||
AC_CHECK_HEADERS(taglib/tag_c.h,, [
|
||||
|
@ -1,12 +1,19 @@
|
||||
# $Id: $
|
||||
#
|
||||
rspdir = ${pkgdatadir}/plugins
|
||||
ssc_ffmpegdir = ${pkgdatadir}/plugins
|
||||
|
||||
rsp_LTLIBRARIES=rsp.la
|
||||
rsp_la_LDFLAGS=-module -avoid-version
|
||||
rsp_la_SOURCES = compat.c rsp.c xml-rpc.c
|
||||
|
||||
EXTRA_DIST = compat.h rsp.h mtd-plugins.h xml-rpc.h
|
||||
if COND_FFMPEG
|
||||
ssc_ffmpeg_LTLIBRARIES=ssc-ffmpeg.la
|
||||
ssc_ffmpeg_la_LDFLAGS=-module -avoid-version
|
||||
ssc_ffmpeg_la_SOURCES=ssc-ffmpeg.c
|
||||
endif
|
||||
|
||||
EXTRA_DIST = compat.h rsp.h mtd-plugins.h xml-rpc.h ssc-ffmpeg.c
|
||||
|
||||
AM_CFLAGS = -I..
|
||||
|
||||
|
479
src/plugins/ssc-ffmpeg.c
Normal file
479
src/plugins/ssc-ffmpeg.c
Normal file
@ -0,0 +1,479 @@
|
||||
/*
|
||||
* $Id: $
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <avcodec.h>
|
||||
#include <avformat.h>
|
||||
|
||||
#include "ff-plugins.h"
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
#define BUFFER_SIZE (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3)/2
|
||||
|
||||
typedef struct tag_ssc_handle {
|
||||
AVCodec *pCodec;
|
||||
AVCodecContext *pCodecCtx;
|
||||
AVFormatContext *pFmtCtx;
|
||||
AVFrame *pFrame;
|
||||
AVPacket packet;
|
||||
AVInputFormat *pFormat;
|
||||
|
||||
uint8_t *packet_data;
|
||||
int packet_size;
|
||||
int audio_stream;
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
||||
char *buf_remainder;
|
||||
int buf_remainder_len;
|
||||
int first_frame;
|
||||
|
||||
int total_decoded;
|
||||
int total_written;
|
||||
|
||||
int errno;
|
||||
int swab;
|
||||
|
||||
char *error;
|
||||
|
||||
int raw;
|
||||
FILE *fin;
|
||||
char file_buffer[256];
|
||||
char *file_buffer_ptr;
|
||||
int file_bytes_read;
|
||||
|
||||
char wav_header[44];
|
||||
int wav_offset;
|
||||
} SSCHANDLE;
|
||||
|
||||
#define SSC_FFMPEG_E_SUCCESS 0
|
||||
#define SSC_FFMPEG_E_BADCODEC 1
|
||||
#define SSC_FFMPEG_E_CODECOPEN 2
|
||||
#define SSC_FFMPEG_E_FILEOPEN 3
|
||||
#define SSC_FFMPEG_E_NOSTREAM 4
|
||||
#define SSC_FFMPEG_E_NOAUDIO 5
|
||||
#define SSC_FFMPEG_E_BADFORMAT 6
|
||||
|
||||
|
||||
/* Forwards */
|
||||
void *ssc_ffmpeg_init(void);
|
||||
void ssc_ffmpeg_deinit(void *pv);
|
||||
int ssc_ffmpeg_open(void *pv, char *file, char *codec);
|
||||
int ssc_ffmpeg_close(void *pv);
|
||||
int ssc_ffmpeg_read(void *pv, char *buffer, int len);
|
||||
|
||||
PLUGIN_INFO *plugin_info(void);
|
||||
|
||||
#define infn ((PLUGIN_INPUT_FN *)(_pi.pi))
|
||||
|
||||
/* Globals */
|
||||
PLUGIN_TRANSCODE_FN _ptfn = {
|
||||
ssc_ffmpeg_init,
|
||||
ssc_ffmpeg_deinit,
|
||||
ssc_ffmpeg_open,
|
||||
ssc_ffmpeg_close,
|
||||
ssc_ffmpeg_read
|
||||
};
|
||||
|
||||
PLUGIN_INFO _pi = {
|
||||
PLUGIN_VERSION, /* version */
|
||||
PLUGIN_TRANSCODE, /* type */
|
||||
"ssc-ffmpeg/" VERSION, /* server */
|
||||
NULL, /* url */
|
||||
NULL, /* output fns */
|
||||
NULL, /* event fns */
|
||||
&_ptfn, /* fns */
|
||||
NULL, /* functions exported by ff */
|
||||
NULL, /* rend info */
|
||||
"flac,alac,ogg,wma" /* codeclist */
|
||||
};
|
||||
|
||||
PLUGIN_INFO *plugin_info(void) {
|
||||
av_register_all();
|
||||
|
||||
return &_pi;
|
||||
}
|
||||
|
||||
void *ssc_ffmpeg_init(void) {
|
||||
SSCHANDLE *handle;
|
||||
|
||||
handle=(SSCHANDLE *)malloc(sizeof(SSCHANDLE));
|
||||
if(handle) {
|
||||
memset(handle,0,sizeof(SSCHANDLE));
|
||||
}
|
||||
|
||||
return (void*)handle;
|
||||
}
|
||||
|
||||
void ssc_ffmpeg_deinit(void *vp) {
|
||||
SSCHANDLE *handle = (SSCHANDLE *)vp;
|
||||
ssc_ffmpeg_close(handle);
|
||||
if(handle) {
|
||||
free(handle);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int ssc_ffmpeg_open(void *vp, char *file, char *codec) {
|
||||
int i;
|
||||
enum CodecID id=CODEC_ID_FLAC;
|
||||
SSCHANDLE *handle = (SSCHANDLE*)vp;
|
||||
|
||||
if(!handle)
|
||||
return FALSE;
|
||||
|
||||
handle->first_frame = 1;
|
||||
handle->raw=0;
|
||||
|
||||
infn->log(E_DBG,"opening %s\n",file);
|
||||
|
||||
if(strcasecmp(codec,"flac") == 0) {
|
||||
handle->raw=1;
|
||||
id=CODEC_ID_FLAC;
|
||||
}
|
||||
|
||||
if(handle->raw) {
|
||||
handle->pCodec = avcodec_find_decoder(id);
|
||||
if(!handle->pCodec) {
|
||||
handle->errno = SSC_FFMPEG_E_BADCODEC;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
handle->pCodecCtx = avcodec_alloc_context();
|
||||
if(avcodec_open(handle->pCodecCtx,handle->pCodec) < 0) {
|
||||
handle->errno = SSC_FFMPEG_E_CODECOPEN;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
handle->fin = fopen(file,"rb");
|
||||
if(!handle->fin) {
|
||||
handle->errno = SSC_FFMPEG_E_FILEOPEN;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(av_open_input_file(&handle->pFmtCtx,file,handle->pFormat,0,NULL) < 0) {
|
||||
handle->errno = SSC_FFMPEG_E_FILEOPEN;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* find the streams */
|
||||
if(av_find_stream_info(handle->pFmtCtx) < 0) {
|
||||
handle->errno = SSC_FFMPEG_E_NOSTREAM;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dump_format(handle->pFmtCtx,0,file,FALSE);
|
||||
|
||||
handle->audio_stream = -1;
|
||||
for(i=0; i < handle->pFmtCtx->nb_streams; i++) {
|
||||
if(handle->pFmtCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
|
||||
handle->audio_stream = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(handle->audio_stream == -1) {
|
||||
handle->errno = SSC_FFMPEG_E_NOAUDIO;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
handle->pCodecCtx = handle->pFmtCtx->streams[handle->audio_stream]->codec;
|
||||
|
||||
handle->pCodec = avcodec_find_decoder(handle->pCodecCtx->codec_id);
|
||||
if(!handle->pCodec) {
|
||||
handle->errno = SSC_FFMPEG_E_BADCODEC;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(handle->pCodec->capabilities & CODEC_CAP_TRUNCATED)
|
||||
handle->pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
|
||||
|
||||
if(avcodec_open(handle->pCodecCtx, handle->pCodec) < 0) {
|
||||
handle->errno = SSC_FFMPEG_E_CODECOPEN;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
handle->pFrame = avcodec_alloc_frame();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int ssc_ffmpeg_close(void *vp) {
|
||||
SSCHANDLE *handle = (SSCHANDLE *)vp;
|
||||
|
||||
if(!handle)
|
||||
return TRUE;
|
||||
|
||||
if(handle->fin)
|
||||
fclose(handle->fin);
|
||||
|
||||
if(handle->pFrame)
|
||||
av_free(handle->pFrame);
|
||||
|
||||
if(handle->pCodecCtx)
|
||||
avcodec_close(handle->pCodecCtx);
|
||||
|
||||
if(handle->pFmtCtx)
|
||||
av_close_input_file(handle->pFmtCtx);
|
||||
|
||||
memset(handle,0,sizeof(SSCHANDLE));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
int _ssc_ffmpeg_read_frame(void *vp, char *buffer, int len) {
|
||||
SSCHANDLE *handle = (SSCHANDLE *)vp;
|
||||
int data_size;
|
||||
int len1;
|
||||
int out_size;
|
||||
|
||||
if(handle->raw) {
|
||||
while(1) {
|
||||
if(!handle->file_bytes_read) {
|
||||
/* need to grab a new chunk */
|
||||
handle->file_buffer_ptr = handle->file_buffer;
|
||||
handle->file_bytes_read = fread(handle->file_buffer,
|
||||
1, sizeof(handle->file_buffer),
|
||||
handle->fin);
|
||||
handle->file_buffer_ptr = handle->file_buffer;
|
||||
}
|
||||
|
||||
if(!handle->file_bytes_read)
|
||||
return 0;
|
||||
|
||||
len1 = avcodec_decode_audio(handle->pCodecCtx,(short*)buffer,
|
||||
&out_size,
|
||||
(uint8_t*)handle->file_buffer_ptr,
|
||||
handle->file_bytes_read);
|
||||
|
||||
if(len1 < 0) /* FIXME: Decode Error */
|
||||
return 0;
|
||||
|
||||
handle->file_bytes_read -= len1;
|
||||
handle->file_buffer_ptr += len1;
|
||||
|
||||
if(out_size > 0) {
|
||||
return out_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(handle->first_frame) {
|
||||
handle->first_frame = 0;
|
||||
handle->packet.data = NULL;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
while(handle->packet_size > 0) {
|
||||
len1=avcodec_decode_audio(handle->pCodecCtx,
|
||||
(int16_t*)buffer,
|
||||
&data_size,
|
||||
handle->packet_data,
|
||||
handle->packet_size);
|
||||
|
||||
if(len1 < 0) {
|
||||
/* skip frame */
|
||||
handle->packet_size=0;
|
||||
break;
|
||||
}
|
||||
|
||||
handle->packet_data += len1;
|
||||
handle->packet_size -= len1;
|
||||
|
||||
if(data_size <= 0)
|
||||
continue;
|
||||
|
||||
handle->total_decoded += data_size;
|
||||
return data_size;
|
||||
}
|
||||
|
||||
do {
|
||||
if(handle->packet.data)
|
||||
av_free_packet(&handle->packet);
|
||||
|
||||
if(av_read_packet(handle->pFmtCtx, &handle->packet) < 0)
|
||||
return -1;
|
||||
} while(handle->packet.stream_index != handle->audio_stream);
|
||||
|
||||
handle->packet_size = handle->packet.size;
|
||||
handle->packet_data = handle->packet.data;
|
||||
}
|
||||
}
|
||||
|
||||
void _ssc_ffmpeg_swab(char *buffer, int bytes_returned) {
|
||||
int blocks = bytes_returned / 2;
|
||||
int index;
|
||||
char tmp;
|
||||
|
||||
for(index = 0; index < blocks; index++) {
|
||||
tmp = buffer[index*2];
|
||||
buffer[index*2] = buffer[index*2 + 1];
|
||||
buffer[index*2 + 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void _ssc_ffmpeg_le32(char *dst, int value) {
|
||||
dst[0] = value & 0xFF;
|
||||
dst[1] = (value >> 8) & 0xFF;
|
||||
dst[2] = (value >> 16) & 0xFF;
|
||||
dst[3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
void _ssc_ffmpeg_le16(char *dst, int value) {
|
||||
dst[0] = value & 0xFF;
|
||||
dst[1] = (value >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
int ssc_ffmpeg_read(void *vp, char *buffer, int len) {
|
||||
SSCHANDLE *handle = (SSCHANDLE *)vp;
|
||||
int bytes_returned = 0;
|
||||
int bytes_to_copy;
|
||||
int size;
|
||||
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int bits_per_sample;
|
||||
int byte_rate;
|
||||
int duration = 180000; /* in ms -- 3 min */
|
||||
int data_len;
|
||||
int block_align;
|
||||
uint16_t test1 = 0xaabb;
|
||||
char test2[2] = { 0xaa, 0xbb };
|
||||
|
||||
/* if we have not yet sent the header, let's do that first */
|
||||
if(handle->wav_offset != sizeof(handle->wav_header)) {
|
||||
/* still have some to send */
|
||||
if(!handle->wav_offset) {
|
||||
/* generate the wav header */
|
||||
if(handle->raw) {
|
||||
channels = 2;
|
||||
sample_rate = 44100;
|
||||
bits_per_sample = 16;
|
||||
} else {
|
||||
channels = handle->pCodecCtx->channels;
|
||||
sample_rate = handle->pCodecCtx->sample_rate;
|
||||
switch(handle->pCodecCtx->sample_fmt) {
|
||||
case SAMPLE_FMT_U8:
|
||||
bits_per_sample = 8;
|
||||
break;
|
||||
case SAMPLE_FMT_S16:
|
||||
bits_per_sample = 16;
|
||||
break;
|
||||
case SAMPLE_FMT_S24:
|
||||
/* BROKEN */
|
||||
bits_per_sample = 24;
|
||||
break;
|
||||
case SAMPLE_FMT_S32:
|
||||
/* BROKEN */
|
||||
bits_per_sample = 32;
|
||||
break;
|
||||
default:
|
||||
bits_per_sample = 16;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handle->swab = (bits_per_sample == 16) &&
|
||||
(memcmp((void*)&test1,test2,2) == 0);
|
||||
|
||||
data_len = (bits_per_sample * sample_rate * channels * (duration/1000));
|
||||
byte_rate = sample_rate * channels * bits_per_sample / 8;
|
||||
block_align = channels * bits_per_sample / 8;
|
||||
|
||||
infn->log(E_DBG,"Channels.......: %d\n",channels);
|
||||
infn->log(E_DBG,"Sample rate....: %d\n",sample_rate);
|
||||
infn->log(E_DBG,"Bits/Sample....: %d\n",bits_per_sample);
|
||||
infn->log(E_DBG,"Swab...........: %d\n",handle->swab);
|
||||
|
||||
memcpy(&handle->wav_header[0],"RIFF",4);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[4],36 + data_len);
|
||||
memcpy(&handle->wav_header[8],"WAVE",4);
|
||||
memcpy(&handle->wav_header[12],"fmt ",4);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[16],16);
|
||||
_ssc_ffmpeg_le16(&handle->wav_header[20],1);
|
||||
_ssc_ffmpeg_le16(&handle->wav_header[22],channels);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[24],sample_rate);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[28],byte_rate);
|
||||
_ssc_ffmpeg_le16(&handle->wav_header[32],block_align);
|
||||
_ssc_ffmpeg_le16(&handle->wav_header[34],bits_per_sample);
|
||||
memcpy(&handle->wav_header[36],"data",4);
|
||||
_ssc_ffmpeg_le32(&handle->wav_header[40],data_len);
|
||||
}
|
||||
|
||||
bytes_to_copy = sizeof(handle->wav_header) - handle->wav_offset;
|
||||
if(len < bytes_to_copy)
|
||||
bytes_to_copy = len;
|
||||
|
||||
memcpy(buffer,&handle->wav_header[handle->wav_offset],bytes_to_copy);
|
||||
handle->wav_offset += bytes_to_copy;
|
||||
return bytes_to_copy;
|
||||
}
|
||||
|
||||
/* could test for good len here */
|
||||
|
||||
/* otherwise, start pumping out data */
|
||||
if(handle->buf_remainder_len) {
|
||||
/* dump remainder into the buffer */
|
||||
bytes_to_copy = handle->buf_remainder_len;
|
||||
if(handle->buf_remainder_len > len) {
|
||||
bytes_to_copy = len;
|
||||
}
|
||||
|
||||
memcpy(buffer,handle->buf_remainder,bytes_to_copy);
|
||||
bytes_returned = bytes_to_copy;
|
||||
handle->buf_remainder_len -= bytes_to_copy;
|
||||
if(handle->buf_remainder_len) {
|
||||
handle->buf_remainder += bytes_returned;
|
||||
}
|
||||
}
|
||||
|
||||
/* keep reading until we have filled the output buffer */
|
||||
while(bytes_returned < len) {
|
||||
size = _ssc_ffmpeg_read_frame(handle,handle->buffer,BUFFER_SIZE);
|
||||
if(size == 0) {
|
||||
/* oops, we're done */
|
||||
if(handle->swab)
|
||||
_ssc_ffmpeg_swab(buffer,bytes_returned);
|
||||
return bytes_returned;
|
||||
}
|
||||
|
||||
if(size < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes_to_copy = len - bytes_returned;
|
||||
if(size < bytes_to_copy)
|
||||
bytes_to_copy = size;
|
||||
|
||||
memcpy(buffer + bytes_returned, handle->buffer, bytes_to_copy);
|
||||
bytes_returned += bytes_to_copy;
|
||||
|
||||
if(size > bytes_to_copy) {
|
||||
handle->buf_remainder = handle->buffer + bytes_to_copy;
|
||||
handle->buf_remainder_len = size - bytes_to_copy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(handle->swab)
|
||||
_ssc_ffmpeg_swab(buffer,bytes_returned);
|
||||
|
||||
return bytes_returned;
|
||||
}
|
Loading…
Reference in New Issue
Block a user