Add the ffmpeg transcoding plugin

This commit is contained in:
Ron Pedde 2006-05-27 10:33:08 +00:00
parent 27f4765408
commit b03942bfba
3 changed files with 520 additions and 1 deletions

View File

@ -38,6 +38,7 @@ AC_DEFINE_UNQUOTED(CONFFILE,"${CONFFILE}",Where the config file is)
rend_posix=true rend_posix=true
db_sqlite=false db_sqlite=false
db_sqlite3=false db_sqlite3=false
use_ffmpeg=false;
STATIC_LIBS=no STATIC_LIBS=no
CPPFLAGS="${CPPFLAGS} -g -Wall" CPPFLAGS="${CPPFLAGS} -g -Wall"
@ -98,6 +99,8 @@ AC_ARG_ENABLE(gdbm,[ --enable-gdbm Enable gdbm support],
use_gdbm=true; use_gdbm=true;
CPPFLAGS="${CPPFLAGS} -DGDBM") 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_HOWL, test x$rend_howl = xtrue)
AM_CONDITIONAL(COND_REND_POSIX, test x$rend_posix = 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_SQLITE,test x$db_sqlite = xtrue)
AM_CONDITIONAL(COND_SQLITE3,test x$db_sqlite3 = xtrue) AM_CONDITIONAL(COND_SQLITE3,test x$db_sqlite3 = xtrue)
AM_CONDITIONAL(COND_GDBM,test x$use_gdbm = 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_STRCASESTR,false)
AM_CONDITIONAL(COND_NEED_STRSEP,false) AM_CONDITIONAL(COND_NEED_STRSEP,false)
if test x$have_sql = xtrue; then if test x$have_sql = xtrue; then
CPPFLAGS="${CPPFLAGS} -DHAVE_SQL" CPPFLAGS="${CPPFLAGS} -DHAVE_SQL"
fi fi
@ -226,6 +231,22 @@ AC_ARG_WITH(sqlite3-libs,
fi 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, AC_ARG_WITH(id3tag,
[--with-id3tag[[=DIR]] use id3tag in DIR],[ [--with-id3tag[[=DIR]] use id3tag in DIR],[
if test "$withval" != "no" -a "$withval" != "yes"; then if test "$withval" != "no" -a "$withval" != "yes"; then
@ -330,6 +351,18 @@ if test x$use_flac = xtrue; then
fi fi
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 if test x$use_musepack = xtrue; then
AC_PATH_PROG(TAGLIB_CONFIG, taglib-config, no) AC_PATH_PROG(TAGLIB_CONFIG, taglib-config, no)
AC_CHECK_HEADERS(taglib/tag_c.h,, [ AC_CHECK_HEADERS(taglib/tag_c.h,, [

View File

@ -1,12 +1,19 @@
# $Id: $ # $Id: $
# #
rspdir = ${pkgdatadir}/plugins rspdir = ${pkgdatadir}/plugins
ssc_ffmpegdir = ${pkgdatadir}/plugins
rsp_LTLIBRARIES=rsp.la rsp_LTLIBRARIES=rsp.la
rsp_la_LDFLAGS=-module -avoid-version rsp_la_LDFLAGS=-module -avoid-version
rsp_la_SOURCES = compat.c rsp.c xml-rpc.c 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.. AM_CFLAGS = -I..

479
src/plugins/ssc-ffmpeg.c Normal file
View 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;
}