247 lines
5.4 KiB
C++
247 lines
5.4 KiB
C++
|
|
/***************************************************************************************
|
||
|
|
*
|
||
|
|
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||
|
|
*
|
||
|
|
* By downloading, copying, installing or using the software you agree to this license.
|
||
|
|
* If you do not agree to this license, do not download, install,
|
||
|
|
* copy or use the software.
|
||
|
|
*
|
||
|
|
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
||
|
|
*
|
||
|
|
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||
|
|
*
|
||
|
|
* Unless required by applicable law or agreed to in writing, software distributed
|
||
|
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||
|
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||
|
|
* language governing permissions and limitations under the License.
|
||
|
|
*
|
||
|
|
****************************************************************************************/
|
||
|
|
|
||
|
|
#include "sys_inc.h"
|
||
|
|
#include "audio_decoder.h"
|
||
|
|
#include "avcodec_mutex.h"
|
||
|
|
#include "media_codec.h"
|
||
|
|
|
||
|
|
CAudioDecoder::CAudioDecoder()
|
||
|
|
{
|
||
|
|
m_bInited = FALSE;
|
||
|
|
m_nSamplerate = 0;
|
||
|
|
m_nChannels = 0;
|
||
|
|
|
||
|
|
m_pCodec = NULL;
|
||
|
|
m_pContext = NULL;
|
||
|
|
m_pFrame = NULL;
|
||
|
|
m_pResampleFrame = NULL;
|
||
|
|
m_pSwrCtx = NULL;
|
||
|
|
m_nDstFormat = AV_SAMPLE_FMT_S16;
|
||
|
|
|
||
|
|
m_pCallback = NULL;
|
||
|
|
m_pUserdata = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
CAudioDecoder::~CAudioDecoder()
|
||
|
|
{
|
||
|
|
uninit();
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CAudioDecoder::init(enum AVCodecID codec, int samplerate, int channels, int bitpersample)
|
||
|
|
{
|
||
|
|
m_pCodec = avcodec_find_decoder(codec);
|
||
|
|
if (m_pCodec == NULL)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "%s, m_pCodec is NULL\r\n", __FUNCTION__);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_pContext = avcodec_alloc_context3(m_pCodec);
|
||
|
|
if (NULL == m_pContext)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "%s, avcodec_alloc_context3 failed\r\n", __FUNCTION__);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_pContext->sample_rate = samplerate;
|
||
|
|
m_pContext->channels = channels;
|
||
|
|
m_pContext->channel_layout = av_get_default_channel_layout(channels);
|
||
|
|
|
||
|
|
if (AV_CODEC_ID_ADPCM_G726 == codec)
|
||
|
|
{
|
||
|
|
if (0 == bitpersample)
|
||
|
|
{
|
||
|
|
bitpersample = 2;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_pContext->bits_per_coded_sample = bitpersample;
|
||
|
|
m_pContext->bit_rate = bitpersample * 8000;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (avcodec_thread_open(m_pContext, m_pCodec, NULL) < 0)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "%s, avcodec_thread_open failed\r\n", __FUNCTION__);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_pContext->sample_fmt != m_nDstFormat)
|
||
|
|
{
|
||
|
|
m_pSwrCtx = swr_alloc_set_opts(NULL,
|
||
|
|
av_get_default_channel_layout(channels), m_nDstFormat, samplerate,
|
||
|
|
av_get_default_channel_layout(channels), m_pContext->sample_fmt, samplerate, 0, NULL);
|
||
|
|
|
||
|
|
swr_init(m_pSwrCtx);
|
||
|
|
}
|
||
|
|
|
||
|
|
m_pFrame = av_frame_alloc();
|
||
|
|
if (NULL == m_pFrame)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "%s, av_frame_alloc failed\r\n", __FUNCTION__);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
m_nSamplerate = samplerate;
|
||
|
|
m_nChannels = channels;
|
||
|
|
|
||
|
|
m_bInited = TRUE;
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CAudioDecoder::init(int codec, int samplerate, int channels, int bitpersample)
|
||
|
|
{
|
||
|
|
return init(to_audio_avcodecid(codec), samplerate, channels, bitpersample);
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CAudioDecoder::decode(uint8 * data, int len, int64_t pts)
|
||
|
|
{
|
||
|
|
AVPacket packet;
|
||
|
|
av_init_packet(&packet);
|
||
|
|
packet.data = data;
|
||
|
|
packet.size = len;
|
||
|
|
packet.pts = packet.dts = pts;
|
||
|
|
return decode(&packet);
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL CAudioDecoder::decode(AVPacket *pkt)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
if (!m_bInited)
|
||
|
|
{
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* send the packet with the compressed data to the decoder */
|
||
|
|
ret = avcodec_send_packet(m_pContext, pkt);
|
||
|
|
if (ret < 0)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "%s, error submitting the packet to the decoder\r\n", __FUNCTION__);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* read all the output frames (in general there may be any number of them */
|
||
|
|
while (ret >= 0)
|
||
|
|
{
|
||
|
|
ret = avcodec_receive_frame(m_pContext, m_pFrame);
|
||
|
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||
|
|
{
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
else if (ret < 0)
|
||
|
|
{
|
||
|
|
log_print(HT_LOG_ERR, "%s, error during decoding\r\n", __FUNCTION__);
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
render(m_pFrame);
|
||
|
|
|
||
|
|
av_frame_unref(m_pFrame);
|
||
|
|
}
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CAudioDecoder::flush()
|
||
|
|
{
|
||
|
|
if (NULL == m_pContext ||
|
||
|
|
NULL == m_pContext->codec ||
|
||
|
|
!(m_pContext->codec->capabilities | AV_CODEC_CAP_DELAY))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
decode(NULL);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CAudioDecoder::uninit()
|
||
|
|
{
|
||
|
|
flush();
|
||
|
|
|
||
|
|
if (m_pContext)
|
||
|
|
{
|
||
|
|
avcodec_thread_close(m_pContext);
|
||
|
|
avcodec_free_context(&m_pContext);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_pFrame)
|
||
|
|
{
|
||
|
|
av_frame_free(&m_pFrame);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_pResampleFrame)
|
||
|
|
{
|
||
|
|
av_frame_free(&m_pResampleFrame);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_pSwrCtx)
|
||
|
|
{
|
||
|
|
swr_free(&m_pSwrCtx);
|
||
|
|
}
|
||
|
|
|
||
|
|
m_bInited = FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CAudioDecoder::render(AVFrame * frame)
|
||
|
|
{
|
||
|
|
if (NULL == frame)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (m_pSwrCtx)
|
||
|
|
{
|
||
|
|
if (NULL == m_pResampleFrame)
|
||
|
|
{
|
||
|
|
m_pResampleFrame = av_frame_alloc();
|
||
|
|
if (NULL == m_pResampleFrame)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
m_pResampleFrame->sample_rate = m_nSamplerate;
|
||
|
|
m_pResampleFrame->format = m_nDstFormat;
|
||
|
|
m_pResampleFrame->channels = m_nChannels;
|
||
|
|
m_pResampleFrame->channel_layout = av_get_default_channel_layout(m_nChannels);
|
||
|
|
|
||
|
|
int swrret = swr_convert_frame(m_pSwrCtx, m_pResampleFrame, frame);
|
||
|
|
if (swrret == 0)
|
||
|
|
{
|
||
|
|
if (m_pCallback)
|
||
|
|
{
|
||
|
|
m_pResampleFrame->pts = frame->pts;
|
||
|
|
m_pResampleFrame->pkt_dts = frame->pkt_dts;
|
||
|
|
|
||
|
|
m_pCallback(m_pResampleFrame, m_pUserdata);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
av_frame_unref(m_pResampleFrame);
|
||
|
|
}
|
||
|
|
else if (m_pCallback)
|
||
|
|
{
|
||
|
|
m_pCallback(frame, m_pUserdata);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|