Successfully encoded an mp3 with test_encode.

This commit is contained in:
2010-11-07 01:48:01 +01:00
parent 629311bb96
commit 3b5e792499
15 changed files with 550 additions and 26 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,6 @@
tcfs
test_encode
*.mp3
*.o
*~
testmnt/

View File

@@ -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
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)

View File

@@ -62,5 +62,6 @@
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#endif

132
src/decode.c Normal file
View File

@@ -0,0 +1,132 @@
/*
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
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;
}

16
src/decode.h Normal file
View File

@@ -0,0 +1,16 @@
/*
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
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

50
src/encode.c Normal file
View File

@@ -0,0 +1,50 @@
/*
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
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;
}

14
src/encode.h Normal file
View 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_H
#define TCFS_ENCODE_H 1
struct encode_ctx;
struct encode_ctx *encode_open(const char *tfmt);
#endif

View File

@@ -7,6 +7,7 @@
#include "common.h"
#include "util.h"
#include "transcode.h"
#include "tcfs_buf.h"
#include <fuse.h>
#include <fuse_opt.h>
@@ -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) {
@@ -243,5 +244,7 @@ int main(int argc, char *argv[])
av_register_all();
tcfs_buf_register();
return fuse_main(args.argc, args.argv, &tcfs_oper, NULL);
}

46
src/tcfs_buf.c Normal file
View File

@@ -0,0 +1,46 @@
/*
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
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;
}

13
src/tcfs_buf.h Normal file
View File

@@ -0,0 +1,13 @@
/*
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
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

173
src/test_encode.c Normal file
View File

@@ -0,0 +1,173 @@
/*
Copyright (c) 2010 Matthias Blankertz <matthias@blankertz.org>
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;
}

View File

@@ -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)
{
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;
}

View File

@@ -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

View File

@@ -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 || (ext<file)) // no . or . not in last component
return ret;
@@ -52,6 +53,23 @@ char *strip_ext_from_path(const char *path)
return ret;
}
char *strip_file_from_path(const char *path)
{
if(!path)
return NULL;
char *ret = strdup(path);
char *file = strrchr(ret, '/');
if(!file) {
ret[0] = '/';
ret[1] = '\0';
return ret;
}
*(file+1) = '\0';
return ret;
}
// returns 1 if the file is a transcodeable audio file, 0 if it is not and a negative value if an error occured
int is_tc_audio_file(const char *path)
{
@@ -77,11 +95,12 @@ int is_tc_audio_file(const char *path)
if(strcmp(ext,options.tfmt) == 0)
return 0; // file already has right format
// TODO: proper detection code here
// for testing
if(strcmp(ext,"ogg") == 0)
// try to open file as transcode source
struct decode_ctx *tcfd = decode_open(path);
if(tcfd) {
decode_close(tcfd);
return 1;
}
return 0;
}
@@ -98,18 +117,59 @@ char *get_tc_source_name(const char *path)
return NULL;
}
char *path_no_ext = strip_ext_from_path(path);
if(!path_no_ext)
char *file_no_ext = strip_ext_from_path(get_file_from_path(path));
if(!file_no_ext)
return NULL;
// TODO : search for transcodable files in path_no_ext.*
// for testing
char *ret;
if(asprintf(&ret, "%s.ogg", path_no_ext) < 0)
char *dir_name = strip_file_from_path(path);
if(!dir_name) {
free(file_no_ext);
return NULL;
}
return ret;
DIR *dir = opendir(dir_name);
if(!dir) {
free(file_no_ext);
free(dir_name);
return NULL;
}
union {
struct dirent de;
char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
} u;
struct dirent *dep;
while(readdir_r(dir, &u.de, &dep) == 0) {
if(!dep) {
free(file_no_ext);
free(dir_name);
closedir(dir);
return 0;
}
char *de_no_ext = strip_ext_from_path(dep->d_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

View File

@@ -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);