概述
本文基于ffmpeg,演示g711a,g711u,g726,g726le的编码以及编码后的文件如何播放测试。
g711本质还是pcm,不过是将16位带符号pcm数据压缩为8位pcm数据。g711a是取s16le格式的高13位;g711u则是取s16le格式的高14位。实际压缩比是2:1,具体压缩算法可参考如下博客:https://blog.csdn.net/q2519008/article/details/80900838。
编码代码
encode_audio.c :
/**
* func: g711a/g711u/g726/g726le encoder.
* author: Francis Fan
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/samplefmt.h>
/* check that a given sample format is supported by the encoder */
static int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
{
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE) {
if (*p == sample_fmt)
return 1;
p++;
}
return 0;
}
static void encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,
FILE *output)
{
int ret;
/* send the frame for encoding */
ret = avcodec_send_frame(ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending the frame to the encoder\n");
exit(1);
}
/* read all the available output packets (in general there may be any
* number of them */
while (ret >= 0) {
ret = avcodec_receive_packet(ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error encoding audio frame\n");
exit(1);
}
fwrite(pkt->data, 1, pkt->size, output);
av_packet_unref(pkt);
}
}
static char optstr[] = "?i:o:c:r:f:b:t:";
static void print_usage(char *name)
{
fprintf(stderr, "Usage: \n");
fprintf(stderr, "\t%s -i <infile> -o <outfile> -c <channel>"
" -r <samplerate> -f <formate> -b <bitrate>"
"-t <encodertype>\n", name);
fprintf(stderr, "Notice: encoder type:\n");
fprintf(stderr, "\t[alaw ]:1\n");
fprintf(stderr, "\t[mulaw ]:2\n");
fprintf(stderr, "\t[g726 ]:3\n");
fprintf(stderr, "\t[g726le]:4\n");
}
int main(int argc, char **argv)
{
const char *outfilename = NULL;
const char *infilename = NULL;
const AVCodec *codec;
AVCodecContext *c= NULL;
AVFrame *frame;
AVPacket *pkt;
int readsize, perframesize;
int ret, opt;
FILE *fin, *fout;
char *inbuff;
int bitrate = 64000;
int channel = 2;
int samplerate = 44100;
int sampleformat = AV_SAMPLE_FMT_S16; //s16le
int encodertype = 0;
opterr = 1;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch (opt) {
case 'i':
infilename = optarg;
fprintf(stderr, "input file path: %s\n", infilename);
break;
case 'o':
outfilename = optarg;
fprintf(stderr, "output file path: %s\n", outfilename);
break;
case 'c':
channel = atoi(optarg);
fprintf(stderr, "input channel: %d\n", channel);
break;
case 'r':
samplerate = atoi(optarg);
fprintf(stderr, "input samplerate: %d\n", samplerate);
break;
case 'f':
sampleformat = atoi(optarg);
fprintf(stderr, "input sampleformat: %d\n", sampleformat);
break;
case 'b':
bitrate = atoi(optarg);
fprintf(stderr, "input bitrate: %d\n", bitrate);
break;
case 't':
encodertype = atoi(optarg);
fprintf(stderr, "input encodertype: %d\n", encodertype);
break;
case '?':
default:
print_usage(argv[0]);
exit(0);
}
}
/* Arguments check */
if (!infilename || !outfilename || !encodertype) {
print_usage(argv[0]);
exit(1);
}
if ((sampleformat < 1) || (sampleformat > 4)) {
fprintf(stderr, "ERROR: invalid encoder type:%d\n", encodertype);
print_usage(argv[0]);
exit(1);
}
if (sampleformat != AV_SAMPLE_FMT_S16) {
fprintf(stderr, "ERROR: Sample format must be AV_SAMPLE_FMT_S16 for this test!\n");
exit(1);
}
if (((encodertype == 3) || (encodertype == 4)) &&
((samplerate > 8000) || (channel != 1) || (bitrate != 16000))) {
fprintf(stderr, "ERROR: [G726 or G726LE] input file should be:"
"channel:1, samplerate:8000, bitrate:16000\n");
exit(1);
}
/* Create encoder */
if (encodertype == 1) {
fprintf(stderr, "# creating alaw encoder...\n");
codec = avcodec_find_encoder(AV_CODEC_ID_PCM_ALAW);
} else if (encodertype == 2) {
fprintf(stderr, "# creating mulaw encoder...\n");
codec = avcodec_find_encoder(AV_CODEC_ID_PCM_MULAW);
} else if (encodertype == 3) {
fprintf(stderr, "# creating g726 encoder...\n");
codec = avcodec_find_encoder(AV_CODEC_ID_ADPCM_G726);
} else if (encodertype == 4) {
fprintf(stderr, "# creating g726le encoder...\n");
codec = avcodec_find_encoder(AV_CODEC_ID_ADPCM_G726LE);
}
if (!codec) {
fprintf(stderr, "ERROR: Codec not found!\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "ERROR: Could not allocate audio codec context\n");
exit(1);
}
/* put sample parameters */
c->bit_rate = bitrate;
/* check that the encoder supports s16 pcm input */
c->sample_fmt = AV_SAMPLE_FMT_S16;
if (!check_sample_fmt(codec, c->sample_fmt)) {
fprintf(stderr, "ERROR: Encoder does not support sample format %s",
av_get_sample_fmt_name(c->sample_fmt));
exit(1);
}
c->sample_rate = samplerate;
c->channels = channel;
if (channel == 1)
c->channel_layout = AV_CH_LAYOUT_MONO;
else if (channel == 2)
c->channel_layout = AV_CH_LAYOUT_STEREO;
#if 0
if ((encodertype == 3) || (encodertype == 4))
c->bits_per_coded_sample = 2;
#endif
fprintf(stderr, "### INFO: sample_rate:%d\n", c->sample_rate);
fprintf(stderr, "### INFO: channels:%d\n", c->channels);
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
fprintf(stderr, "### INFO: frame_size:%d\n", c->frame_size);
fprintf(stderr, "### INFO: bits_per_coded_sample:%d\n", c->bits_per_coded_sample);
fin = fopen(infilename, "rb");
if (!fin) {
fprintf(stderr, "Could not open input: %s\n", infilename);
exit(1);
}
fout = fopen(outfilename, "wb");
if (!fout) {
fprintf(stderr, "Could not open output: %s\n", outfilename);
exit(1);
}
/* packet for holding encoded output */
pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "could not allocate the packet\n");
exit(1);
}
/* frame containing input raw audio */
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
if (c->frame_size == 0) {
fprintf(stderr, "# Codec has no default frame_size, set 1024 default\n");
c->frame_size = 1024;
}
perframesize = c->channels * av_get_bytes_per_sample(c->sample_fmt);
fprintf(stderr, "### INFO: perframesize:%d\n", perframesize);
inbuff = (char *)malloc(c->frame_size * perframesize);
if (!inbuff) {
fprintf(stderr, "ERROR: no space left!\n");
exit(1);
}
printf("+++ FLC-DBG: readsize:%d\n", c->frame_size * perframesize);
while (1) {
readsize = fread(inbuff, 1, c->frame_size * perframesize, fin);
if (readsize <= 0) {
fprintf(stderr, "# Get the end of file! error:%s\n", strerror(errno));
break;
}
fprintf(stderr, "# encoder %d size...\n", readsize);
frame->nb_samples = readsize / perframesize;
ret = avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, (unsigned char *)inbuff, readsize, 1);
if (ret < 0) {
fprintf(stderr, "ERROR: fill audio frame failed!\n");
break;
}
encode(c, frame, pkt, fout);
}
/* flush the encoder */
encode(c, NULL, pkt, fout);
free(inbuff);
fclose(fin);
fclose(fout);
av_frame_free(&frame);
av_packet_free(&pkt);
avcodec_free_context(&c);
return 0;
}
编码演示
使用说明:
-
g711a
编码:
encode_audio -i test8000.pcm -r 8000 -c 1 -o test.alaw -t 1
播放:
ffplay -ar 8000 -channels 1 -f alaw -i test.alaw -nod isp
-
g711u
编码:encode_audio -i test8000.pcm -r 8000 -c 1 -o test.ulaw -t 2
播放:
ffplay -ar 8000 -channels 1 -f mulaw -i test.ulaw -nod isp
-
g726
encode_audio -i test8000.pcm -r 8000 -c 1 -o test.g726 -t 3
播放:
由于ffplay播放码率为32kbps,编码器要求的编码码率只能为16kbps,因此使用ffplay播放失败。可使用下面章节的解码程序对test.g726进行解码然后再进行pcm的播放。注意:
g726要求必须是采样率:8k,单通道,16kbps的音频数据。本文使用S16LE格式进行测试,其他格式是否支持尚未深究。
解码代码
decode_audio.c: 仅支持G726解码。
/**
* func: Only decode G726 file.
* author: Francis Fan
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,
FILE *outfile)
{
int i, ch;
int ret, data_size;
/* send the packet with the compressed data to the decoder */
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error submitting the packet to the decoder\n");
exit(1);
}
/* read all the output frames (in general there may be any number of them */
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
if (data_size < 0) {
/* This should not occur, checking just for paranoia */
fprintf(stderr, "Failed to calculate data size\n");
exit(1);
}
for (i = 0; i < frame->nb_samples; i++)
for (ch = 0; ch < dec_ctx->channels; ch++)
fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
}
}
int main(int argc, char **argv)
{
const char *outfilename, *filename;
const AVCodec *codec;
AVCodecContext *c= NULL;
AVCodecParserContext *parser = NULL;
int len, ret;
FILE *f, *outfile;
uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t data_size;
AVPacket *pkt;
AVFrame *decoded_frame = NULL;
if (argc <= 2) {
fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
exit(0);
}
filename = argv[1];
outfilename = argv[2];
pkt = av_packet_alloc();
/* find the MPEG audio decoder */
codec = avcodec_find_decoder(AV_CODEC_ID_ADPCM_G726);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate audio codec context\n");
exit(1);
}
c->bits_per_coded_sample = 2; //g726压缩比为8:1 编码前采样用bit数为那么编码后应该占/8 = 2
c->channels = 1;
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->sample_rate = 8000;
c->codec_type = AVMEDIA_TYPE_AUDIO;
c->bit_rate = 16000;
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
outfile = fopen(outfilename, "wb");
if (!outfile) {
av_free(c);
exit(1);
}
while (1) {
if (!decoded_frame) {
if (!(decoded_frame = av_frame_alloc())) {
fprintf(stderr, "Could not allocate audio frame\n");
exit(1);
}
}
data = inbuf;
data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
if (data_size <= 0) {
printf("INFO: Get end of the file\n");
break;
}
pkt->data = (uint8_t *)inbuf;
pkt->size = data_size;
if (pkt->size)
decode(c, pkt, decoded_frame, outfile);
}
/* flush the decoder */
pkt->data = NULL;
pkt->size = 0;
decode(c, pkt, decoded_frame, outfile);
fclose(outfile);
fclose(f);
avcodec_free_context(&c);
av_frame_free(&decoded_frame);
av_packet_free(&pkt);
return 0;
}
解码演示
decode_audio test.g726 g726.pcm
注:有些设备不支持mono pcm,可使用ffplay播放或者pull出源文件,使用Audacity播放。