- Added LAME mp3 encode module
- Redesigned encode/decode API - misc. changes and debugging
This commit is contained in:
21
Makefile.am
21
Makefile.am
@@ -1,7 +1,18 @@
|
|||||||
bin_PROGRAMS=tcfs test_encode
|
bin_PROGRAMS=tcfs
|
||||||
tcfs_SOURCES=src/tcfs.c src/transcode.c src/decode.c src/encode.c src/util.c src/tcfs_buf.c src/decode-ffmpeg.c src/encode-ffmpeg.c
|
|
||||||
|
tcfs_SOURCES = src/tcfs.c src/transcode.c src/decode.c src/encode.c src/util.c src/tcfs_buf.c src/decode-ffmpeg.c src/encode-ffmpeg.c
|
||||||
|
if MP3LAME
|
||||||
|
tcfs_SOURCES += src/encode-mp3lame.c
|
||||||
|
endif
|
||||||
tcfs_LDADD = $(LIBOBJS) $(FUSE_LIBS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS)
|
tcfs_LDADD = $(LIBOBJS) $(FUSE_LIBS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS)
|
||||||
|
if MP3LAME
|
||||||
|
tcfs_LDADD += -lmp3lame
|
||||||
|
endif
|
||||||
tcfs_CFLAGS = $(FUSE_CFLAGS) $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS) -DFUSE_USE_VERSION=28
|
tcfs_CFLAGS = $(FUSE_CFLAGS) $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS) -DFUSE_USE_VERSION=28
|
||||||
test_encode_SOURCES=src/decode.c src/encode.c src/tcfs_buf.c src/util.c src/test_encode.c src/decode-ffmpeg.c src/encode-ffmpeg.c
|
if MP3LAME
|
||||||
test_encode_LDADD = $(LIBOBJS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS)
|
tcfs_CFLAGS += -I/usr/include/lame -DHAVE_LIBMP3LAME
|
||||||
test_encode_CFLAGS = $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS)
|
endif
|
||||||
|
|
||||||
|
#test_encode_SOURCES = src/decode.c src/encode.c src/tcfs_buf.c src/util.c src/test_encode.c src/decode-ffmpeg.c src/encode-ffmpeg.c
|
||||||
|
#test_encode_LDADD = $(LIBOBJS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS)
|
||||||
|
#test_encode_CFLAGS = $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS)
|
||||||
13
configure.ac
13
configure.ac
@@ -16,9 +16,11 @@ PKG_CHECK_MODULES([libavcodec], [libavcodec >= 52.94])
|
|||||||
PKG_CHECK_MODULES([libavformat], [libavformat >= 52.84])
|
PKG_CHECK_MODULES([libavformat], [libavformat >= 52.84])
|
||||||
PKG_CHECK_MODULES([libavcore], [libavcore >= 0.12])
|
PKG_CHECK_MODULES([libavcore], [libavcore >= 0.12])
|
||||||
PKG_CHECK_MODULES([libavutil], [libavutil >= 50.32])
|
PKG_CHECK_MODULES([libavutil], [libavutil >= 50.32])
|
||||||
|
PKG_CHECK_MODULES([taglib_c], [taglib_c >= 1.7])
|
||||||
|
|
||||||
|
|
||||||
# Checks for header files.
|
# Checks for header files.
|
||||||
AC_CHECK_HEADERS([fcntl.h stddef.h stdlib.h string.h errno.h dirent.h])
|
AC_CHECK_HEADERS([fcntl.h stddef.h stdlib.h string.h errno.h dirent.h assert.h])
|
||||||
|
|
||||||
# Checks for typedefs, structures, and compiler characteristics.
|
# Checks for typedefs, structures, and compiler characteristics.
|
||||||
AC_PROG_CC_C99
|
AC_PROG_CC_C99
|
||||||
@@ -29,5 +31,14 @@ AX_TLS
|
|||||||
AC_CHECK_FUNCS([memset strerror])
|
AC_CHECK_FUNCS([memset strerror])
|
||||||
AC_CHECK_FUNCS([asprintf], [], [AC_MSG_ERROR([asprintf emulation for non-GNU systems NYI])])
|
AC_CHECK_FUNCS([asprintf], [], [AC_MSG_ERROR([asprintf emulation for non-GNU systems NYI])])
|
||||||
|
|
||||||
|
# Check for optional encoders
|
||||||
|
# libmp3lame
|
||||||
|
AC_ARG_ENABLE(mp3lame, [ --enable-mp3lame Enable support for MP3 encoding with libmp3lame],
|
||||||
|
[AC_CHECK_LIB(mp3lame, get_lame_version, [mp3lame=true], [AC_MSG_WARN([libmp3lame not found]);mp3lame=false])]
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
AM_CONDITIONAL([MP3LAME], [test x$mp3lame = xtrue])
|
||||||
|
|
||||||
|
# finish
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES([Makefile])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|||||||
@@ -58,6 +58,10 @@
|
|||||||
# include <dirent.h>
|
# include <dirent.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ASSERT_H
|
||||||
|
# include <assert.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <libavcore/avcore.h>
|
#include <libavcore/avcore.h>
|
||||||
#include <libavutil/avutil.h>
|
#include <libavutil/avutil.h>
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ struct decode_ffmpeg_ctx
|
|||||||
AVFormatContext *avfctx;
|
AVFormatContext *avfctx;
|
||||||
int stream;
|
int stream;
|
||||||
AVCodec *avc;
|
AVCodec *avc;
|
||||||
char *bod;
|
int16_t *bod;
|
||||||
int len, offs;
|
int len, offs;
|
||||||
void *metadata;
|
void *metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct decode_ctx *decode_ffmpeg_open(const char *src);
|
struct decode_ctx *decode_ffmpeg_open(const char *src, struct source_info *src_info);
|
||||||
int decode_ffmpeg_read(void *buf, size_t size, struct decode_ctx *fd);
|
int decode_ffmpeg_read(void *buf, size_t size, struct decode_ctx *fd);
|
||||||
int decode_ffmpeg_close(struct decode_ctx *fd);
|
int decode_ffmpeg_close(struct decode_ctx *fd);
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ struct decode_ops decode_ffmpeg_ops = {
|
|||||||
decode_ffmpeg_close
|
decode_ffmpeg_close
|
||||||
};
|
};
|
||||||
|
|
||||||
struct decode_ctx *decode_ffmpeg_open(const char *src)
|
struct decode_ctx *decode_ffmpeg_open(const char *src, struct source_info *src_info)
|
||||||
{
|
{
|
||||||
struct decode_ctx *fd = (struct decode_ctx*)malloc(sizeof(struct decode_ctx)+sizeof(struct decode_ffmpeg_ctx));
|
struct decode_ctx *fd = (struct decode_ctx*)malloc(sizeof(struct decode_ctx)+sizeof(struct decode_ffmpeg_ctx));
|
||||||
if(!fd)
|
if(!fd)
|
||||||
@@ -105,24 +105,28 @@ struct decode_ctx *decode_ffmpeg_open(const char *src)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->bod = malloc(128*1024*1024);
|
ctx->bod = (int16_t*)malloc(128*1024*1024);
|
||||||
if(!ctx->bod) {
|
if(!ctx->bod) {
|
||||||
tcfs_errno = ENOMEM;
|
tcfs_errno = ENOMEM;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
src_info->sample_rate = ctx->avfctx->streams[ctx->stream]->codec->sample_rate;
|
||||||
|
src_info->channels = ctx->avfctx->streams[ctx->stream]->codec->channels;
|
||||||
|
src_info->depth = 16;
|
||||||
|
|
||||||
AVPacket pack;
|
AVPacket pack;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int fsp = 128*1024*1024;
|
int fsp = 128*1024*1024;
|
||||||
while(av_read_frame(ctx->avfctx, &pack) == 0) {
|
while(av_read_frame(ctx->avfctx, &pack) == 0) {
|
||||||
if(pack.stream_index == ctx->stream) {
|
if(pack.stream_index == ctx->stream) {
|
||||||
if(avcodec_decode_audio3(ctx->avfctx->streams[ctx->stream]->codec,
|
if(avcodec_decode_audio3(ctx->avfctx->streams[ctx->stream]->codec,
|
||||||
(int16_t*)(ctx->bod+pos), &fsp, &pack) < 0) {
|
ctx->bod+pos, &fsp, &pack) < 0) {
|
||||||
printf("Decode error.\n");
|
printf("Decode error.\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
pos += fsp;
|
pos += fsp/sizeof(int16_t);
|
||||||
fsp = 128*1024*1024-pos;
|
fsp = 128*1024*1024-pos*sizeof(int16_t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx->len = pos;
|
ctx->len = pos;
|
||||||
@@ -140,12 +144,14 @@ int decode_ffmpeg_read(void *buf, size_t size, struct decode_ctx *fd)
|
|||||||
|
|
||||||
struct decode_ffmpeg_ctx *ctx = (struct decode_ffmpeg_ctx*)(fd+1);
|
struct decode_ffmpeg_ctx *ctx = (struct decode_ffmpeg_ctx*)(fd+1);
|
||||||
|
|
||||||
if(size < ctx->len-ctx->offs) {
|
if(ctx->offs == ctx->len) { // EOF
|
||||||
memcpy(buf, ctx->bod+ctx->offs, size);
|
return 0;
|
||||||
|
} else if(size < ctx->len-ctx->offs) {
|
||||||
|
memcpy(buf, ctx->bod+ctx->offs, size*sizeof(int16_t));
|
||||||
ctx->offs += size;
|
ctx->offs += size;
|
||||||
return size;
|
return size;
|
||||||
} else {
|
} else {
|
||||||
memcpy(buf, ctx->bod+ctx->offs, ctx->len-ctx->offs);
|
memcpy(buf, ctx->bod+ctx->offs, (ctx->len-ctx->offs)*sizeof(int16_t));
|
||||||
int ret = ctx->len - ctx->offs;
|
int ret = ctx->len - ctx->offs;
|
||||||
ctx->offs = ctx->len;
|
ctx->offs = ctx->len;
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -9,17 +9,18 @@
|
|||||||
#include "decode.h"
|
#include "decode.h"
|
||||||
#include "decode-ffmpeg.h"
|
#include "decode-ffmpeg.h"
|
||||||
|
|
||||||
struct decoders_entry decoders[] = {
|
const struct decoders_entry decoders[] = {
|
||||||
{&decode_ffmpeg_ops, "ffmpeg"},
|
{&decode_ffmpeg_ops, "ffmpeg"},
|
||||||
{NULL, ""}
|
{NULL, ""}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct decode_ctx *decode_open(const char *src)
|
struct decode_ctx *decode_open(const char *src, struct source_info *src_info)
|
||||||
{
|
{
|
||||||
struct decode_ctx *ctx;
|
struct decode_ctx *ctx;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(decoders[i].ops) {
|
while(decoders[i].ops) {
|
||||||
ctx = decoders[i++].ops->open(src);
|
assert(decoders[i].ops->open != NULL);
|
||||||
|
ctx = decoders[i++].ops->open(src, src_info);
|
||||||
if(!ctx) { // open failed
|
if(!ctx) { // open failed
|
||||||
if(tcfs_errno == ECODEC) // the codec doesn't support this file, try the next one
|
if(tcfs_errno == ECODEC) // the codec doesn't support this file, try the next one
|
||||||
continue;
|
continue;
|
||||||
@@ -33,7 +34,7 @@ struct decode_ctx *decode_open(const char *src)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int decode_read(char *buf, size_t size, struct decode_ctx *fd)
|
int decode_read(void *buf, size_t size, struct decode_ctx *fd)
|
||||||
{
|
{
|
||||||
if(!fd) {
|
if(!fd) {
|
||||||
tcfs_errno = EINVAL;
|
tcfs_errno = EINVAL;
|
||||||
|
|||||||
12
src/decode.h
12
src/decode.h
@@ -7,25 +7,27 @@
|
|||||||
#ifndef TCFS_DECODE_H
|
#ifndef TCFS_DECODE_H
|
||||||
#define TCFS_DECODE_H 1
|
#define TCFS_DECODE_H 1
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
struct decode_ctx {
|
struct decode_ctx {
|
||||||
struct decode_ops *ops;
|
struct decode_ops *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct decode_ops {
|
struct decode_ops {
|
||||||
struct decode_ctx *(*open)(const char *src);
|
struct decode_ctx *(*open)(const char *src, struct source_info *src_info);
|
||||||
int (*read)(void *buf, size_t size, struct decode_ctx *fd);
|
int (*read)(void *buf, size_t size, struct decode_ctx *fd);
|
||||||
int (*close)(struct decode_ctx *fd);
|
int (*close)(struct decode_ctx *fd);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct decoders_entry {
|
struct decoders_entry {
|
||||||
struct decode_ops *ops;
|
struct decode_ops *ops;
|
||||||
char name[];
|
char name[TCFS_MAX_NAME_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct decoders_entry decoders[];
|
extern const struct decoders_entry decoders[];
|
||||||
|
|
||||||
struct decode_ctx *decode_open(const char *src);
|
struct decode_ctx *decode_open(const char *src, struct source_info *src_info);
|
||||||
int decode_read(char *buf, size_t size, struct decode_ctx *fd);
|
int decode_read(void *buf, size_t size, struct decode_ctx *fd);
|
||||||
int decode_close(struct decode_ctx *fd);
|
int decode_close(struct decode_ctx *fd);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,33 +9,136 @@
|
|||||||
#include "encode.h"
|
#include "encode.h"
|
||||||
#include "encode-ffmpeg.h"
|
#include "encode-ffmpeg.h"
|
||||||
|
|
||||||
struct encode_ffmpeg_ctx
|
struct ffmpeg_formats {
|
||||||
{
|
char name[32];
|
||||||
|
char codec[32];
|
||||||
|
char format[32];
|
||||||
|
int raw;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encode_ctx *encode_ffmpeg_open(const char *tfmt);
|
struct encode_ffmpeg_ctx
|
||||||
int encode_ffmpeg_encode(void *dst, size_t dst_size, const void *src, size_t src_size, struct encode_ctx *ctx);
|
{
|
||||||
int encode_ffmpeg_close(struct encode_ctx *ctx);
|
struct ffmpeg_formats format;
|
||||||
|
AVCodecContext *codecctx;
|
||||||
|
AVCodec *codec;
|
||||||
|
AVFormatContext *formatctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encode_ctx *encode_ffmpeg_init(struct decode_ctx *src, const char *tfmt, const struct source_info *src_info);
|
||||||
|
int encode_ffmpeg_encode(void **dst, size_t *dst_size, struct encode_ctx *fd);
|
||||||
|
int encode_ffmpeg_close(struct encode_ctx *fd);
|
||||||
|
|
||||||
struct encode_ops encode_ffmpeg_ops = {
|
struct encode_ops encode_ffmpeg_ops = {
|
||||||
encode_ffmpeg_open,
|
encode_ffmpeg_init,
|
||||||
encode_ffmpeg_encode,
|
encode_ffmpeg_encode,
|
||||||
encode_ffmpeg_close
|
encode_ffmpeg_close
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encode_ctx *encode_ffmpeg_open(const char *tfmt)
|
// special handling instructions for certain encoders
|
||||||
|
static struct ffmpeg_formats ffmpeg_formats[] = {
|
||||||
|
{"mp3", "libmp3lame", "", 1},
|
||||||
|
{"ogg", "libvorbis", "ogg", 0},
|
||||||
|
{"flac", "flac", "ogg", 0},
|
||||||
|
{"", "", 0} // END
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encode_ctx *encode_ffmpeg_init(struct decode_ctx *src, const char *tfmt, const struct source_info *src_info)
|
||||||
{
|
{
|
||||||
tcfs_errno = ENOSYS;
|
struct encode_ctx *fd = (struct encode_ctx*)malloc(sizeof(struct encode_ctx)+sizeof(struct encode_ffmpeg_ctx));
|
||||||
|
if(!fd) {
|
||||||
|
tcfs_errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_ffmpeg_ctx *ctx = (struct encode_ffmpeg_ctx*)(fd+1);
|
||||||
|
|
||||||
|
fd->src = src;
|
||||||
|
fd->ops = &encode_ffmpeg_ops;
|
||||||
|
|
||||||
|
int i = 0, found = 0;
|
||||||
|
while(ffmpeg_formats[i].name[0]) {
|
||||||
|
if(strcmp(tfmt, ffmpeg_formats[i].name) == 0) {
|
||||||
|
memcpy(&ctx->format, ffmpeg_formats+i, sizeof(struct ffmpeg_formats));
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try default settings
|
||||||
|
if(!found) {
|
||||||
|
strncpy(ctx->format.name, tfmt, 32);
|
||||||
|
strncpy(ctx->format.codec, tfmt, 32);
|
||||||
|
strncpy(ctx->format.format, tfmt, 32);
|
||||||
|
ctx->format.raw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ctx->format.raw) {
|
||||||
|
ctx->formatctx = NULL;
|
||||||
|
ctx->codec = avcodec_find_encoder_by_name(ctx->format.codec);
|
||||||
|
if(!ctx->codec) {
|
||||||
|
tcfs_errno = ECODEC;
|
||||||
|
free(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->codecctx = (AVCodecContext*)malloc(sizeof(AVCodecContext));
|
||||||
|
if(!ctx->codecctx) {
|
||||||
|
free(fd);
|
||||||
|
tcfs_errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
avcodec_get_context_defaults3(ctx->codecctx, ctx->codec);
|
||||||
|
|
||||||
|
//avcodec_thread_init(st->codec, 1);
|
||||||
|
|
||||||
|
ctx->codecctx->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||||
|
ctx->codecctx->channels = 2;
|
||||||
|
ctx->codecctx->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||||
|
ctx->codecctx->sample_rate = 44100;
|
||||||
|
ctx->codecctx->channel_layout = 0;
|
||||||
|
// ctx->codecctx->flags |= CODEC_FLAG_QSCALE;
|
||||||
|
// ctx->codecctx->global_quality = 5;
|
||||||
|
ctx->codecctx->bit_rate = 160000;
|
||||||
|
ctx->codecctx->time_base = (AVRational){1, 44100};
|
||||||
|
ctx->codecctx->compression_level = FF_COMPRESSION_DEFAULT;
|
||||||
|
|
||||||
|
if(avcodec_open(ctx->codecctx, ctx->codec) < 0) {
|
||||||
|
free(fd);
|
||||||
|
tcfs_errno = ECODEC;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tcfs_errno = ENOSYS;
|
||||||
|
free(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int encode_ffmpeg_encode(void *dst, size_t dst_size, const void *src, size_t src_size, struct encode_ctx *ctx)
|
int encode_ffmpeg_encode(void **dst, size_t *dst_size, struct encode_ctx *fd)
|
||||||
{
|
{
|
||||||
|
if(!fd || (fd->ops != &encode_ffmpeg_ops)) {
|
||||||
|
tcfs_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_ffmpeg_ctx *ctx = (struct encode_ffmpeg_ctx*)(fd+1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int encode_ffmpeg_close(struct encode_ctx *ctx)
|
int encode_ffmpeg_close(struct encode_ctx *fd)
|
||||||
{
|
{
|
||||||
|
if(!fd || (fd->ops != &encode_ffmpeg_ops)) {
|
||||||
|
tcfs_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_ffmpeg_ctx *ctx = (struct encode_ffmpeg_ctx*)(fd+1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
127
src/encode-mp3lame.c
Normal file
127
src/encode-mp3lame.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010-2011 Matthias Blankertz <matthias@blankertz.org>
|
||||||
|
|
||||||
|
This work is licensed under the Open Software License ("OSL") v. 3.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include <lame.h>
|
||||||
|
#include "util.h"
|
||||||
|
#include "encode.h"
|
||||||
|
#include "encode-mp3lame.h"
|
||||||
|
|
||||||
|
struct encode_ctx *encode_mp3lame_init(struct decode_ctx *src, const char *tfmt, const struct source_info *src_info);
|
||||||
|
int encode_mp3lame_encode(void **dst, size_t *dst_size, struct encode_ctx *fd);
|
||||||
|
int encode_mp3lame_close(struct encode_ctx *fd);
|
||||||
|
|
||||||
|
struct encode_ops encode_mp3lame_ops = {
|
||||||
|
encode_mp3lame_init,
|
||||||
|
encode_mp3lame_encode,
|
||||||
|
encode_mp3lame_close
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encode_mp3lame_ctx
|
||||||
|
{
|
||||||
|
lame_global_flags *gfp;
|
||||||
|
struct source_info src_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct encode_ctx *encode_mp3lame_init(struct decode_ctx *src, const char *tfmt, const struct source_info* src_info)
|
||||||
|
{
|
||||||
|
assert(tfmt != NULL);
|
||||||
|
assert(src_info != NULL);
|
||||||
|
|
||||||
|
if(strcmp(tfmt, "mp3") != 0) {
|
||||||
|
tcfs_errno = ECODEC;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_ctx *fd = (struct encode_ctx*)malloc(sizeof(struct encode_ctx)+sizeof(struct encode_mp3lame_ctx));
|
||||||
|
if(!fd) {
|
||||||
|
tcfs_errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_mp3lame_ctx *ctx = (struct encode_mp3lame_ctx*)(fd+1);
|
||||||
|
|
||||||
|
fd->src = src;
|
||||||
|
fd->ops = &encode_mp3lame_ops;
|
||||||
|
ctx->src_info = *src_info;
|
||||||
|
|
||||||
|
ctx->gfp = lame_init();
|
||||||
|
if(!ctx->gfp) {
|
||||||
|
free(fd);
|
||||||
|
tcfs_errno = ENOMEM;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(src_info->samples != 0)
|
||||||
|
lame_set_num_samples(ctx->gfp, src_info->samples);
|
||||||
|
if(src_info->sample_rate != 0)
|
||||||
|
lame_set_in_samplerate(ctx->gfp, src_info->sample_rate);
|
||||||
|
|
||||||
|
lame_set_quality(ctx->gfp, 2);
|
||||||
|
lame_set_VBR(ctx->gfp, vbr_mtrh);
|
||||||
|
lame_set_VBR_q(ctx->gfp, 3);
|
||||||
|
lame_set_strict_ISO(ctx->gfp, 1);
|
||||||
|
|
||||||
|
if(lame_init_params(ctx->gfp) < 0) {
|
||||||
|
tcfs_errno = EINVAL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int encode_mp3lame_encode(void **dst, size_t *dst_size, struct encode_ctx *fd)
|
||||||
|
{
|
||||||
|
if(!fd || (fd->ops != &encode_mp3lame_ops)) {
|
||||||
|
tcfs_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_mp3lame_ctx *ctx = (struct encode_mp3lame_ctx*)(fd+1);
|
||||||
|
|
||||||
|
int dst_bufsize = 1.25*ctx->src_info.samples+7200;
|
||||||
|
int dst_pos = 0;
|
||||||
|
char *dst_buffer = (char*)malloc(dst_bufsize);
|
||||||
|
if(!dst_buffer) {
|
||||||
|
tcfs_errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t *src_buffer = (int16_t*)malloc(1024*ctx->src_info.channels*sizeof(int16_t));
|
||||||
|
|
||||||
|
int dec_ret;
|
||||||
|
while((dec_ret = decode_read((void*)src_buffer, 1024*ctx->src_info.channels, fd->src)) > 0) {
|
||||||
|
int enc_ret = lame_encode_buffer_interleaved(ctx->gfp, src_buffer, dec_ret/ctx->src_info.channels, dst_buffer+dst_pos, dst_bufsize-dst_pos);
|
||||||
|
if(enc_ret < 0) {
|
||||||
|
tcfs_errno = ECODEC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dst_pos += enc_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int enc_ret = lame_encode_flush(ctx->gfp, dst_buffer+dst_pos, dst_bufsize-dst_pos);
|
||||||
|
|
||||||
|
dst_pos += enc_ret;
|
||||||
|
|
||||||
|
dst_buffer = (char*)realloc((void*)dst_buffer, dst_pos);
|
||||||
|
|
||||||
|
*dst = dst_buffer;
|
||||||
|
*dst_size = dst_pos;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int encode_mp3lame_close(struct encode_ctx *fd)
|
||||||
|
{
|
||||||
|
if(!fd || (fd->ops != &encode_mp3lame_ops)) {
|
||||||
|
tcfs_errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct encode_mp3lame_ctx *ctx = (struct encode_mp3lame_ctx*)(fd+1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
14
src/encode-mp3lame.h
Normal file
14
src/encode-mp3lame.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
|
||||||
|
|
||||||
|
This work is licensed under the Open Software License ("OSL") v. 3.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TCFS_ENCODE_MP3LAME_H
|
||||||
|
#define TCFS_ENCODE_MP3LAME_H 1
|
||||||
|
|
||||||
|
#include "encode.h"
|
||||||
|
|
||||||
|
extern struct encode_ops encode_mp3lame_ops;
|
||||||
|
|
||||||
|
#endif
|
||||||
22
src/encode.c
22
src/encode.c
@@ -8,18 +8,22 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "encode.h"
|
#include "encode.h"
|
||||||
#include "encode-ffmpeg.h"
|
#include "encode-ffmpeg.h"
|
||||||
|
#include "encode-mp3lame.h"
|
||||||
|
|
||||||
struct encoders_entry encoders[] = {
|
struct encoders_entry encoders[] = {
|
||||||
{&encode_ffmpeg_ops, "ffmpeg"},
|
#ifdef HAVE_LIBMP3LAME
|
||||||
|
{&encode_mp3lame_ops, "mp3lame", 0},
|
||||||
|
#endif
|
||||||
|
{&encode_ffmpeg_ops, "ffmpeg", 1},
|
||||||
{NULL, ""}
|
{NULL, ""}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encode_ctx *encode_open(const char *tfmt)
|
struct encode_ctx *encode_init(struct decode_ctx *src, const char *tfmt, const struct source_info *src_info)
|
||||||
{
|
{
|
||||||
struct encode_ctx *ctx;
|
struct encode_ctx *ctx;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(encoders[i].ops) {
|
while(encoders[i].ops) {
|
||||||
ctx = encoders[i++].ops->open(tfmt);
|
ctx = encoders[i++].ops->init(src, tfmt, src_info);
|
||||||
if(!ctx) { // open failed
|
if(!ctx) { // open failed
|
||||||
if(tcfs_errno == ECODEC) // the codec doesn't support this format, try the next one
|
if(tcfs_errno == ECODEC) // the codec doesn't support this format, try the next one
|
||||||
continue;
|
continue;
|
||||||
@@ -33,12 +37,18 @@ struct encode_ctx *encode_open(const char *tfmt)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int encode_encode(void *dst, size_t dst_size, const void *src, size_t src_size, struct encode_ctx *ctx)
|
int encode_encode(void **dst, size_t *dst_size, struct encode_ctx *ctx)
|
||||||
{
|
{
|
||||||
return 0;
|
assert(ctx);
|
||||||
|
assert(ctx->ops);
|
||||||
|
assert(ctx->ops->encode);
|
||||||
|
return ctx->ops->encode(dst, dst_size, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int encode_close(struct encode_ctx *ctx)
|
int encode_close(struct encode_ctx *ctx)
|
||||||
{
|
{
|
||||||
return 0;
|
assert(ctx);
|
||||||
|
assert(ctx->ops);
|
||||||
|
assert(ctx->ops->close);
|
||||||
|
return ctx->ops->close(ctx);
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/encode.h
15
src/encode.h
@@ -7,25 +7,30 @@
|
|||||||
#ifndef TCFS_ENCODE_H
|
#ifndef TCFS_ENCODE_H
|
||||||
#define TCFS_ENCODE_H 1
|
#define TCFS_ENCODE_H 1
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "decode.h"
|
||||||
|
|
||||||
struct encode_ctx {
|
struct encode_ctx {
|
||||||
struct encode_ops *ops;
|
struct encode_ops *ops;
|
||||||
|
struct decode_ctx *src;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encode_ops {
|
struct encode_ops {
|
||||||
struct encode_ctx *(*open)(const char *);
|
struct encode_ctx *(*init)(struct decode_ctx *, const char *, const struct source_info *);
|
||||||
int (*encode)(void *, size_t, const void *, size_t, struct encode_ctx *);
|
int (*encode)(void **, size_t *, struct encode_ctx *);
|
||||||
int (*close)(struct encode_ctx *);
|
int (*close)(struct encode_ctx *);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encoders_entry {
|
struct encoders_entry {
|
||||||
struct encode_ops *ops;
|
struct encode_ops *ops;
|
||||||
char name[];
|
char name[TCFS_MAX_NAME_LENGTH];
|
||||||
|
unsigned stream:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct encoders_entry encoders[];
|
extern struct encoders_entry encoders[];
|
||||||
|
|
||||||
struct encode_ctx *encode_open(const char *tfmt);
|
struct encode_ctx *encode_init(struct decode_ctx *src, const char *tfmt, const struct source_info *src_info);
|
||||||
int encode_encode(void *dst, size_t dst_size, const void *src, size_t src_size, struct encode_ctx *ctx);
|
int encode_encode(void **dst, size_t *dst_size, struct encode_ctx *ctx);
|
||||||
int encode_close(struct encode_ctx *ctx);
|
int encode_close(struct encode_ctx *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
30
src/tcfs.c
30
src/tcfs.c
@@ -27,6 +27,7 @@ int tcfs_getattr(const char *path, struct stat *stbuf)
|
|||||||
if(tc_src) {
|
if(tc_src) {
|
||||||
ret = stat(tc_src, stbuf);
|
ret = stat(tc_src, stbuf);
|
||||||
stbuf->st_size = 0;
|
stbuf->st_size = 0;
|
||||||
|
free(tc_src);
|
||||||
if(ret == -1) {
|
if(ret == -1) {
|
||||||
free(base_path);
|
free(base_path);
|
||||||
return -errno;
|
return -errno;
|
||||||
@@ -117,10 +118,11 @@ int tcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
filler(buf, file_new_ext, NULL, 0);
|
filler(buf, file_new_ext, NULL, 0);
|
||||||
|
free(file_new_ext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(base_path);
|
free(base_path);
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -131,6 +133,7 @@ struct tcfs_file_info {
|
|||||||
TRANSCODE
|
TRANSCODE
|
||||||
} type;
|
} type;
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
|
struct TC_FILE *tc_fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
int tcfs_open(const char *path, struct fuse_file_info *fi)
|
int tcfs_open(const char *path, struct fuse_file_info *fi)
|
||||||
@@ -150,15 +153,19 @@ int tcfs_open(const char *path, struct fuse_file_info *fi)
|
|||||||
if(!tcfs_fi->fd) {
|
if(!tcfs_fi->fd) {
|
||||||
char *src_name = get_tc_source_name(base_path);
|
char *src_name = get_tc_source_name(base_path);
|
||||||
if(src_name) {
|
if(src_name) {
|
||||||
tcfs_fi->fd = fopen(src_name, "r");
|
tcfs_fi->tc_fd = tc_open(src_name, base_path, options.tfmt);
|
||||||
if(!tcfs_fi->fd) {
|
free(src_name);
|
||||||
|
if(!tcfs_fi->tc_fd) {
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
tcfs_fi->type = TRANSCODE;
|
||||||
} else {
|
} else {
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
tcfs_fi->type = DIRECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
fi->fh = (uint64_t)tcfs_fi;
|
fi->fh = (uint64_t)tcfs_fi;
|
||||||
fi->direct_io = 1;
|
fi->direct_io = 1;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -170,12 +177,15 @@ int tcfs_read(const char *path, char *buf, size_t size, off_t offset, struct fus
|
|||||||
if(!tcfs_fi)
|
if(!tcfs_fi)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
errno = 0;
|
if(tcfs_fi->type == DIRECT) {
|
||||||
int ret = fread(buf, 1, size, tcfs_fi->fd);
|
errno = 0;
|
||||||
if((ret == 0) && (errno != 0))
|
int ret = fread(buf, 1, size, tcfs_fi->fd);
|
||||||
return -errno;
|
if((ret == 0) && (errno != 0))
|
||||||
|
return -errno;
|
||||||
return ret;
|
return ret;
|
||||||
|
} else {
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *tcfs_init(struct fuse_conn_info *conn)
|
void *tcfs_init(struct fuse_conn_info *conn)
|
||||||
|
|||||||
@@ -10,9 +10,13 @@
|
|||||||
|
|
||||||
struct TC_FILE {
|
struct TC_FILE {
|
||||||
struct decode_ctx *decode;
|
struct decode_ctx *decode;
|
||||||
|
struct encode_ctx *encode;
|
||||||
|
char *encoded_data;
|
||||||
|
size_t encoded_size;
|
||||||
|
size_t pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TC_FILE *tc_open(const char *src, const char *tfmt)
|
struct TC_FILE *tc_open(const char *src, const char *dst, const char *tfmt)
|
||||||
{
|
{
|
||||||
struct TC_FILE *fd = (struct TC_FILE*)malloc(sizeof(struct TC_FILE));
|
struct TC_FILE *fd = (struct TC_FILE*)malloc(sizeof(struct TC_FILE));
|
||||||
if(!fd) {
|
if(!fd) {
|
||||||
@@ -20,11 +24,59 @@ struct TC_FILE *tc_open(const char *src, const char *tfmt)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd->decode = decode_open(src);
|
struct source_info src_info;
|
||||||
|
|
||||||
|
fd->decode = decode_open(src, &src_info);
|
||||||
if(!fd->decode) {
|
if(!fd->decode) {
|
||||||
free(fd);
|
free(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fd->encode = encode_init(fd->decode, tfmt, &src_info);
|
||||||
|
if(!fd->encode) {
|
||||||
|
decode_close(fd->decode);
|
||||||
|
free(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encode_encode(&fd->encoded_data, &fd->encoded_size, fd->encode) < 0) {
|
||||||
|
encode_close(fd->encode);
|
||||||
|
decode_close(fd->decode);
|
||||||
|
free(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug dump
|
||||||
|
char name[11+strlen(tfmt)];
|
||||||
|
strcpy(name,"debugdump.");
|
||||||
|
strcpy(name+10, tfmt);
|
||||||
|
FILE *debugdump = fopen(name, "w");
|
||||||
|
fwrite(fd->encoded_data, fd->encoded_size, 1, debugdump);
|
||||||
|
fclose(debugdump);
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tc_read(char *buf, size_t size, struct TC_FILE *fd)
|
||||||
|
{
|
||||||
|
assert(fd);
|
||||||
|
|
||||||
|
size_t readsize = (size+fd->pos<fd->encoded_size)?size:(fd->encoded_size-fd->pos);
|
||||||
|
if(!readsize)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memcpy(buf, fd->encoded_data, readsize);
|
||||||
|
|
||||||
|
fd->pos += readsize;
|
||||||
|
|
||||||
|
return readsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tc_close(struct TC_FILE *fd)
|
||||||
|
{
|
||||||
|
assert(fd);
|
||||||
|
encode_close(fd->encode);
|
||||||
|
decode_close(fd->decode);
|
||||||
|
free(fd->encoded_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
struct TC_FILE;
|
struct TC_FILE;
|
||||||
|
|
||||||
struct TC_FILE *tc_open(const char *src, const char *tfmt);
|
struct TC_FILE *tc_open(const char *src, const char *dst, const char *tfmt);
|
||||||
int tc_read(char *buf, size_t size, struct TC_FILE *fd);
|
int tc_read(char *buf, size_t size, struct TC_FILE *fd);
|
||||||
int tc_close(struct TC_FILE *fd);
|
int tc_close(struct TC_FILE *fd);
|
||||||
|
|
||||||
|
|||||||
@@ -97,8 +97,9 @@ int is_tc_audio_file(const char *path)
|
|||||||
if(strcmp(ext,options.tfmt) == 0)
|
if(strcmp(ext,options.tfmt) == 0)
|
||||||
return 0; // file already has right format
|
return 0; // file already has right format
|
||||||
|
|
||||||
|
struct source_info src_info;
|
||||||
// try to open file as transcode source
|
// try to open file as transcode source
|
||||||
struct decode_ctx *tcfd = decode_open(path);
|
struct decode_ctx *tcfd = decode_open(path, &src_info);
|
||||||
if(tcfd) {
|
if(tcfd) {
|
||||||
decode_close(tcfd);
|
decode_close(tcfd);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -17,7 +17,15 @@ struct options {
|
|||||||
extern struct options options;
|
extern struct options options;
|
||||||
extern TLS int tcfs_errno;
|
extern TLS int tcfs_errno;
|
||||||
|
|
||||||
|
struct source_info {
|
||||||
|
unsigned long samples;
|
||||||
|
int sample_rate;
|
||||||
|
int depth;
|
||||||
|
int channels;
|
||||||
|
};
|
||||||
|
|
||||||
#define ECODEC 1001
|
#define ECODEC 1001
|
||||||
|
#define TCFS_MAX_NAME_LENGTH 64
|
||||||
|
|
||||||
const char *get_file_from_path(const char *path);
|
const char *get_file_from_path(const char *path);
|
||||||
const char *get_ext_from_file(const char *file);
|
const char *get_ext_from_file(const char *file);
|
||||||
|
|||||||
Reference in New Issue
Block a user