ffmpeg添加MP4的pcm音频支持

原文地址:https://blog.csdn.net/garefield/article/details/45113363

ffmpeg中对MP4的打包处理是在movenc.c中,在实际打包过程中发现除了mov类型外,其它类型如vob等均无法生成音频的声道信息,经过分析发现在mov_write_audio_tag函数的最后有如下代码

 if (track->mode == MODE_MOV && track->enc->codec_type == AVMEDIA_TYPE_AUDIO)

        mov_write_chan_tag(pb, track);

即只有MP4为mov类型的时候才会写入音频信息,因此这个判断需要修改为

 if ( track->enc->codec_type == AVMEDIA_TYPE_AUDIO)

        mov_write_chan_tag(pb, track);

即不论哪种MP4,均将声道信息写入。

为MP4编码添加pcm音频支持:

static const AVCodecTag codec_ipod_tags[] = {

    { AV_CODEC_ID_H264,     MKTAG('a','v','c','1') },

    { AV_CODEC_ID_MPEG4,    MKTAG('m','p','4','v') },

    { AV_CODEC_ID_AAC,      MKTAG('m','p','4','a') },

    { AV_CODEC_ID_PCM_MULAW,MKTAG('u','l','a','w') },

    { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') },

    { AV_CODEC_ID_ALAC,     MKTAG('a','l','a','c') },

    { AV_CODEC_ID_AC3,      MKTAG('a','c','-','3') },

    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },

    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','e','x','t') },

    { AV_CODEC_ID_NONE, 0 },

};

 

static const AVCodecTag codec_3gp_tags[] = {

    { AV_CODEC_ID_H263,     MKTAG('s','2','6','3') },

    { AV_CODEC_ID_H264,     MKTAG('a','v','c','1') },

    { AV_CODEC_ID_MPEG4,    MKTAG('m','p','4','v') },

    { AV_CODEC_ID_AAC,      MKTAG('m','p','4','a') },

    { AV_CODEC_ID_PCM_MULAW,MKTAG('u','l','a','w') },

    { AV_CODEC_ID_PCM_ALAW, MKTAG('a','l','a','w') },

    { AV_CODEC_ID_AMR_NB,   MKTAG('s','a','m','r') },

    { AV_CODEC_ID_AMR_WB,   MKTAG('s','a','w','b') },

    { AV_CODEC_ID_MOV_TEXT, MKTAG('t','x','3','g') },

    { AV_CODEC_ID_NONE, 0 },

};

 

static const AVCodecTag codec_f4v_tags[] = { // XXX: add GIF/PNG/JPEG?

    { AV_CODEC_ID_MP3,    MKTAG('.','m','p','3') },

    { AV_CODEC_ID_AAC,    MKTAG('m','p','4','a') },

    { AV_CODEC_ID_PCM_MULAW,MKTAG('u','l','a','w') },

    { AV_CODEC_ID_PCM_ALAW,MKTAG('a','l','a','w') },

    { AV_CODEC_ID_H264,   MKTAG('a','v','c','1') },

    { AV_CODEC_ID_VP6A,   MKTAG('V','P','6','A') },

    { AV_CODEC_ID_VP6F,   MKTAG('V','P','6','F') },

    { AV_CODEC_ID_NONE, 0 },

};

以上红色部分是添加的针对pcm的格式信息,然后在mov_write_header函数中,还需要设置声道信息,将

 else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {

            track->timescale = st->codec->sample_rate;

改为

else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {

            if (st->codec->codec_id == AV_CODEC_ID_PCM_MULAW || st->codec->codec_id == AV_CODEC_ID_PCM_ALAW)

            {

                st->codec->channels = 1;

                st->codec->sample_rate = 8000;

                st->codec->channel_layout = AV_CH_LAYOUT_MONO;

                st->codec->sample_fmt = AV_SAMPLE_FMT_S16;

                track->enc->channels = 1;

                track->enc->channel_layout = AV_CH_LAYOUT_MONO;

            }

            track->timescale = st->codec->sample_rate;

红色部分是添加内容

 

此外,解码部分也要做修改,在mov_chan.c中,添加

static const enum MovChannelLayoutTag mov_ch_layouts_law[] = {

    MOV_CH_LAYOUT_MONO,

    MOV_CH_LAYOUT_STEREO,

    0,

};

以及修改mov_codec_ch_layouts的定义为

static const struct {

    enum AVCodecID codec_id;

    const enum MovChannelLayoutTag *layouts;

} mov_codec_ch_layouts[] = {

    { AV_CODEC_ID_AAC,     mov_ch_layouts_aac      },

    { AV_CODEC_ID_PCM_MULAW, mov_ch_layouts_law    },

    { AV_CODEC_ID_PCM_ALAW,  mov_ch_layouts_law    },

    { AV_CODEC_ID_AC3,     mov_ch_layouts_ac3      },

    { AV_CODEC_ID_ALAC,    mov_ch_layouts_alac     },

    { AV_CODEC_ID_PCM_U8,    mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S8,    mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S16LE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S16BE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S24LE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S24BE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S32LE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_S32BE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_F32LE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_F32BE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_F64LE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_PCM_F64BE, mov_ch_layouts_wav    },

    { AV_CODEC_ID_NONE,    NULL                    },

};

这两部分内容是定义pam的声道模式,然后还要修改isom.c

在ff_mp4_obj_type中添加

    { AV_CODEC_ID_PCM_MULAW   , 0xE3 }, /* a private definition */

    { AV_CODEC_ID_PCM_ALAW  , 0xE4 }, /* a private definition */

这两个定义是pcm的流类型标志定义,参考自另一个MP4打包库mpeg4ip的MP4.h --------------------- 作者:garefield 来源:CSDN 原文:https://blog.csdn.net/garefield/article/details/45113363?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用FFmpeg的C API来实现RTP拉取PCM音频流的功能。以下是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <libavutil/avutil.h> #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #define RTP_HEADER_SIZE 12 typedef struct { AVFormatContext *fmt_ctx; AVCodecContext *codec_ctx; AVPacket *pkt; int audio_stream_index; int sockfd; struct sockaddr_in addr; } RTPContext; static void *recv_rtp_packets(void *arg) { RTPContext *ctx = (RTPContext *)arg; uint8_t buffer[4096]; int n; while (1) { n = recvfrom(ctx->sockfd, buffer, sizeof(buffer), 0, NULL, NULL); if (n <= 0) { break; } av_packet_from_data(ctx->pkt, buffer + RTP_HEADER_SIZE, n - RTP_HEADER_SIZE); avcodec_send_packet(ctx->codec_ctx, ctx->pkt); while (avcodec_receive_frame(ctx->codec_ctx, ctx->fmt_ctx->streams[ctx->audio_stream_index]->codecpar) == 0) { // 处理音频帧 } } return NULL; } int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "Usage: %s <ip_address> <port>\n", argv[0]); exit(1); } av_register_all(); avformat_network_init(); RTPContext ctx = {0}; int ret; // 打开RTP流 avformat_open_input(&ctx.fmt_ctx, "rtp://", NULL, NULL); av_dict_set(&ctx.fmt_ctx->metadata, "rtpflags", "listen", 0); av_dict_set(&ctx.fmt_ctx->metadata, "protocol_whitelist", "udp,rtp", 0); av_dict_set(&ctx.fmt_ctx->metadata, "listen_timeout", "0", 0); // 添加音频流 AVStream *audio_stream = avformat_new_stream(ctx.fmt_ctx, NULL); audio_stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; audio_stream->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; audio_stream->codecpar->channels = 1; audio_stream->codecpar->sample_rate = 8000; avformat_write_header(ctx.fmt_ctx, NULL); // 打开解码器 ctx.codec_ctx = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(ctx.codec_ctx, audio_stream->codecpar); avcodec_open2(ctx.codec_ctx, avcodec_find_decoder(AV_CODEC_ID_PCM_S16LE), NULL); // 创建RTP套接字 ctx.sockfd = socket(AF_INET, SOCK_DGRAM, 0); ctx.addr.sin_family = AF_INET; ctx.addr.sin_addr.s_addr = inet_addr(argv[1]); ctx.addr.sin_port = htons(atoi(argv[2])); bind(ctx.sockfd, (struct sockaddr *)&ctx.addr, sizeof(ctx.addr)); // 创建AVPacket ctx.pkt = av_packet_alloc(); // 启动接收线程 pthread_t recv_thread; pthread_create(&recv_thread, NULL, recv_rtp_packets, &ctx); // 等待接收线程结束 pthread_join(recv_thread, NULL); // 清理资源 av_packet_free(&ctx.pkt); avcodec_free_context(&ctx.codec_ctx); avformat_close_input(&ctx.fmt_ctx); avformat_network_deinit(); return 0; } ``` 代码中使用了FFmpeg的C API来实现RTP流的接收和音频解码,需要注意的是,音频帧的处理需要在解码器解码后进行。在代码中使用了一个线程来接收RTP数据包,当收到数据包后,将其存入一个AVPacket中,然后送给解码器进行解码。解码器每次解码出一帧音频数据后,将其存储在AVCodecParameters结构体中,可以通过该结构体的成员变量获取音频数据的信息。可以根据需要修改音频流的参数和输出方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值