From 3b5e792499d9ea67ac1ac817ae162682baf0ac7c Mon Sep 17 00:00:00 2001 From: Matthias Blankertz Date: Sun, 7 Nov 2010 01:48:01 +0100 Subject: [PATCH] Successfully encoded an mp3 with test_encode. --- .gitignore | 2 + Makefile.am | 9 ++- src/common.h | 1 + src/decode.c | 132 +++++++++++++++++++++++++++++++++++ src/decode.h | 16 +++++ src/encode.c | 50 ++++++++++++++ src/encode.h | 14 ++++ src/tcfs.c | 5 +- src/tcfs_buf.c | 46 ++++++++++++ src/tcfs_buf.h | 13 ++++ src/test_encode.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++ src/transcode.c | 21 ++++-- src/transcode.h | 1 + src/util.c | 92 +++++++++++++++++++----- src/util.h | 1 + 15 files changed, 550 insertions(+), 26 deletions(-) create mode 100644 src/decode.c create mode 100644 src/decode.h create mode 100644 src/encode.c create mode 100644 src/encode.h create mode 100644 src/tcfs_buf.c create mode 100644 src/tcfs_buf.h create mode 100644 src/test_encode.c diff --git a/.gitignore b/.gitignore index 5da01fb..6b96f59 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ tcfs +test_encode +*.mp3 *.o *~ testmnt/ diff --git a/Makefile.am b/Makefile.am index 63bddcc..bcb9f81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,7 @@ -bin_PROGRAMS=tcfs -tcfs_SOURCES=src/tcfs.c src/transcode.c src/util.c +bin_PROGRAMS=tcfs test_encode +tcfs_SOURCES=src/tcfs.c src/transcode.c src/decode.c src/encode.c src/util.c src/tcfs_buf.c tcfs_LDADD = $(LIBOBJS) $(FUSE_LIBS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS) -tcfs_CFLAGS = $(FUSE_CFLAGS) $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS) -DFUSE_USE_VERSION=28 \ No newline at end of file +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 +test_encode_LDADD = $(LIBOBJS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS) +test_encode_CFLAGS = $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS) \ No newline at end of file diff --git a/src/common.h b/src/common.h index a49c224..f47d0f6 100644 --- a/src/common.h +++ b/src/common.h @@ -62,5 +62,6 @@ #include #include #include +#include #endif diff --git a/src/decode.c b/src/decode.c new file mode 100644 index 0000000..0ba0c63 --- /dev/null +++ b/src/decode.c @@ -0,0 +1,132 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#include "common.h" + +struct decode_ctx { + AVFormatContext *avfctx; + int stream; + AVCodec *avc; + char *bod; + int len, offs; +}; + +struct decode_ctx *decode_open(const char *src) +{ + struct decode_ctx *fd = (struct decode_ctx*)malloc(sizeof(struct decode_ctx)); + if(!fd) + return NULL; + + int ret; + if((ret = av_open_input_file(&fd->avfctx, src, NULL, 0, NULL)) != 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(fd); + errno = EIO; + return NULL; + } + + if((ret = av_find_stream_info(fd->avfctx)) < 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + av_close_input_file(fd->avfctx); + free(fd); + errno = EIO; + return NULL; + } + + dump_format(fd->avfctx, 0, src, 0); + + int i; + for(i = 0;i < fd->avfctx->nb_streams;++i) { + if(fd->avfctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) + break; + } + if(i == fd->avfctx->nb_streams) { + // no audio stream + av_close_input_file(fd->avfctx); + free(fd); + errno = EIO; + return NULL; + } + + fd->stream = i; + + fd->avc = avcodec_find_decoder(fd->avfctx->streams[fd->stream]->codec->codec_id); + if(!fd->avc) { + fprintf(stderr, "ffmpeg error: Could not find decoder.\n"); + av_close_input_file(fd->avfctx); + free(fd); + errno = EIO; + return NULL; + } + + if((ret = avcodec_open(fd->avfctx->streams[fd->stream]->codec, fd->avc)) < 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + av_close_input_file(fd->avfctx); + free(fd); + errno = EIO; + return NULL; + } + + fd->bod = malloc(128*1024*1024); + if(!fd->bod) + return NULL; + + AVPacket pack; + int pos = 0; + int fsp = 128*1024*1024; + while(av_read_frame(fd->avfctx, &pack) == 0) { + if(pack.stream_index == fd->stream) { + if(avcodec_decode_audio3(fd->avfctx->streams[fd->stream]->codec, fd->bod+pos, &fsp, &pack) < 0) { + printf("Decode error.\n"); + return NULL; + } + pos += fsp; + fsp = 128*1024*1024-pos; + } + } + fd->len = pos; + fd->offs = 0; + + return fd; +} + +int decode_read(char *buf, size_t size, struct decode_ctx *fd) +{ + if(size < fd->len-fd->offs) { + memcpy(buf, fd->bod+fd->offs, size); + fd->offs += size; + return size; + } else { + memcpy(buf, fd->bod+fd->offs, fd->len-fd->offs); + int ret = fd->len - fd->offs; + fd->offs = fd->len; + return ret; + } +} + +int decode_close(struct decode_ctx *fd) +{ + if(!fd) { + errno = EINVAL; + return -1; + } + if(!fd->avfctx || !fd->avc) { + free(fd); + return 0; + } + + free(fd->bod); + avcodec_close(fd->avfctx->streams[fd->stream]->codec); + av_close_input_file(fd->avfctx); + free(fd); + return 0; +} diff --git a/src/decode.h b/src/decode.h new file mode 100644 index 0000000..009726a --- /dev/null +++ b/src/decode.h @@ -0,0 +1,16 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#ifndef TCFS_DECODE_H +#define TCFS_DECODE_H 1 + +struct decode_ctx; + +struct decode_ctx *decode_open(const char *src); +int decode_read(char *buf, size_t size, struct decode_ctx *fd); +int decode_close(struct decode_ctx *fd); + +#endif diff --git a/src/encode.c b/src/encode.c new file mode 100644 index 0000000..f392cb1 --- /dev/null +++ b/src/encode.c @@ -0,0 +1,50 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#include "common.h" + +struct encode_ctx { + AVFormatContext *avfctx; +}; + +struct encode_ctx *encode_open(const char *tfmt) +{ + struct encode_ctx *ctx = (struct encode_ctx*)malloc(sizeof(struct encode_ctx)); + if(!ctx) { + errno = ENOMEM; + return NULL; + } + + ctx->avfctx = avformat_alloc_context(); + if(!ctx->avfctx) { + free(ctx); + return NULL; + } + + int ret; + URLContext *urlctx; + if((ret = url_open_protocol(&urlctx, tcfs_buf_get_protocol(), "", 0)) != 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(ctx->avfctx); + free(ctx); + errno = EIO; + return NULL; + } + + if((ret = url_fdopen(&ctx->avfctx->pb, urlctx)) != 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(ctx->avfctx); + free(ctx); + errno = EIO; + return NULL; + } + + return ctx; +} diff --git a/src/encode.h b/src/encode.h new file mode 100644 index 0000000..7d3b763 --- /dev/null +++ b/src/encode.h @@ -0,0 +1,14 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#ifndef TCFS_ENCODE_H +#define TCFS_ENCODE_H 1 + +struct encode_ctx; + +struct encode_ctx *encode_open(const char *tfmt); + +#endif diff --git a/src/tcfs.c b/src/tcfs.c index 972eb00..acf2e04 100644 --- a/src/tcfs.c +++ b/src/tcfs.c @@ -7,6 +7,7 @@ #include "common.h" #include "util.h" #include "transcode.h" +#include "tcfs_buf.h" #include #include @@ -101,7 +102,7 @@ int tcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, if(ret == 0) filler(buf, dep->d_name, NULL, 0); else { - char *ext = strchr(dep->d_name, '.'); + char *ext = strrchr(dep->d_name, '.'); char *file_new_ext; if(ext == NULL) { if(asprintf(&file_new_ext, "%s.%s", dep->d_name, options.tfmt) < 0) { @@ -242,6 +243,8 @@ int main(int argc, char *argv[]) } av_register_all(); + + tcfs_buf_register(); return fuse_main(args.argc, args.argv, &tcfs_oper, NULL); } diff --git a/src/tcfs_buf.c b/src/tcfs_buf.c new file mode 100644 index 0000000..2f981c6 --- /dev/null +++ b/src/tcfs_buf.c @@ -0,0 +1,46 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#include "common.h" +#include "util.h" + +int tcfs_buf_open(URLContext *h, const char *url, int flags) +{ + return AVERROR_NOTSUPP; +} + +int tcfs_buf_read(URLContext *h, unsigned char *buf, int size) +{ + return AVERROR_NOTSUPP; +} + +int tcfs_buf_write(URLContext *h, const unsigned char *buf, int size) +{ + return AVERROR_NOTSUPP; +} + +int tcfs_buf_get_handle(URLContext *h) +{ + return AVERROR_NOTSUPP; +} + +URLProtocol tcfs_buf_protocol = { + "tcfs_buf", + tcfs_buf_open, + tcfs_buf_read, + tcfs_buf_write, + .url_get_file_handle = tcfs_buf_get_handle +}; + +void tcfs_buf_register() +{ + av_register_protocol2(&tcfs_buf_protocol, sizeof(URLProtocol)); +} + +URLProtocol *tcfs_buf_get_protocol() +{ + return &tcfs_buf_protocol; +} diff --git a/src/tcfs_buf.h b/src/tcfs_buf.h new file mode 100644 index 0000000..1b9f9d3 --- /dev/null +++ b/src/tcfs_buf.h @@ -0,0 +1,13 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#ifndef TCFS_BUF_H +#define TCFS_BUF_H 1 + +void tcfs_buf_register(); +URLProtocol *tcfs_buf_get_protocol(); + +#endif diff --git a/src/test_encode.c b/src/test_encode.c new file mode 100644 index 0000000..c1c775c --- /dev/null +++ b/src/test_encode.c @@ -0,0 +1,173 @@ +/* + Copyright (c) 2010 Matthias Blankertz + + This work is licensed under the Open Software License ("OSL") v. 3.0. + */ + +#include "common.h" +#include "util.h" +#include "encode.h" + +struct options options = { + "testbase", + "mp3" +}; +static void choose_sample_fmt(AVStream *st, AVCodec *codec) +{ + if(codec && codec->sample_fmts){ + const enum SampleFormat *p= codec->sample_fmts; + for(; *p!=-1; p++){ + if(*p == st->codec->sample_fmt) + break; + } + if(*p == -1) + st->codec->sample_fmt = codec->sample_fmts[0]; + } + } + + static void choose_sample_rate(AVStream *st, AVCodec *codec) + { + if(codec && codec->supported_samplerates){ + const int *p= codec->supported_samplerates; + int best=0; + int best_dist=INT_MAX; + for(; *p; p++){ + int dist= abs(st->codec->sample_rate - *p); + if(dist < best_dist){ + best_dist= dist; + best= *p; + } + } + if(best_dist){ + av_log(st->codec, AV_LOG_WARNING, "Requested sampling rate unsupported using closest supported (%d)\n", best); + } + st->codec->sample_rate= best; + } + } + +int main(int argc, char *argv[]) +{ + av_register_all(); + + tcfs_buf_register(); + + struct decode_ctx *decode = decode_open("testbase/fmc/Sinn/01 - Das frivole BurgfrÀulein - Erwachsen.ogg"); + if(!decode) { + printf("Can't decode source.\n"); + return -1; + } + + AVFormatContext *avfctx = avformat_alloc_context(); + if(!avfctx) { + return NULL; + } + + int ret; + + AVOutputFormat *ofmt; + ofmt = av_guess_format(NULL, "test.mp3", NULL); + if(!ofmt) { + printf("Can't find output format.\n"); + return -1; + } + + avfctx->oformat = ofmt; + + AVStream *st = av_new_stream(avfctx, 0); + if(!st) { + printf("Can't allocate stream.\n"); + return -1; + } + + enum CodecID codecid = av_guess_codec(ofmt, NULL, "test.mp3", NULL, AVMEDIA_TYPE_AUDIO); + AVCodec *codec = avcodec_find_encoder(codecid); + if(!codec) { + printf("Can't get codec.\n"); + return -1; + } + + avcodec_get_context_defaults3(st->codec, codec); + + //avcodec_thread_init(st->codec, 1); + + // st->codec->codec = codec; + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = codecid; + st->codec->channels = 2; + st->codec->sample_fmt = AV_SAMPLE_FMT_S16; + st->codec->sample_rate = 44100; + st->codec->channel_layout = 0; + st->codec->flags |= CODEC_FLAG_QSCALE; + st->codec->global_quality = st->quality = 5; + st->codec->time_base = (AVRational){1, 44100}; + choose_sample_fmt(st, codec); + choose_sample_rate(st, codec); + + if((ret=avcodec_open(st->codec, codec)) < 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(avfctx); + return NULL; + } + + if((ret = url_fopen(&avfctx->pb, "file:///home/matthias/devel/tcfs/test.mp3", URL_WRONLY)) != 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(avfctx); + return NULL; + } + + if((ret = av_write_header(avfctx)) != 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(avfctx); + return NULL; + } + + short *ibuf = (short*)malloc(sizeof(short)*st->codec->frame_size*st->codec->channels); + if(!ibuf) { + printf("No memory.\n"); + return -2; + } + memset(ibuf, 0, sizeof(short)*st->codec->frame_size*st->codec->channels); + + + + uint8_t obuf[4096]; + int done = 0; + while(!done) { + memset(ibuf, 0, sizeof(short)*st->codec->frame_size*st->codec->channels); + ret = decode_read(ibuf, sizeof(short)*st->codec->frame_size*st->codec->channels, decode); + if(ret != sizeof(short)*st->codec->frame_size*st->codec->channels) + done = 1; + if((ret = avcodec_encode_audio(st->codec, obuf, 4096, ibuf)) < 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(avfctx); + return NULL; + } + if(ret != 0) { + AVPacket pack; + av_new_packet(&pack, ret); + memcpy(pack.data, obuf, ret); + + av_write_frame(avfctx, &pack); + } + } + + if((ret = av_write_trailer(avfctx)) != 0) { + char err[512]; + av_strerror(ret, err, 512); + fprintf(stderr, "ffmpeg error: %s\n", err); + free(avfctx); + return NULL; + } + + // url_fclose(&avfctx->pb); + + return 0; +} diff --git a/src/transcode.c b/src/transcode.c index eecd888..aa23914 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -5,17 +5,26 @@ */ #include "common.h" +#include "decode.h" +#include "encode.h" struct TC_FILE { + struct decode_ctx *decode; }; struct TC_FILE *tc_open(const char *src, const char *tfmt) { - return NULL; -} + struct TC_FILE *fd = (struct TC_FILE*)malloc(sizeof(struct TC_FILE)); + if(!fd) { + errno = ENOMEM; + return NULL; + } -int tc_read(char *buf, size_t size, struct TC_FILE *fd) -{ - errno = ENOSYS; - return 0; + fd->decode = decode_open(src); + if(!fd->decode) { + free(fd); + return NULL; + } + + return fd; } diff --git a/src/transcode.h b/src/transcode.h index a63d743..496b407 100644 --- a/src/transcode.h +++ b/src/transcode.h @@ -11,5 +11,6 @@ struct TC_FILE; struct TC_FILE *tc_open(const char *src, const char *tfmt); int tc_read(char *buf, size_t size, struct TC_FILE *fd); +int tc_close(struct TC_FILE *fd); #endif diff --git a/src/util.c b/src/util.c index 62adfd1..c189ab4 100644 --- a/src/util.c +++ b/src/util.c @@ -6,6 +6,7 @@ #include "common.h" #include "util.h" +#include "decode.h" // Get the filename from a path, i.e. a pointer to the character after the last / in path. // If path contains no /, returns path. @@ -14,7 +15,7 @@ const char *get_file_from_path(const char *path) if(!path) return NULL; - char *ret = strchr(path, '/'); + char *ret = strrchr(path, '/'); if(ret == NULL) return path; @@ -27,7 +28,7 @@ const char *get_ext_from_file(const char *file) if(!file) return NULL; - char *ret = strchr(file, '.'); + char *ret = strrchr(file, '.'); if(!ret) return file+strlen(file); if(ret == file) @@ -43,8 +44,8 @@ char *strip_ext_from_path(const char *path) char *ret = strdup(path); - char *ext = strchr(ret, '.'); - char *file = strchr(ret, '/'); + char *ext = strrchr(ret, '.'); + char *file = strrchr(ret, '/'); if(!ext || (extd_name); + if(strcmp(de_no_ext, file_no_ext) == 0) { + char *de_with_path; + if(asprintf(&de_with_path, "%s%s", dir_name, dep->d_name) < 0) { + free(file_no_ext); + free(dir_name); + free(de_no_ext); + return NULL; + } + if(is_tc_audio_file(de_with_path) > 0) { + free(file_no_ext); + free(dir_name); + free(de_no_ext); + return de_with_path; + } + free(de_with_path); + } + free(de_no_ext); + } + + free(file_no_ext); + free(dir_name); + closedir(dir); + return NULL; } // safely concatenate options.base and path diff --git a/src/util.h b/src/util.h index 554fd42..94e3700 100644 --- a/src/util.h +++ b/src/util.h @@ -17,6 +17,7 @@ extern struct options options; const char *get_file_from_path(const char *path); const char *get_ext_from_file(const char *file); char *strip_ext_from_path(const char *path); +char *strip_file_from_path(const char *path); int is_tc_audio_file(const char *path); char *get_tc_source_name(const char *path); char *path_cat(const char *path);