shithub: aacdec

ref: 3ba088193fc0e9b022957fb348cfdfe1d60daab7
dir: /frontend/aacdec.c/

View raw version
/*
** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** Any non-GPL usage of this software or parts of this software is strictly
** forbidden.
**
** The "appropriate copyright message" mentioned in section 2c of the GPLv2
** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"
**
** Commercial non-GPL licensing of this software is possible.
** For more info contact Nero AG through [email protected].
**
** $Id: main.c,v 1.89 2015/01/19 09:46:12 knik Exp $
**/

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <neaacdec.h>

#define min(a,b) ((a)<=(b)?(a):(b))

#define MAX_CHANNELS 6 /* make this higher to support files with more channels */

/* FAAD file buffering routines */
typedef struct {
	long bytes_into_buffer;
	long bytes_consumed;
	long file_offset;
	unsigned char *buffer;
	int at_eof;
}aac_buffer;

static Biobuf stdin, stdout;

static int
fill_buffer(aac_buffer *b)
{
	int bread;

	if(b->bytes_consumed > 0){
		if(b->bytes_into_buffer)
			memmove(b->buffer, b->buffer + b->bytes_consumed, b->bytes_into_buffer);

		if(!b->at_eof){
			bread = Bread(&stdin, (void*)(b->buffer + b->bytes_into_buffer), b->bytes_consumed);
			if(bread != b->bytes_consumed)
				b->at_eof = 1;
			b->bytes_into_buffer += bread;
		}

		b->bytes_consumed = 0;

		if(b->bytes_into_buffer > 3 && memcmp(b->buffer, "TAG", 3) == 0)
			b->bytes_into_buffer = 0;
		if(b->bytes_into_buffer > 11 && memcmp(b->buffer, "LYRICSBEGIN", 11) == 0)
			b->bytes_into_buffer = 0;
		if(b->bytes_into_buffer > 8 && memcmp(b->buffer, "APETAGEX", 8) == 0)
			b->bytes_into_buffer = 0;
	}

	return 1;
}

static void
advance_buffer(aac_buffer *b, int bytes)
{
	while(b->bytes_into_buffer > 0 && bytes > 0){
		int chunk = min(bytes, b->bytes_into_buffer);

		bytes -= chunk;
		b->file_offset += chunk;
		b->bytes_consumed = chunk;
		b->bytes_into_buffer -= chunk;

		if(b->bytes_into_buffer == 0)
			fill_buffer(b);
	}
}

static int
decode(void)
{
	NeAACDecHandle hDecoder;
	NeAACDecFrameInfo frameInfo;
	NeAACDecConfigurationPtr config;
	unsigned long samplerate;
	unsigned char channels;
	void *sample_buffer;
	int bread;
	aac_buffer b;

	memset(&b, 0, sizeof(b));
	if((b.buffer = malloc(FAAD_MIN_STREAMSIZE*MAX_CHANNELS)) == nil)
		return -1;
	memset(b.buffer, 0, FAAD_MIN_STREAMSIZE*MAX_CHANNELS);

	bread = Bread(&stdin, b.buffer, FAAD_MIN_STREAMSIZE*MAX_CHANNELS);
	b.bytes_into_buffer = bread;
	b.bytes_consumed = 0;
	b.file_offset = 0;

	if(bread != FAAD_MIN_STREAMSIZE*MAX_CHANNELS)
		b.at_eof = 1;

	hDecoder = NeAACDecOpen();

	config = NeAACDecGetCurrentConfiguration(hDecoder);
	config->defSampleRate = 44100;
	config->defObjectType = LC;
	config->outputFormat = FAAD_FMT_16BIT;
	config->downMatrix = 0;
	config->useOldADTSFormat = 0;
	NeAACDecSetConfiguration(hDecoder, config);

	fill_buffer(&b);
	if((bread = NeAACDecInit(hDecoder, b.buffer, b.bytes_into_buffer, &samplerate, &channels)) < 0){
		sysfatal("NeAACDecInit failed: %d", bread);
		if(b.buffer)
			free(b.buffer);
		NeAACDecClose(hDecoder);
		return -1;
	}
	advance_buffer(&b, bread);
	fill_buffer(&b);

	if(samplerate != 44100 || channels != 2){
		int pfd[2], pid;
		char fmt[32];

		pid = -1;
		if(pipe(pfd) < 0 || (pid = fork()) < 0)
			sysfatal("%r");
		if(pid == 0){
			dup(pfd[1], 0);
			close(pfd[1]);
			close(pfd[0]);
			snprint(fmt, sizeof(fmt), "s16c%dr%ld", channels, samplerate);
			execl("/bin/audio/pcmconv", "pcmconv", "-i", fmt, nil);
			sysfatal("%r");
		}
		close(pfd[1]);
		Binit(&stdout, pfd[0], OWRITE);
	}

	for(;;){
		sample_buffer = NeAACDecDecode(hDecoder, &frameInfo, b.buffer, b.bytes_into_buffer);

		/* update buffer indices */
		advance_buffer(&b, frameInfo.bytesconsumed);

		if(frameInfo.error != 0)
			sysfatal("%s: %d", NeAACDecGetErrorMessage(frameInfo.error), frameInfo.error);

		if(frameInfo.samples > 0 && Bwrite(&stdout, sample_buffer, frameInfo.samples*2) != frameInfo.samples*2)
			break;

		fill_buffer(&b);

		if(b.bytes_into_buffer == 0)
			break;
	}

	NeAACDecClose(hDecoder);

	if(b.buffer)
		free(b.buffer);

	return frameInfo.error;
}

static void
usage(void)
{
	fprint(2, "usage: %s\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	int n;

	ARGBEGIN{
	case 's': /* FIXME seeking isn't implemented yet */
		break;
	default:
		usage();
	}ARGEND

	if(argc != 0)
		usage();

	Binit(&stdin, 0, OREAD);
	Binit(&stdout, 1, OWRITE);

	n = decode();
	Bterm(&stdout);
	waitpid();

	exits(n == 0 ? nil : "failed");
}