Skip to content

Commit

Permalink
format_mp3: Add MP3 write support.
Browse files Browse the repository at this point in the history
This adds the ability to write in the MP3 format;
previously, MP3 data could only be read.

Resolves: asterisk#664

UserNote: Asterisk now supports natively writing
data in the MP3 format.
  • Loading branch information
InterLinked1 committed Mar 28, 2024
1 parent 688095c commit 6029fd7
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 6 deletions.
131 changes: 125 additions & 6 deletions addons/format_mp3.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
* Thanks to mpglib from http://www.mpg123.org/
* and Chris Stenton [jacs@gnome.co.uk]
* for coding the ability to play stereo and non-8khz files
*
* MP3 write support using LAME
* added by Naveen Albert <asterisk@phreaknet.org>
* and sponsored by Marfox Ltd.
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
Expand All @@ -28,6 +32,7 @@
*/

/*** MODULEINFO
<use type="external">lame</use>
<defaultenabled>no</defaultenabled>
<support_level>extended</support_level>
***/
Expand All @@ -37,6 +42,17 @@
#include "mp3/mpg123.h"
#include "mp3/mpglib.h"

/* LAME (libmp3lame) is only needed for MP3 write support.
* This is provided by the libmp3lame-dev package on Debian. */
#ifdef HAVE_LAME
#define ENABLE_MP3_WRITING
#endif

#ifdef ENABLE_MP3_WRITING
#include <stdarg.h>
#include <lame/lame.h>
#endif

#include "asterisk/module.h"
#include "asterisk/mod_format.h"
#include "asterisk/logger.h"
Expand All @@ -46,6 +62,10 @@
#define MP3_SCACHE 16384
#define MP3_DCACHE 8192

#ifdef ENABLE_MP3_WRITING
static lame_global_flags *gfp;
#endif

struct mp3_private {
/*! state for the mp3 decoder */
struct mpstr mp;
Expand All @@ -63,6 +83,9 @@ struct mp3_private {
int dbufoffset;
int offset;
long seek;
#ifdef ENABLE_MP3_WRITING
unsigned int wrote:1;
#endif
};

static const char name[] = "mp3";
Expand Down Expand Up @@ -107,6 +130,20 @@ static void mp3_close(struct ast_filestream *s)
{
struct mp3_private *p = s->_private;

#ifdef ENABLE_MP3_WRITING
if (p->wrote) {
unsigned char buf[7200];
int wres, res;
ast_debug(1, "Flushing MP3 stream\n");
res = lame_encode_flush(gfp, buf, sizeof(buf));
wres = fwrite(buf, 1, res, s->f);
if (wres != res) {
ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", wres, res, strerror(errno));
}
lame_mp3_tags_fid(gfp, s->f);
}
#endif

ExitMP3(&p->mp);
return;
}
Expand Down Expand Up @@ -236,14 +273,44 @@ static struct ast_frame *mp3_read(struct ast_filestream *s, int *whennext)
return &s->fr;
}


static int mp3_write(struct ast_filestream *fs, struct ast_frame *f)
{
ast_log(LOG_ERROR,"I Can't write MP3 only read them.\n");
return -1;
#ifdef ENABLE_MP3_WRITING
/* mp3buf_size in bytes = 1.25*num_samples + 7200
* Assuming f->datalen <= 720, 1.25 * 720 + 7200 = 8100 */
#define MP3_BUFSIZE 8192
int res, wres;
unsigned char buf[MP3_BUFSIZE];
struct mp3_private *p = fs->_private;

if (!f->datalen) {
return -1;
}

}
if (f->datalen > 720) {
ast_log(LOG_WARNING, "Too much data to write at once: %d\n", f->datalen);
return -1;
}

/* Since this is mono audio, we only have a L channel. R channel buffer is ignored. */
res = lame_encode_buffer(gfp, f->data.ptr, f->data.ptr, f->samples, buf, sizeof(buf));
if (res < 0) {
ast_log(LOG_WARNING, "LAME encode returned %d\n", res);
return -1;
}

wres = fwrite(buf, 1, res, fs->f);
if (wres != res) {
ast_log(LOG_WARNING, "Bad write (%d/%d): %s\n", wres, res, strerror(errno));
return -1;
}
p->wrote = 1;
return 0;
#else
ast_log(LOG_ERROR, "I Can't write MP3 only read them.\n");
return -1;
#endif
}

static int mp3_seek(struct ast_filestream *s, off_t sample_offset, int whence)
{
Expand Down Expand Up @@ -274,8 +341,13 @@ static int mp3_seek(struct ast_filestream *s, off_t sample_offset, int whence)

static int mp3_rewrite(struct ast_filestream *s, const char *comment)
{
#ifdef ENABLE_MP3_WRITING
/* Header gets written at the end */
return 0;
#else
ast_log(LOG_ERROR,"I Can't write MP3 only read them.\n");
return -1;
#endif
}

static int mp3_trunc(struct ast_filestream *s)
Expand Down Expand Up @@ -313,17 +385,64 @@ static struct ast_format_def mp3_f = {
.desc_size = sizeof(struct mp3_private),
};

#ifdef ENABLE_MP3_WRITING
/* gcc suggests printf attribute but that is wrong here */
#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
static void mp3_error(const char *format, va_list ap)
{
ast_log_ap(AST_LOG_WARNING, format, ap);
}

static void mp3_debug(const char *format, va_list ap)
{
ast_log_ap(AST_LOG_DEBUG, format, ap);
}

static void mp3_msg(const char *format, va_list ap)
{
ast_log_ap(AST_LOG_NOTICE, format, ap);
}
#endif

static int load_module(void)
{
#ifdef ENABLE_MP3_WRITING
int res;

ast_debug(1, "LAME version: %s\n", get_lame_version());
gfp = lame_init();

/* Set logging callbacks */
lame_set_errorf(gfp, mp3_error);
lame_set_debugf(gfp, mp3_debug);
lame_set_msgf(gfp, mp3_msg);

/* Override default settings */
lame_set_num_channels(gfp, 1); /* Mono */
lame_set_in_samplerate(gfp, 8000); /* 8 KHz */
/* Bit rate, e.g. 64 = 64kbps PCM audio */
lame_set_brate(gfp, 16);
lame_set_mode(gfp, 3); /* Mono */
lame_set_quality(gfp, 5); /* Medium quality */

res = lame_init_params(gfp);
if (res < 0) {
ast_log(LOG_ERROR, "Failed to initialize LAME\n");
return -1;
}
#endif
mp3_f.format = ast_format_slin;
InitMP3Constants();
return ast_format_def_register(&mp3_f);
}

static int unload_module(void)
{
return ast_format_def_unregister(name);
int res = ast_format_def_unregister(name);
#ifdef ENABLE_MP3_WRITING
lame_close(gfp);
#endif
return res;
}

AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "MP3 format [Any rate but 8000hz mono is optimal]");
1 change: 1 addition & 0 deletions build_tools/menuselect-deps.in
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ JACK=@PBX_JACK@
JANSSON=@PBX_JANSSON@
URIPARSER=@PBX_URIPARSER@
KQUEUE=@PBX_KQUEUE@
LAME=@PBX_LAME@
LDAP=@PBX_LDAP@
LIBEDIT=@PBX_LIBEDIT@
LIBJWT=@PBX_LIBJWT@
Expand Down
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile])
AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
AST_EXT_LIB_SETUP([BEANSTALK], [Beanstalk Job Queue], [beanstalk])
AST_EXT_LIB_SETUP([LAME], [LAME MP3], [mp3lame])

if test "x${PBX_PJPROJECT}" != "x1" ; then
AST_EXT_LIB_SETUP([PJPROJECT], [PJPROJECT], [pjproject])
Expand Down Expand Up @@ -1630,6 +1631,7 @@ fi

# do the package library checks now

AST_EXT_LIB_CHECK([LAME], [mp3lame], [get_lame_version], [lame/lame.h])
AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h])
# Fedora/RedHat/CentOS require extra libraries
AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h], [-ldl -liberty])
Expand Down
3 changes: 3 additions & 0 deletions makeopts.in
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ LIBJWT_CONFIGURE_OPTS=@LIBJWT_CONFIGURE_OPTS@
URIPARSER_INCLUDE=@URIPARSER_INCLUDE@
URIPARSER_LIB=@URIPARSER_LIB@

LAME_INCLUDE=@LAME_INCLUDE@
LAME_LIB=@LAME_LIB@

LDAP_INCLUDE=@LDAP_INCLUDE@
LDAP_LIB=@LDAP_LIB@

Expand Down

0 comments on commit 6029fd7

Please sign in to comment.