Created
June 23, 2025 15:11
-
-
Save garyachy/80ded365ae0203752cce2eb97cecdac4 to your computer and use it in GitHub Desktop.
G711ToPcmTranscoder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class G711ToPcmTranscoder | |
{ | |
public: | |
G711ToPcmTranscoder( | |
int sample_rate, | |
int channels, | |
int output_sample_rate, | |
AVSampleFormat sample_fmt | |
): | |
sample_rate_( sample_rate ), | |
channels_( channels ), | |
output_sample_rate_( output_sample_rate ), | |
sample_fmt_( sample_fmt ) | |
{ | |
const AVCodec *codec = avcodec_find_decoder( AV_CODEC_ID_PCM_MULAW ); | |
if( !codec ) | |
{ | |
throw std::runtime_error( "Codec 'pcm_mulaw' not found" ); | |
} | |
codec_ctx = avcodec_alloc_context3( codec ); | |
if( !codec_ctx ) | |
{ | |
throw std::runtime_error( "Could not allocate audio codec context" ); | |
} | |
// Manually set parameters for G.711 u-law, as we are decoding a raw stream | |
codec_ctx->sample_rate = sample_rate_; | |
codec_ctx->channels = channels_; | |
codec_ctx->channel_layout = | |
channels_ == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; | |
codec_ctx->sample_fmt = sample_fmt_; | |
if( avcodec_open2( codec_ctx, codec, nullptr ) < 0 ) | |
{ | |
avcodec_free_context( &codec_ctx ); | |
throw std::runtime_error( "Could not open codec" ); | |
} | |
swr_ctx = swr_alloc_set_opts( | |
nullptr, AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_FLT, | |
output_sample_rate_, // out | |
codec_ctx->channel_layout, codec_ctx->sample_fmt, | |
codec_ctx->sample_rate, // in | |
0, nullptr | |
); | |
if( !swr_ctx || swr_init( swr_ctx ) < 0 ) | |
{ | |
swr_free( &swr_ctx ); | |
avcodec_free_context( &codec_ctx ); | |
throw std::runtime_error( "Failed to initialize the resampling context" ); | |
} | |
pkt = av_packet_alloc(); | |
frame = av_frame_alloc(); | |
if( !pkt || !frame ) | |
{ | |
av_packet_free( &pkt ); | |
av_frame_free( &frame ); | |
swr_free( &swr_ctx ); | |
avcodec_free_context( &codec_ctx ); | |
throw std::runtime_error( "Failed to allocate packet or frame" ); | |
} | |
} | |
~G711ToPcmTranscoder() | |
{ | |
av_frame_free( &frame ); | |
av_packet_free( &pkt ); | |
swr_free( &swr_ctx ); | |
avcodec_free_context( &codec_ctx ); | |
} | |
std::vector<float> transcode( const u_char *g711_payload, int payload_size ) | |
{ | |
std::vector<float> pcm_data; | |
pkt->data = const_cast<uint8_t *>( g711_payload ); | |
pkt->size = payload_size; | |
if( avcodec_send_packet( codec_ctx, pkt ) >= 0 ) | |
{ | |
while( avcodec_receive_frame( codec_ctx, frame ) >= 0 ) | |
{ | |
resample_and_store( frame, pcm_data ); | |
av_frame_unref( frame ); | |
} | |
} | |
return pcm_data; | |
} | |
std::vector<float> flush() | |
{ | |
std::vector<float> pcm_data; | |
// Flush the decoder | |
avcodec_send_packet( codec_ctx, nullptr ); | |
while( avcodec_receive_frame( codec_ctx, frame ) >= 0 ) | |
{ | |
resample_and_store( frame, pcm_data ); | |
av_frame_unref( frame ); | |
} | |
// Flush the resampler | |
resample_and_store( nullptr, pcm_data ); | |
return pcm_data; | |
} | |
private: | |
void | |
resample_and_store( AVFrame *decoded_frame, std::vector<float> &pcm_data ) | |
{ | |
const int frame_size = decoded_frame ? decoded_frame->nb_samples : 0; | |
const uint8_t **in_data = | |
decoded_frame ? (const uint8_t **)decoded_frame->data : nullptr; | |
uint8_t *output_buffer = nullptr; | |
int output_buffer_size = av_rescale_rnd( | |
swr_get_delay( swr_ctx, codec_ctx->sample_rate ) + frame_size, 16000, | |
codec_ctx->sample_rate, AV_ROUND_UP | |
); | |
if( output_buffer_size <= 0 ) | |
{ | |
return; | |
} | |
av_samples_alloc( | |
&output_buffer, nullptr, 1, output_buffer_size, AV_SAMPLE_FMT_FLT, 0 | |
); | |
int resampled_samples = swr_convert( | |
swr_ctx, &output_buffer, output_buffer_size, in_data, frame_size | |
); | |
if( resampled_samples > 0 ) | |
{ | |
pcm_data.insert( | |
pcm_data.end(), (float *)output_buffer, | |
(float *)output_buffer + resampled_samples | |
); | |
} | |
av_freep( &output_buffer ); | |
} | |
AVCodecContext *codec_ctx = nullptr; | |
SwrContext *swr_ctx = nullptr; | |
AVPacket *pkt = nullptr; | |
AVFrame *frame = nullptr; | |
int sample_rate_; | |
int channels_; | |
int output_sample_rate_; | |
AVSampleFormat sample_fmt_; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment