/* * 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; }