From 4e142ea98dbc56e5efb1ad729f099f9cfefd0169 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sat, 7 Apr 2007 20:45:55 +0000 Subject: Moved plc, resample, and mp3 to third_party git-svn-id: http://svn.pjsip.org/repos/pjproject/branches/split-3rd-party@1175 74dad513-b988-da41-8d7b-12977e46ad98 --- third_party/mp3/BladeMP3EncDLL.h | 283 ++++++++++++++++++++ third_party/mp3/mp3_writer.c | 563 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 846 insertions(+) create mode 100644 third_party/mp3/BladeMP3EncDLL.h create mode 100644 third_party/mp3/mp3_writer.c (limited to 'third_party/mp3') diff --git a/third_party/mp3/BladeMP3EncDLL.h b/third_party/mp3/BladeMP3EncDLL.h new file mode 100644 index 00000000..2e32b913 --- /dev/null +++ b/third_party/mp3/BladeMP3EncDLL.h @@ -0,0 +1,283 @@ +/* + * Blade Type of DLL Interface for Lame encoder + * + * Copyright (c) 1999-2002 A.L. Faber + * Based on bladedll.h version 1.0 written by Jukka Poikolainen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +//#define _BLADEDLL 1 + +#ifndef ___BLADEDLL_H_INCLUDED___ +#define ___BLADEDLL_H_INCLUDED___ + +#ifdef __GNUC__ +//#define ATTRIBUTE_PACKED __attribute__((packed)) +#define ATTRIBUTE_PACKED +#else +#define ATTRIBUTE_PACKED +#pragma pack(push) +#pragma pack(1) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* encoding formats */ + +#define BE_CONFIG_MP3 0 +#define BE_CONFIG_LAME 256 + +/* type definitions */ + +typedef unsigned long HBE_STREAM; +typedef HBE_STREAM *PHBE_STREAM; +typedef unsigned long BE_ERR; + +/* error codes */ + +#define BE_ERR_SUCCESSFUL 0x00000000 +#define BE_ERR_INVALID_FORMAT 0x00000001 +#define BE_ERR_INVALID_FORMAT_PARAMETERS 0x00000002 +#define BE_ERR_NO_MORE_HANDLES 0x00000003 +#define BE_ERR_INVALID_HANDLE 0x00000004 +#define BE_ERR_BUFFER_TOO_SMALL 0x00000005 + +/* other constants */ + +#define BE_MAX_HOMEPAGE 128 + +/* format specific variables */ + +#define BE_MP3_MODE_STEREO 0 +#define BE_MP3_MODE_JSTEREO 1 +#define BE_MP3_MODE_DUALCHANNEL 2 +#define BE_MP3_MODE_MONO 3 + + + +#define MPEG1 1 +#define MPEG2 0 + +#ifdef _BLADEDLL +#undef FLOAT + #include +#endif + +#define CURRENT_STRUCT_VERSION 1 +#define CURRENT_STRUCT_SIZE sizeof(BE_CONFIG) // is currently 331 bytes + + +typedef enum +{ + VBR_METHOD_NONE = -1, + VBR_METHOD_DEFAULT = 0, + VBR_METHOD_OLD = 1, + VBR_METHOD_NEW = 2, + VBR_METHOD_MTRH = 3, + VBR_METHOD_ABR = 4 +} VBRMETHOD; + +typedef enum +{ + LQP_NOPRESET =-1, + + // QUALITY PRESETS + LQP_NORMAL_QUALITY = 0, + LQP_LOW_QUALITY = 1, + LQP_HIGH_QUALITY = 2, + LQP_VOICE_QUALITY = 3, + LQP_R3MIX = 4, + LQP_VERYHIGH_QUALITY = 5, + LQP_STANDARD = 6, + LQP_FAST_STANDARD = 7, + LQP_EXTREME = 8, + LQP_FAST_EXTREME = 9, + LQP_INSANE = 10, + LQP_ABR = 11, + LQP_CBR = 12, + LQP_MEDIUM = 13, + LQP_FAST_MEDIUM = 14, + + // NEW PRESET VALUES + LQP_PHONE =1000, + LQP_SW =2000, + LQP_AM =3000, + LQP_FM =4000, + LQP_VOICE =5000, + LQP_RADIO =6000, + LQP_TAPE =7000, + LQP_HIFI =8000, + LQP_CD =9000, + LQP_STUDIO =10000 + +} LAME_QUALITY_PRESET; + + + +typedef struct { + unsigned long dwConfig; // BE_CONFIG_XXXXX + // Currently only BE_CONFIG_MP3 is supported + union { + + struct { + + unsigned long dwSampleRate; // 48000, 44100 and 32000 allowed + unsigned char byMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO + unsigned short wBitrate; // 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 allowed + int bPrivate; + int bCRC; + int bCopyright; + int bOriginal; + + } mp3; // BE_CONFIG_MP3 + + struct + { + // STRUCTURE INFORMATION + unsigned long dwStructVersion; + unsigned long dwStructSize; + + // BASIC ENCODER SETTINGS + unsigned long dwSampleRate; // SAMPLERATE OF INPUT FILE + unsigned long dwReSampleRate; // DOWNSAMPLERATE, 0=ENCODER DECIDES + long nMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO + unsigned long dwBitrate; // CBR bitrate, VBR min bitrate + unsigned long dwMaxBitrate; // CBR ignored, VBR Max bitrate + long nPreset; // Quality preset, use one of the settings of the LAME_QUALITY_PRESET enum + unsigned long dwMpegVersion; // FUTURE USE, MPEG-1 OR MPEG-2 + unsigned long dwPsyModel; // FUTURE USE, SET TO 0 + unsigned long dwEmphasis; // FUTURE USE, SET TO 0 + + // BIT STREAM SETTINGS + int bPrivate; // Set Private Bit (TRUE/FALSE) + int bCRC; // Insert CRC (TRUE/FALSE) + int bCopyright; // Set Copyright Bit (TRUE/FALSE) + int bOriginal; // Set Original Bit (TRUE/FALSE) + + // VBR STUFF + int bWriteVBRHeader; // WRITE XING VBR HEADER (TRUE/FALSE) + int bEnableVBR; // USE VBR ENCODING (TRUE/FALSE) + int nVBRQuality; // VBR QUALITY 0..9 + unsigned long dwVbrAbr_bps; // Use ABR in stead of nVBRQuality + VBRMETHOD nVbrMethod; + int bNoRes; // Disable Bit resorvoir (TRUE/FALSE) + + // MISC SETTINGS + int bStrictIso; // Use strict ISO encoding rules (TRUE/FALSE) + unsigned short nQuality; // Quality Setting, HIGH unsigned char should be NOT LOW byte, otherwhise quality=5 + + // FUTURE USE, SET TO 0, align strucutre to 331 bytes + unsigned char btReserved[255-4*sizeof(unsigned long) - sizeof( unsigned short )]; + + } LHV1; // LAME header version 1 + + struct { + + unsigned long dwSampleRate; + unsigned char byMode; + unsigned short wBitrate; + unsigned char byEncodingMethod; + + } aac; + + } format; + +} BE_CONFIG, *PBE_CONFIG ATTRIBUTE_PACKED; + + +typedef struct { + + // BladeEnc DLL Version number + + unsigned char byDLLMajorVersion; + unsigned char byDLLMinorVersion; + + // BladeEnc Engine Version Number + + unsigned char byMajorVersion; + unsigned char byMinorVersion; + + // DLL Release date + + unsigned char byDay; + unsigned char byMonth; + unsigned short wYear; + + // BladeEnc Homepage URL + + char zHomepage[BE_MAX_HOMEPAGE + 1]; + + unsigned char byAlphaLevel; + unsigned char byBetaLevel; + unsigned char byMMXEnabled; + + unsigned char btReserved[125]; + + +} BE_VERSION, *PBE_VERSION ATTRIBUTE_PACKED; + +#ifndef _BLADEDLL + +typedef unsigned long (*BEINITSTREAM) (PBE_CONFIG, unsigned long *, unsigned long *, PHBE_STREAM); +typedef unsigned long (*BEENCODECHUNK) (HBE_STREAM, unsigned long, short *, unsigned char *, unsigned long *); + +// added for floating point audio -- DSPguru, jd +typedef unsigned long (*BEENCODECHUNKFLOATS16NI) (HBE_STREAM, unsigned long, float *, float *, unsigned char *, unsigned long *); +typedef unsigned long (*BEDEINITSTREAM) (HBE_STREAM, unsigned char *, unsigned long *); +typedef unsigned long (*BECLOSESTREAM) (HBE_STREAM); +typedef void (*BEVERSION) (PBE_VERSION); +typedef unsigned long (*BEWRITEVBRHEADER) (const char*); +typedef unsigned long (*BEWRITEINFOTAG) (HBE_STREAM, const char * ); + +#define TEXT_BEINITSTREAM "beInitStream" +#define TEXT_BEENCODECHUNK "beEncodeChunk" +#define TEXT_BEENCODECHUNKFLOATS16NI "beEncodeChunkFloatS16NI" +#define TEXT_BEDEINITSTREAM "beDeinitStream" +#define TEXT_BECLOSESTREAM "beCloseStream" +#define TEXT_BEVERSION "beVersion" +#define TEXT_BEWRITEVBRHEADER "beWriteVBRHeader" +#define TEXT_BEFLUSHNOGAP "beFlushNoGap" +#define TEXT_BEWRITEINFOTAG "beWriteInfoTag" + + +#else + +__declspec(dllexport) unsigned long beInitStream(PBE_CONFIG pbeConfig, Punsigned long dwSamples, Punsigned long dwBufferSize, PHBE_STREAM phbeStream); +__declspec(dllexport) unsigned long beEncodeChunk(HBE_STREAM hbeStream, unsigned long nSamples, PSHORT pSamples, Punsigned char pOutput, Punsigned long pdwOutput); + +// added for floating point audio -- DSPguru, jd +__declspec(dllexport) unsigned long beEncodeChunkFloatS16NI(HBE_STREAM hbeStream, unsigned long nSamples, PFLOAT buffer_l, PFLOAT buffer_r, Punsigned char pOutput, Punsigned long pdwOutput); +__declspec(dllexport) unsigned long beDeinitStream(HBE_STREAM hbeStream, Punsigned char pOutput, Punsigned long pdwOutput); +__declspec(dllexport) unsigned long beCloseStream(HBE_STREAM hbeStream); +__declspec(dllexport) VOID beVersion(PBE_VERSION pbeVersion); +__declspec(dllexport) unsigned long beWriteVBRHeader(LPCSTR lpszFileName); +__declspec(dllexport) unsigned long beFlushNoGap(HBE_STREAM hbeStream, Punsigned char pOutput, Punsigned long pdwOutput); +__declspec(dllexport) unsigned long beWriteInfoTag( HBE_STREAM hbeStream, LPCSTR lpszFileName ); + +#endif + +#ifndef __GNUC__ +#pragma pack(pop) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/mp3/mp3_writer.c b/third_party/mp3/mp3_writer.c new file mode 100644 index 00000000..98a0da29 --- /dev/null +++ b/third_party/mp3/mp3_writer.c @@ -0,0 +1,563 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono + * + * 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 + */ + +/* + * Contributed by: + * Toni < buldozer at aufbix dot org > + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Include BladeDLL declarations */ +#include "BladeMP3EncDLL.h" + + +#define THIS_FILE "mp3_writer.c" +#define SIGNATURE PJMEDIA_PORT_SIGNATURE('F', 'W', 'M', '3') +#define BYTES_PER_SAMPLE 2 + +static struct BladeDLL +{ + void *hModule; + int refCount; + BEINITSTREAM beInitStream; + BEENCODECHUNK beEncodeChunk; + BEDEINITSTREAM beDeinitStream; + BECLOSESTREAM beCloseStream; + BEVERSION beVersion; + BEWRITEVBRHEADER beWriteVBRHeader; + BEWRITEINFOTAG beWriteInfoTag; +} BladeDLL; + + +struct mp3_file_port +{ + pjmedia_port base; + pj_size_t total; + pj_oshandle_t fd; + pj_size_t cb_size; + pj_status_t (*cb)(pjmedia_port*, void*); + + unsigned silence_duration; + + pj_str_t mp3_filename; + pjmedia_mp3_encoder_option mp3_option; + unsigned mp3_samples_per_frame; + pj_int16_t *mp3_sample_buf; + unsigned mp3_sample_pos; + HBE_STREAM mp3_stream; + unsigned char *mp3_buf; +}; + + +static pj_status_t file_put_frame(pjmedia_port *this_port, + const pjmedia_frame *frame); +static pj_status_t file_get_frame(pjmedia_port *this_port, + pjmedia_frame *frame); +static pj_status_t file_on_destroy(pjmedia_port *this_port); + + +#if defined(PJ_WIN32) || defined(_WIN32) || defined(WIN32) + +#include +#define DLL_NAME PJ_T("LAME_ENC.DLL") + +/* + * Load BladeEncoder DLL. + */ +static pj_status_t init_blade_dll(void) +{ + if (BladeDLL.refCount == 0) { + #define GET_PROC(type, name) \ + BladeDLL.name = (type)GetProcAddress(BladeDLL.hModule, PJ_T(#name)); \ + if (BladeDLL.name == NULL) { \ + PJ_LOG(1,(THIS_FILE, "Unable to find %s in %s", #name, DLL_NAME)); \ + return PJ_RETURN_OS_ERROR(GetLastError()); \ + } + + BE_VERSION beVersion; + BladeDLL.hModule = (void*)LoadLibrary(DLL_NAME); + if (BladeDLL.hModule == NULL) { + pj_status_t status = PJ_RETURN_OS_ERROR(GetLastError()); + char errmsg[PJ_ERR_MSG_SIZE]; + + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(1,(THIS_FILE, "Unable to load %s: %s", DLL_NAME, errmsg)); + return status; + } + + GET_PROC(BEINITSTREAM, beInitStream); + GET_PROC(BEENCODECHUNK, beEncodeChunk); + GET_PROC(BEDEINITSTREAM, beDeinitStream); + GET_PROC(BECLOSESTREAM, beCloseStream); + GET_PROC(BEVERSION, beVersion); + GET_PROC(BEWRITEVBRHEADER, beWriteVBRHeader); + GET_PROC(BEWRITEINFOTAG, beWriteInfoTag); + + #undef GET_PROC + + BladeDLL.beVersion(&beVersion); + PJ_LOG(4,(THIS_FILE, "%s encoder v%d.%d loaded (%s)", DLL_NAME, + beVersion.byMajorVersion, beVersion.byMinorVersion, + beVersion.zHomepage)); + } + ++BladeDLL.refCount; + return PJ_SUCCESS; +} + +/* + * Decrement the reference counter of the DLL. + */ +static void deinit_blade_dll() +{ + --BladeDLL.refCount; + if (BladeDLL.refCount == 0 && BladeDLL.hModule) { + FreeLibrary(BladeDLL.hModule); + BladeDLL.hModule = NULL; + PJ_LOG(4,(THIS_FILE, "%s unloaded", DLL_NAME)); + } +} + +#else + +static pj_status_t init_blade_dll(void) +{ + PJ_LOG(1,(THIS_FILE, "Error: MP3 writer port only works on Windows for now")); + return PJ_ENOTSUP; +} + +static void deinit_blade_dll() +{ +} +#endif + + + +/* + * Initialize MP3 encoder. + */ +static pj_status_t init_mp3_encoder(struct mp3_file_port *fport, + pj_pool_t *pool) +{ + BE_CONFIG LConfig; + unsigned long InSamples; + unsigned long OutBuffSize; + long MP3Err; + + /* + * Initialize encoder configuration. + */ + pj_bzero(&LConfig, sizeof(BE_CONFIG)); + LConfig.dwConfig = BE_CONFIG_LAME; + LConfig.format.LHV1.dwStructVersion = 1; + LConfig.format.LHV1.dwStructSize = sizeof(BE_CONFIG); + LConfig.format.LHV1.dwSampleRate = fport->base.info.clock_rate; + LConfig.format.LHV1.dwReSampleRate = 0; + + if (fport->base.info.channel_count==1) + LConfig.format.LHV1.nMode = BE_MP3_MODE_MONO; + else if (fport->base.info.channel_count==2) + LConfig.format.LHV1.nMode = BE_MP3_MODE_STEREO; + else + return PJMEDIA_ENCCHANNEL; + + LConfig.format.LHV1.dwBitrate = fport->mp3_option.bit_rate / 1000; + LConfig.format.LHV1.nPreset = LQP_NOPRESET; + LConfig.format.LHV1.bCopyright = 0; + LConfig.format.LHV1.bCRC = 1; + LConfig.format.LHV1.bOriginal = 1; + LConfig.format.LHV1.bPrivate = 0; + + if (!fport->mp3_option.vbr) { + LConfig.format.LHV1.nVbrMethod = VBR_METHOD_NONE; + LConfig.format.LHV1.bWriteVBRHeader = 0; + LConfig.format.LHV1.bEnableVBR = 0; + } else { + LConfig.format.LHV1.nVbrMethod = VBR_METHOD_DEFAULT; + LConfig.format.LHV1.bWriteVBRHeader = 1; + LConfig.format.LHV1.dwVbrAbr_bps = fport->mp3_option.bit_rate; + LConfig.format.LHV1.nVBRQuality = (pj_uint16_t) + fport->mp3_option.quality; + LConfig.format.LHV1.bEnableVBR = 1; + } + + LConfig.format.LHV1.nQuality = (pj_uint16_t) + (((0-fport->mp3_option.quality-1)<<8) | + fport->mp3_option.quality); + + /* + * Init MP3 stream. + */ + InSamples = 0; + MP3Err = BladeDLL.beInitStream(&LConfig, &InSamples, &OutBuffSize, + &fport->mp3_stream); + if (MP3Err != BE_ERR_SUCCESSFUL) + return PJMEDIA_ERROR; + + /* + * Allocate sample buffer. + */ + fport->mp3_samples_per_frame = (unsigned)InSamples; + fport->mp3_sample_buf = pj_pool_alloc(pool, fport->mp3_samples_per_frame * 2); + if (!fport->mp3_sample_buf) + return PJ_ENOMEM; + + /* + * Allocate encoded MP3 buffer. + */ + fport->mp3_buf = pj_pool_alloc(pool, (pj_size_t)OutBuffSize); + if (fport->mp3_buf == NULL) + return PJ_ENOMEM; + + + return PJ_SUCCESS; +} + + +/* + * Create MP3 file writer port. + */ +PJ_DEF(pj_status_t) +pjmedia_mp3_writer_port_create( pj_pool_t *pool, + const char *filename, + unsigned sampling_rate, + unsigned channel_count, + unsigned samples_per_frame, + unsigned bits_per_sample, + const pjmedia_mp3_encoder_option *param_option, + pjmedia_port **p_port ) +{ + struct mp3_file_port *fport; + pj_status_t status; + + status = init_blade_dll(); + if (status != PJ_SUCCESS) + return status; + + /* Check arguments. */ + PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); + + /* Only supports 16bits per sample for now. */ + PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + + /* Create file port instance. */ + fport = pj_pool_zalloc(pool, sizeof(struct mp3_file_port)); + PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM); + + /* Initialize port info. */ + pj_strdup2_with_null(pool, &fport->mp3_filename, filename); + pjmedia_port_info_init(&fport->base.info, &fport->mp3_filename, SIGNATURE, + sampling_rate, channel_count, bits_per_sample, + samples_per_frame); + + fport->base.get_frame = &file_get_frame; + fport->base.put_frame = &file_put_frame; + fport->base.on_destroy = &file_on_destroy; + + + /* Open file in write and read mode. + * We need the read mode because we'll modify the WAVE header once + * the recording has completed. + */ + status = pj_file_open(pool, filename, PJ_O_WRONLY, &fport->fd); + if (status != PJ_SUCCESS) { + deinit_blade_dll(); + return status; + } + + /* Copy and initialize option with default settings */ + if (param_option) { + pj_memcpy(&fport->mp3_option, param_option, + sizeof(pjmedia_mp3_encoder_option)); + } else { + pj_bzero(&fport->mp3_option, sizeof(pjmedia_mp3_encoder_option)); + fport->mp3_option.vbr = PJ_TRUE; + } + + /* Calculate bitrate if it's not specified, only if it's not VBR. */ + if (fport->mp3_option.bit_rate == 0 && !fport->mp3_option.vbr) + fport->mp3_option.bit_rate = sampling_rate * channel_count; + + /* Set default quality if it's not specified */ + if (fport->mp3_option.quality == 0) + fport->mp3_option.quality = 2; + + /* Init mp3 encoder */ + status = init_mp3_encoder(fport, pool); + if (status != PJ_SUCCESS) { + pj_file_close(fport->fd); + deinit_blade_dll(); + return status; + } + + /* Done. */ + *p_port = &fport->base; + + PJ_LOG(4,(THIS_FILE, + "MP3 file writer '%.*s' created: samp.rate=%dKHz, " + "bitrate=%dkbps%s, quality=%d", + (int)fport->base.info.name.slen, + fport->base.info.name.ptr, + fport->base.info.clock_rate/1000, + fport->mp3_option.bit_rate/1000, + (fport->mp3_option.vbr ? " (VBR)" : ""), + fport->mp3_option.quality)); + + return PJ_SUCCESS; +} + + + +/* + * Register callback. + */ +PJ_DEF(pj_status_t) +pjmedia_mp3_writer_port_set_cb( pjmedia_port *port, + pj_size_t pos, + void *user_data, + pj_status_t (*cb)(pjmedia_port *port, + void *usr_data)) +{ + struct mp3_file_port *fport; + + /* Sanity check */ + PJ_ASSERT_RETURN(port && cb, PJ_EINVAL); + + /* Check that this is really a writer port */ + PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP); + + fport = (struct mp3_file_port*) port; + + fport->cb_size = pos; + fport->base.port_data.pdata = user_data; + fport->cb = cb; + + return PJ_SUCCESS; + +} + + +/* + * Put a frame into the buffer. When the buffer is full, flush the buffer + * to the file. + */ +static pj_status_t file_put_frame(pjmedia_port *this_port, + const pjmedia_frame *frame) +{ + struct mp3_file_port *fport = (struct mp3_file_port *)this_port; + unsigned long MP3Err; + pj_ssize_t bytes; + pj_status_t status; + unsigned long WriteSize; + + /* Record silence if input is no-frame */ + if (frame->type == PJMEDIA_FRAME_TYPE_NONE || frame->size == 0) { + unsigned samples_left = fport->base.info.samples_per_frame; + unsigned samples_copied = 0; + + /* Only want to record at most 1 second of silence */ + if (fport->silence_duration >= fport->base.info.clock_rate) + return PJ_SUCCESS; + + while (samples_left) { + unsigned samples_needed = fport->mp3_samples_per_frame - + fport->mp3_sample_pos; + if (samples_needed > samples_left) + samples_needed = samples_left; + + pjmedia_zero_samples(fport->mp3_sample_buf + fport->mp3_sample_pos, + samples_needed); + fport->mp3_sample_pos += samples_needed; + samples_left -= samples_needed; + samples_copied += samples_needed; + + /* Encode if we have full frame */ + if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) { + + /* Clear position */ + fport->mp3_sample_pos = 0; + + /* Encode ! */ + MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream, + fport->mp3_samples_per_frame, + fport->mp3_sample_buf, + fport->mp3_buf, + &WriteSize); + if (MP3Err != BE_ERR_SUCCESSFUL) + return PJMEDIA_ERROR; + + /* Write the chunk */ + bytes = WriteSize; + status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); + if (status != PJ_SUCCESS) + return status; + + /* Increment total written. */ + fport->total += bytes; + } + } + + fport->silence_duration += fport->base.info.samples_per_frame; + + } + /* If encoder is expecting different sample size, then we need to + * buffer the samples. + */ + else if (fport->mp3_samples_per_frame != + fport->base.info.samples_per_frame) + { + unsigned samples_left = frame->size / 2; + unsigned samples_copied = 0; + const pj_int16_t *src_samples = frame->buf; + + fport->silence_duration = 0; + + while (samples_left) { + unsigned samples_needed = fport->mp3_samples_per_frame - + fport->mp3_sample_pos; + if (samples_needed > samples_left) + samples_needed = samples_left; + + pjmedia_copy_samples(fport->mp3_sample_buf + fport->mp3_sample_pos, + src_samples + samples_copied, + samples_needed); + fport->mp3_sample_pos += samples_needed; + samples_left -= samples_needed; + samples_copied += samples_needed; + + /* Encode if we have full frame */ + if (fport->mp3_sample_pos == fport->mp3_samples_per_frame) { + + /* Clear position */ + fport->mp3_sample_pos = 0; + + /* Encode ! */ + MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream, + fport->mp3_samples_per_frame, + fport->mp3_sample_buf, + fport->mp3_buf, + &WriteSize); + if (MP3Err != BE_ERR_SUCCESSFUL) + return PJMEDIA_ERROR; + + /* Write the chunk */ + bytes = WriteSize; + status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); + if (status != PJ_SUCCESS) + return status; + + /* Increment total written. */ + fport->total += bytes; + } + } + + } else { + + fport->silence_duration = 0; + + /* Encode ! */ + MP3Err = BladeDLL.beEncodeChunk(fport->mp3_stream, + fport->mp3_samples_per_frame, + frame->buf, + fport->mp3_buf, + &WriteSize); + if (MP3Err != BE_ERR_SUCCESSFUL) + return PJMEDIA_ERROR; + + /* Write the chunk */ + bytes = WriteSize; + status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); + if (status != PJ_SUCCESS) + return status; + + /* Increment total written. */ + fport->total += bytes; + } + + /* Increment total written, and check if we need to call callback */ + + if (fport->cb && fport->total >= fport->cb_size) { + pj_status_t (*cb)(pjmedia_port*, void*); + pj_status_t status; + + cb = fport->cb; + fport->cb = NULL; + + status = (*cb)(this_port, this_port->port_data.pdata); + return status; + } + + return PJ_SUCCESS; +} + +/* + * Get frame, basicy is a no-op operation. + */ +static pj_status_t file_get_frame(pjmedia_port *this_port, + pjmedia_frame *frame) +{ + PJ_UNUSED_ARG(this_port); + PJ_UNUSED_ARG(frame); + return PJ_EINVALIDOP; +} + + +/* + * Close the port, modify file header with updated file length. + */ +static pj_status_t file_on_destroy(pjmedia_port *this_port) +{ + struct mp3_file_port *fport = (struct mp3_file_port*)this_port; + pj_status_t status; + unsigned long WriteSize; + unsigned long MP3Err; + + + /* Close encoder */ + MP3Err = BladeDLL.beDeinitStream(fport->mp3_stream, fport->mp3_buf, + &WriteSize); + if (MP3Err == BE_ERR_SUCCESSFUL) { + pj_ssize_t bytes = WriteSize; + status = pj_file_write(fport->fd, fport->mp3_buf, &bytes); + } + + /* Close file */ + status = pj_file_close(fport->fd); + + /* Write additional VBR header */ + if (fport->mp3_option.vbr) { + MP3Err = BladeDLL.beWriteVBRHeader(fport->mp3_filename.ptr); + } + + + /* Decrement DLL reference counter */ + deinit_blade_dll(); + + /* Done. */ + return PJ_SUCCESS; +} + -- cgit v1.2.3