/*
* LAME MP3 encoder for DirectShow
* LAME encoder wrapper
*
* Copyright (c) 2000-2005 Marie Orlova, Peter Gubanov, Vitaly Ivanov, Elecard Ltd.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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.
*/
#include <streams.h>
#include "Encoder.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CEncoder::CEncoder() :
m_bInpuTypeSet(FALSE),
m_bOutpuTypeSet(FALSE),
m_bFinished(FALSE),
m_outOffset(0),
m_outReadOffset(0),
m_frameCount(0),
pgf(NULL)
{
m_outFrameBuf = new unsigned char[OUT_BUFFER_SIZE];
}
CEncoder::~CEncoder()
{
Close();
if (m_outFrameBuf)
delete [] m_outFrameBuf;
}
//////////////////////////////////////////////////////////////////////
// SetInputType - check if given input type is supported
//////////////////////////////////////////////////////////////////////
HRESULT CEncoder::SetInputType(LPWAVEFORMATEX lpwfex, bool bJustCheck)
{
CAutoLock l(&m_lock);
if (lpwfex->wFormatTag == WAVE_FORMAT_PCM)
{
if (lpwfex->nChannels == 1 || lpwfex->nChannels == 2)
{
if (lpwfex->nSamplesPerSec == 48000 ||
lpwfex->nSamplesPerSec == 44100 ||
lpwfex->nSamplesPerSec == 32000 ||
lpwfex->nSamplesPerSec == 24000 ||
lpwfex->nSamplesPerSec == 22050 ||
lpwfex->nSamplesPerSec == 16000 ||
lpwfex->nSamplesPerSec == 12000 ||
lpwfex->nSamplesPerSec == 11025 ||
lpwfex->nSamplesPerSec == 8000)
{
if (lpwfex->wBitsPerSample == 16)
{
if (!bJustCheck)
{
memcpy(&m_wfex, lpwfex, sizeof(WAVEFORMATEX));
m_bInpuTypeSet = true;
}
return S_OK;
}
}
}
}
if (!bJustCheck)
m_bInpuTypeSet = false;
return E_INVALIDARG;
}
//////////////////////////////////////////////////////////////////////
// SetOutputType - try to initialize encoder with given output type
//////////////////////////////////////////////////////////////////////
HRESULT CEncoder::SetOutputType(MPEG_ENCODER_CONFIG &mabsi)
{
CAutoLock l(&m_lock);
m_mabsi = mabsi;
m_bOutpuTypeSet = true;
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// SetDefaultOutputType - sets default MPEG audio properties according
// to input type
//////////////////////////////////////////////////////////////////////
HRESULT CEncoder::SetDefaultOutputType(LPWAVEFORMATEX lpwfex)
{
CAutoLock l(&m_lock);
if(lpwfex->nChannels == 1 || m_mabsi.bForceMono)
m_mabsi.ChMode = MONO;
if((lpwfex->nSamplesPerSec < m_mabsi.dwSampleRate) || (lpwfex->nSamplesPerSec % m_mabsi.dwSampleRate != 0))
m_mabsi.dwSampleRate = lpwfex->nSamplesPerSec;
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// Init - initialized or reiniyialized encoder SDK with given input
// and output settings
//////////////////////////////////////////////////////////////////////
HRESULT CEncoder::Init()
{
CAutoLock l(&m_lock);
m_outOffset = 0;
m_outReadOffset = 0;
m_bFinished = FALSE;
m_frameCount = 0;
if (!pgf)
{
if (!m_bInpuTypeSet || !m_bOutpuTypeSet)
return E_UNEXPECTED;
// Init Lame library
// note: newer, safer interface which doesn't
// allow or require direct access to 'gf' struct is being written
// see the file 'API' included with LAME.
if (pgf = lame_init())
{
pgf->num_channels = m_wfex.nChannels;
pgf->in_samplerate = m_wfex.nSamplesPerSec;
pgf->out_samplerate = m_mabsi.dwSampleRate;
if ((pgf->out_samplerate >= 32000) && (m_mabsi.dwBitrate < 32))
pgf->brate = 32;
else
pgf->brate = m_mabsi.dwBitrate;
pgf->VBR = m_mabsi.vmVariable;
pgf->VBR_min_bitrate_kbps = m_mabsi.dwVariableMin;
pgf->VBR_max_bitrate_kbps = m_mabsi.dwVariableMax;
pgf->copyright = m_mabsi.bCopyright;
pgf->original = m_mabsi.bOriginal;
pgf->error_protection = m_mabsi.bCRCProtect;
pgf->bWriteVbrTag = m_mabsi.dwXingTag;
pgf->strict_ISO = m_mabsi.dwStrictISO;
pgf->VBR_hard_min = m_mabsi.dwEnforceVBRmin;
if (pgf->num_channels == 2 && !m_mabsi.bForceMono)
{
int act_br = pgf->VBR ? pgf->VBR_min_bitrate_kbps + pgf->VBR_max_bitrate_kbps / 2 : pgf->brate;
// Disabled. It's for user's consideration now
//int rel = pgf->out_samplerate / (act_br + 1);
//pgf->mode = rel < 200 ? m_mabsi.ChMode : JOINT_STEREO;
pgf->mode = m_mabsi.ChMode;
}
else
pgf->mode = MONO;
if (pgf->mode == JOINT_STEREO)
pgf->force_ms = m_mabsi.dwForceMS;
else
pgf->force_ms = 0;
pgf->mode_fixed = m_mabsi.dwModeFixed;
if (m_mabsi.dwVoiceMode != 0)
{
pgf->lowpassfreq = 12000;
pgf->VBR_max_bitrate_kbps = 160;
}
if (m_mabsi.dwKeepAllFreq != 0)
{
pgf->lowpassfreq = -1;
pgf->highpassfreq = -1;
}
pgf->quality = m_mabsi.dwQuality;
pgf->VBR_q = m_mabsi.dwVBRq;
lame_init_params(pgf);
// encoder delay compensation
{
short * start_padd = (short *)calloc(48, pgf->num_channels * sizeof(short));
if (pgf->num_channels == 2)
lame_encode_buffer_interleaved(pgf, start_padd, 48, m_outFrameBuf, OUT_BUFFER_SIZE);
else
lame_encode_buffer(pgf, start_padd, start_padd, 48, m_outFrameBuf, OUT_BUFFER_SIZE);
free(start_padd);
}
return S_OK;
}
return E_FAIL;
}
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// Close - closes encoder
//////////////////////////////////////////////////////////////////////
HRESULT CEncoder::Close()
{
CAutoLock l(&m_lock);
if (pgf)
{
lame_close(pgf);
pgf = NULL;
}
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// Encode - encodes data placed on pdata and returns
// the number of processed bytes
//////////////////////////////////////////////////////////////////////
int CEncoder::Encode(const short * pdata, int data_size)
{
CAutoLock l(&m_lock);
if (!pgf || !m_outFrameBuf || !pdata || data_size < 0 || (data_size & (sizeof(short) - 1)))
return -1;
// some data left in the buffer, shift to start
if (m_outReadOffset > 0)
{
if (m_outOffset > m_outReadOffset)
memmove(m_outFrameBuf, m_outFrameBuf + m_outReadOffset, m_outOffset - m_outReadOffset);
m_outOffset -= m_outReadOffset;
}
m_outReadOffset = 0;
m_bFinished = FALSE;
int bytes_processed = 0;
while (1)
{
int nsamples = (data_size - bytes_processed) / (sizeof(short) * pgf->num_channels);
if (nsamples <= 0)
break;
if (nsamples > 1152)
nsamples = 1152;
if (m_outOffset >= OUT_BUFFER_MAX)
break;
int out_bytes = 0;
if (pgf->num_channels == 2)
out_bytes = lame_encode_buffer_interleaved(
pgf,
(short *)(pdata + (bytes_processed / sizeof(short))),
nsamples,
m_outFrameBuf + m_outOffset,
OUT_BUFFER_SIZE - m_outOffset);
else
out_bytes = lame_encode_buffer(
pgf,
pdata + (bytes_processed / sizeof(short)),
pdata + (bytes_processed / sizeof(short)),
nsamples,
m_outFrameBuf + m_outOffset,
OUT_BUFFER_SIZE - m_outOffset);
if (out_bytes < 0)
return -1;
m_outOffset += out_bytes;
bytes_processed += nsamples * pgf->num_channels * sizeof(short);
}
return bytes_processed;
}
//
// Finsh - flush the buffered samples
//
HRESULT CEncoder::Finish()
{
CAutoLock l(&m_lock);
if (!pgf || !m_outFrameBuf || (m_outOffset >= OUT_BUFFER_MAX))
return E_FAIL;
m_outOffset += lame_encode_flush(pgf, m_outFrameBuf + m_outOffset, OUT_BUFFER_SIZE - m_outOffset);
m_bFinished = TRUE;
return S_OK;
}
int getFrameLength(const unsigned char * pdata)
{
if (!pdata || pdata[0] != 0xff || (pdata[1] & 0xe0) != 0xe0)
return -1;
const int sample_rate_tab[4][4] =
{
{11025,12000,8000,1},
{1,1,1,1},
{22050,24000,16000,1},
{44100,48000,32000,1}
};
#define MPEG_VERSION_RESERVED 1
#define MPEG_VERSION_1 3
#define LAYER_III 1
#define BITRATE_FREE 0
#define BITRATE_RESERVED 15
#define SRATE_RESERVED 3
#define EMPHASIS_RESERVED 2
int version_id = (pdata[1] & 0x18) >> 3;
int layer = (pdata[1] & 0x06) >> 1;
int bitrate_id = (pdata[2] & 0xF0) >> 4;
int sample_rate_id = (pdata[2] & 0x0C) >> 2;
int padding = (pdata[2] & 0x02) >> 1;
int emphasis = pdata[3] & 0x03;
if (version_id != MPEG_VERSION_RESERVED &&
layer == LAYER_III &&
bitrate_id != BITRATE_FREE &&
bitrate_id != BITRATE_RESERVED &&
sample_rate_id != SRATE_RESERVED &&
emphasis != EMPHASIS_RESERVED)
{
int spf = (version_id == MPEG_VERSION_1) ? 1152 : 576;
int sample_rate = sample_rate_tab[version_id][sample_rate_id];
int bitrate = dwBitRateValue[version_id != MPEG_VERSION_1][bitrate_id - 1] * 1000;
return (bitrate * spf) / (8 * sample_rate) + padding;
}
return -1;
}
int CEncoder::GetFrame(const unsigned char ** pframe)
{
if (!pgf || !m_outFrameBuf || !pframe)
return -1;
while ((m_outOffset - m_outReadOffset) > 4)
{
int frame_length = getFrameLength(m_outFrameBuf + m_outReadOffset);
if (frame_length < 0)
{
m_outReadOffset++;
}
else if (frame_length <= (m_outOffset - m_outReadOffset))
{
*pframe = m_outFrameBuf + m_outReadOffset;
m_outReadOffset += frame_length;
m_frameCount++;
// don't deliver the first and the last frames
if (m_frameCount != 1 && !(m_bFinished && (m_outOffset - m_outReadOffset) < 5))
return frame_length;
}
else
break;
}
return 0;
}