G726編解碼處理(ffmpeg,rtsp)

目錄

G.726簡介

FFMpeg編解碼G.726

rtsp傳輸G.726

VLC播放G.726音頻有問題


G.726簡介

維基百科:https://en.wikipedia.org/wiki/G.726

以下內容,摘抄於維基百科

G.726ITU-T ADPCM 語音編解碼器標準,涵蓋以16、24、32和40 kbit / s的速率傳輸語音  。它被引入以取代以32 kbit / s的速率覆蓋ADPCM的G.721和以24和40 kbit / s的速率描述ADPCM的G.723。G.726還引入了新的16 kbit / s速率。與G.726相關的四個比特率通常由樣本的比特大小來指代,分別爲2、3、4和5位。基於相同技術的相應寬帶編解碼器爲G.722

最常用的模式是32 kbit / s,通過使用G.711速率的一半將可用網絡容量增加一倍。它主要用於電話網絡中的國際中繼線上,並且是DECT無線電話系統中使用的標準編解碼器。24和16 kbit / s通道的主要應用是用於在數字電路乘法設備(DCME)中承載語音的過載通道。40 kbit / s通道的主要應用是在DCME中傳送數據調制解調器信號,尤其是對於工作於4800 bit / s以上的調制解調器

  • 採樣頻率8 kHz
  • 提供16 kbit / s,24 kbit / s,32 kbit / s,40 kbit / s比特率
  • 生成一個比特流,因此幀長度由下式確定分組時間(通常爲80個採樣爲10  毫秒的幀大小)
  • 典型算法延遲爲0.125 ms,無提前延遲
  • G.726是使用自適應差分脈衝編碼調製(ADPCM)的波形語音編碼器

 

FFMpeg編解碼G.726

參考:https://blog.csdn.net/byxdaz/article/details/78430453

由維基百科中我們得知,G.726包括了little endian和big endian(RFC3551),即小端和大端

對應ffmpeg中的AV_CODEC_ID_ADPCM_G726LEAV_CODEC_ID_ADPCM_G726

g726文件解碼成PCM

/*
 * Copyright (c) 2001 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file
 * audio decoding with libavcodec API example
 *
 * @example decode_audio.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/frame.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.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)
{
	if(argc != 3)
	{
		printf("usage:./demo input_file output_file\n");
		return 0;
	}
    AVCodec *codec;
	AVCodecContext *c= NULL;
	AVPacket avpkt;
	AVFrame *decoded_frame = NULL;
	avcodec_register_all();
	av_init_packet(&avpkt);
	/* find the mpeg audio decoder */
	codec = avcodec_find_decoder(AV_CODEC_ID_ADPCM_G726LE);
	if (!codec) 
	{
		fprintf(stderr, "codec not found\n");
		return;
	}
	c = avcodec_alloc_context3(codec);
	//採樣率= 8000 每個採樣用的bit數= 16 通道數= 1
	c->bits_per_coded_sample = 2;   //g726 比特率/採樣率 = 16Kbit/8KHz = 2
	c->channels = 1;
	c->sample_fmt = AV_SAMPLE_FMT_S16;
	c->sample_rate = 8000;
	c->codec_type = AVMEDIA_TYPE_AUDIO;
	int iRet = avcodec_open2(c, codec,NULL);
	if ( iRet < 0 ) 
	{
		fprintf(stderr, "could not open codec\n");
		return;
	}

	char szData[100] = {0};
	char szOutData[320] = {0};
	int  nDataLen = c->bits_per_coded_sample * 20;
	int  nOutDataLen = 320;
	int  nReadedSize = 0;
	FILE *fp_in = fopen(argv[1],"r");
	FILE *fp_out = fopen(argv[2],"w");
	
	while(1)
	{
		nDataLen = 40;
		nReadedSize = fread(szData,sizeof(char),nDataLen,fp_in);
		if(nReadedSize < nDataLen)
		{
			break;
		}
		avpkt.data = (uint8_t *)szData;
		avpkt.size = nDataLen;
		int got_frame = 0;
		if (!decoded_frame) 
		{
			if (!(decoded_frame = av_frame_alloc())) 
			{
				return;
			}
		} 
		else
		{
			av_frame_unref(decoded_frame);
		}
		int len = avcodec_decode_audio4(c, decoded_frame, &got_frame, &avpkt);
		if (len < 0) 
		{
			return;
		}
		if (got_frame) 
		{
			/* if a frame has been decoded, output it */
			int data_size = av_samples_get_buffer_size(NULL, c->channels,
				decoded_frame->nb_samples,
				c->sample_fmt, 1);
			fwrite(decoded_frame->data[0], 1, data_size, fp_out);
		}
	}
	fclose(fp_in);
	fclose(fp_out);
	
	return 0;
}

 

解碼G726 RTP流或者包含G726的avi文件

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#include <signal.h>
 
#define MAX_AUDIO_FRAME_SIZE  192000
 
const char *out_file = "./test.pcm";

static int g_run = 1;
void int_handle(int sig)
{
	printf("quit!\n");
	g_run = 0;
}


int main()
{
	int i = 0;
	int ret = 0;
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext  *dec_ctx = NULL;
    AVCodec         *dec   = NULL;
	const char url[64] = "rtsp://admin:[email protected]:554/main";
//	const char url[256] = "rtsp://admin:[email protected]:554/ISAPI/streaming/channels/101";
//	const char url[64] = "./g726.avi";
	

	signal(SIGINT,int_handle);
 
    //分配一個avformat
    fmt_ctx = avformat_alloc_context();
    if (fmt_ctx == NULL)
    {
        printf("alloc fail");
		exit(1);
    }
 
    //打開文件,解封裝
    if (avformat_open_input(&fmt_ctx, url, NULL, NULL) != 0)
    {
        printf("open fail");
		exit(1);
    }
 
    //查找文件的相關流信息
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
    {
        printf("find stream fail");
		exit(1);
    }
 
    //輸出格式信息
    av_dump_format(fmt_ctx, 0, url, 0);
 
    //查找解碼信息
    int stream_index = -1;
    for (i = 0; i < fmt_ctx->nb_streams; i++)
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            stream_index = i;
			printf("audio index------------------%d,codec_id:%d\n",i,fmt_ctx->streams[stream_index]->codecpar->codec_id);
            break;
        }
 
    if (stream_index == -1)
        printf("find stream fail");
	printf("AV_CODEC_ID_ADPCM_G726:%d\n",AV_CODEC_ID_ADPCM_G726);
	printf("AV_CODEC_ID_ADPCM_G726LE:%d\n",AV_CODEC_ID_ADPCM_G726LE);
	printf("AV_CODEC_ID_ADPCM_G722:%d\n",AV_CODEC_ID_ADPCM_G722);
 
    //保存解碼器
    dec = avcodec_find_decoder(fmt_ctx->streams[stream_index]->codecpar->codec_id);
    if (dec == NULL)
        printf("find codec fail");

	/* create decoding context */
	dec_ctx = avcodec_alloc_context3(dec);
	if (!dec_ctx)
		printf("avcodec_alloc_context3 failed\n");
	avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[stream_index]->codecpar);
	//採樣率= 8000 每個採樣用的bit數= 16 通道數= 1
	dec_ctx->bits_per_coded_sample = 2;   //g726壓縮比爲8:1 編碼前採樣用bit數爲那麼編碼後應該佔/8 = 2
	dec_ctx->channels = 1;
	dec_ctx->sample_fmt = AV_SAMPLE_FMT_S16;
	dec_ctx->sample_rate = 8000;
	dec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
 
    if (avcodec_open2(dec_ctx, dec, NULL) < 0)
        printf("can't open codec");
 
    FILE *out_fb = NULL;
    out_fb = fopen(out_file, "wb");
 
    //創建packet,用於存儲解碼前的數據
    AVPacket *packet = av_packet_alloc();;
    av_init_packet(packet);
 
    //設置轉碼後輸出相關參數


	int buffer_size = 0;
 
 
    //注意要用av_malloc
    uint8_t *buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
 
 
    //創建Frame,用於存儲解碼後的數據
    AVFrame *frame = av_frame_alloc();
	AVFrame *decoded_frame = NULL;

 
    //while循環,每次讀取一幀
 
    while (av_read_frame(fmt_ctx, packet) >= 0 && g_run) {
 
        if (packet->stream_index == stream_index) {

			int got_frame = 0;
			if (!decoded_frame) 
			{
				if (!(decoded_frame = av_frame_alloc())) 
				{
					return;
				}
			} 
			else
			{
				av_frame_unref(decoded_frame);
			}
			int len = avcodec_decode_audio4(dec_ctx, decoded_frame, &got_frame, packet);
			if (len < 0) 
			{
				return;
			}
			if (got_frame) 
			{
				/* if a frame has been decoded, output it */
				int data_size = av_samples_get_buffer_size(NULL, dec_ctx->channels,
					decoded_frame->nb_samples,
					dec_ctx->sample_fmt, 1);
				printf("nb_samples:%d----------------data_size:%d\n",decoded_frame->nb_samples,data_size);
				fwrite(decoded_frame->data[0], 1, data_size, out_fb);
			}

        }
 		//release resource
    	av_packet_unref(packet);
    }
 
 	av_frame_free(&frame);
	av_packet_free(&packet);
    fclose(out_fb);
 
    return 0;
}

解碼後的PCM可以使用Audacity進行播放

 

rtsp傳輸G.726

由該圖可知道,當使用比特率爲16kbit時,rtpmap爲G726-16/8000,以下爲抓包結果

 

VLC播放G.726音頻有問題

ffmpeg已經支持G.726大端和小端,但是VLC只支持rtpmap爲G726,但是使用標準的G726小端,在VLC中播放聲音有問題。將G726編碼改爲大端後,rtpmap仍然使用小端,這時候播放正常。這應該是VLC沒有兼容好的問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章