使用FFmpeg将pcm数据编码为aac

代码对一些数据没做判断,仅仅是做个备忘!请谨慎参考!

#include <libavcodec/avcodec.h>
#include <stdio.h>

void f32le_convert_to_fltp(float *f32le, float *fltp, int nb_samples) {
  float *fltp_l = fltp;               // 左通道
  float *fltp_r = fltp + nb_samples;  // 右通道
  for (int i = 0; i < nb_samples; i++) {
    fltp_l[i] = f32le[i * 2];
    fltp_r[i] = f32le[i * 2 + 1];
  }
}

static void get_adts_header(AVCodecContext *ctx, uint8_t *adts_header,
                            int aac_length) {
  uint8_t freq_idx = 0;
  switch (ctx->sample_rate) {
    case 96000:
      freq_idx = 0;
      break;
    case 88200:
      freq_idx = 1;
      break;
    case 64000:
      freq_idx = 2;
      break;
    case 48000:
      freq_idx = 3;
      break;
    case 44100:
      freq_idx = 4;
      break;
    case 32000:
      freq_idx = 5;
      break;
    case 24000:
      freq_idx = 6;
      break;
    case 22050:
      freq_idx = 7;
      break;
    case 16000:
      freq_idx = 8;
      break;
    case 12000:
      freq_idx = 9;
      break;
    case 11025:
      freq_idx = 10;
      break;
    case 8000:
      freq_idx = 11;
      break;
    case 7350:
      freq_idx = 12;
      break;
    default:
      freq_idx = 4;
      break;
  }
  uint8_t chanCfg = ctx->channels;
  uint32_t frame_length = aac_length + 7;
  adts_header[0] = 0xFF;
  adts_header[1] = 0xF1;
  adts_header[2] = ((ctx->profile) << 6) + (freq_idx << 2) + (chanCfg >> 2);
  adts_header[3] = (((chanCfg & 3) << 6) + (frame_length >> 11));
  adts_header[4] = ((frame_length & 0x7FF) >> 3);
  adts_header[5] = (((frame_length & 7) << 5) + 0x1F);
  adts_header[6] = 0xFC;
}

void encode_audio(AVCodecContext *codec_ctx, AVFrame *frame, AVPacket *pkt,
                  FILE *out_aac) {
  int ret;
  ret = avcodec_send_frame(codec_ctx, frame);
  while (!ret) {
    ret = avcodec_receive_packet(codec_ctx, pkt);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
      printf("EAGAIN OR EOF\n");
      return;
    } else if (ret < 0) {
      printf("error 6\n");
      exit(1);
    }
    if ((codec_ctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) {
      uint8_t aac_header[7];//7个字节的adts header
      get_adts_header(codec_ctx, aac_header, pkt->size);
      ret = fwrite(aac_header, 1, 7, out_aac);
      if (ret != 7) {
        fprintf(stderr, "fwrite aac_header failed\n");
        return -1;
      }
    }
    fwrite(pkt->data, 1, pkt->size, out_aac);
  }
}

/**
 * @brief 音频编码 PCM编码为AAC
 * @param argc
 * @param argv
 * @return
 */
int main(int argc, char **argv) {
  AVCodecContext *codec_ctx;
  AVCodec *codec;
  FILE *in_pcm;
  FILE *out_aac;
  AVPacket *pkt;
  AVFrame *frame;
  int ret;
  if(argc != 3){
      printf("param error\n");
      exit(1);
  }
  in_pcm = fopen(argv[1], "rb");
  out_aac = fopen(argv[2], "wb");
  codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
  if (!codec) {
    printf("error 1\n");
    exit(1);
  }
  codec_ctx = avcodec_alloc_context3(codec);
  codec_ctx->codec_id = AV_CODEC_ID_AAC;
  codec_ctx->sample_rate = 44100;
  codec_ctx->bit_rate = 512 * 1024;
  codec_ctx->channels = 2;
  codec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
  codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
  codec_ctx->profile = FF_PROFILE_AAC_LOW;
  codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
  codec_ctx->time_base = (AVRational){1, codec_ctx->sample_rate};
  codec_ctx->flags = AV_CODEC_FLAG_GLOBAL_HEADER;
  ret = avcodec_open2(codec_ctx, codec, NULL);
  if (ret) {
    printf("error 2\n");
    exit(1);
  }
  frame = av_frame_alloc();
  frame->nb_samples = codec_ctx->frame_size;
  frame->channels = 2;
  frame->channel_layout = AV_CH_LAYOUT_STEREO;
  frame->format = codec_ctx->sample_fmt;
  ret = av_frame_get_buffer(frame, 0);
  int nb_samples = 0;
  if (ret) {
    printf("error 3\n");
    exit(1);
  }
  pkt = av_packet_alloc();
  av_init_packet(pkt);
  int bytes_per_frame = av_get_bytes_per_sample(AV_SAMPLE_FMT_FLTP) *
                        frame->nb_samples * frame->channels;
  uint8_t *in_buf = (uint8_t *)av_mallocz(bytes_per_frame);
  uint8_t *in_buf_temp = (uint8_t *)malloc(bytes_per_frame);
  if (!in_buf_temp) {
    printf("in_buf_temp malloc failed\n");
    return 1;
  }
  while (fread(in_buf, 1, bytes_per_frame, in_pcm) == bytes_per_frame) {
    ret = av_frame_make_writable(frame);

    //fltp如果是按照packeted方式存储的,需要先转换成planar方式
//    memset(in_buf_temp, 0, bytes_per_frame);
//    f32le_convert_to_fltp((float *)in_buf, (float *)in_buf_temp,
//                          frame->nb_samples);

    ret = av_samples_fill_arrays(frame->data, frame->linesize, in_buf/*in_buf_temp*/,
                                 frame->channels, frame->nb_samples,
                                 frame->format, 0);
    if (ret < 0) {
      printf("error 4\n");
      exit(1);
    }
    frame->pts = nb_samples;
    nb_samples += frame->nb_samples;
    encode_audio(codec_ctx, frame, pkt, out_aac);
    memset(in_buf, 0, bytes_per_frame);
  }
  encode_audio(codec_ctx, NULL, pkt, out_aac);
end:
  fclose(in_pcm);
  fclose(out_aac);
  avcodec_free_context(&codec_ctx);
  av_frame_free(&frame);
  av_packet_free(&pkt);
  printf("Hello World!\n");
  return 0;
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
将YUV和PCM数据编码成视频和音频文件需要使用FFmpeg库,具体的实现步骤如下: 1. 初始化FFmpeg库 在使用FFmpeg库之前,需要先进行初始化。使用av_register_all函数可以注册FFmpeg库中的所有编解码器、格式器和协议等。 ``` av_register_all(); ``` 2. 打开输出文件 使用avformat_alloc_output_context2和avio_open2函数打开输出文件,创建AVFormatContext结构体并分配内存,将输出文件与该结构体关联。 ``` AVFormatContext *out_ctx = NULL; int ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, output_file); if (ret < 0) { // 创建AVFormatContext失败 return; } if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) { ret = avio_open2(&out_ctx->pb, output_file, AVIO_FLAG_WRITE, NULL, NULL); if (ret < 0) { // 打开输出文件失败 return; } } ``` 3. 创建音视频流 使用avformat_new_stream函数创建音视频流,并设置音视频流的相关参数,如编码器、帧率、码率、采样率等。 ``` AVStream *video_stream = avformat_new_stream(out_ctx, NULL); if (video_stream == NULL) { // 创建视频流失败 return; } AVCodecParameters *codecpar = video_stream->codecpar; codecpar->codec_type = AVMEDIA_TYPE_VIDEO; codecpar->width = width; codecpar->height = height; codecpar->format = AV_PIX_FMT_YUV420P; codecpar->codec_id = AV_CODEC_ID_H264; codecpar->bit_rate = bit_rate; codecpar->framerate = {fps, 1}; AVStream *audio_stream = avformat_new_stream(out_ctx, NULL); if (audio_stream == NULL) { // 创建音频流失败 return; } codecpar = audio_stream->codecpar; codecpar->codec_type = AVMEDIA_TYPE_AUDIO; codecpar->sample_rate = sample_rate; codecpar->format = AV_SAMPLE_FMT_S16; codecpar->channels = channels; codecpar->channel_layout = av_get_default_channel_layout(channels); codecpar->codec_id = AV_CODEC_ID_AAC; codecpar->bit_rate = bit_rate; ``` 4. 打开视频和音频编码使用avcodec_find_encoder函数查找视频和音频编码器,并使用avcodec_open2打开编码器。 ``` AVCodec *video_codec = avcodec_find_encoder(video_stream->codecpar->codec_id); if (video_codec == NULL) { // 查找视频编码器失败 return; } AVCodecContext *video_cctx = avcodec_alloc_context3(video_codec); if (video_cctx == NULL) { // 创建视频编码器上下文失败 return; } ret = avcodec_open2(video_cctx, video_codec, NULL); if (ret < 0) { // 打开视频编码器失败 return; } AVCodec *audio_codec = avcodec_find_encoder(audio_stream->codecpar->codec_id); if (audio_codec == NULL) { // 查找音频编码器失败 return; } AVCodecContext *audio_cctx = avcodec_alloc_context3(audio_codec); if (audio_cctx == NULL) { // 创建音频编码器上下文失败 return; } ret = avcodec_open2(audio_cctx, audio_codec, NULL); if (ret < 0) { // 打开音频编码器失败 return; } ``` 5. 写入视频和音频数据 使用av_frame_alloc函数创建AVFrame结构体,填充YUV和PCM数据,并使用avcodec_send_frame和avcodec_receive_packet函数将数据编码成视频和音频包,最后使用av_write_frame函数将包写入输出文件。 ``` AVFrame *video_frame = av_frame_alloc(); // 填充YUV数据到video_frame中 AVPacket *video_packet = av_packet_alloc(); ret = avcodec_send_frame(video_cctx, video_frame); if (ret < 0) { // 向视频编码器发送数据失败 return; } while (ret >= 0) { ret = avcodec_receive_packet(video_cctx, video_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { // 从视频编码器接收数据失败 return; } av_packet_rescale_ts(video_packet, video_cctx->time_base, video_stream->time_base); video_packet->stream_index = video_stream->index; ret = av_write_frame(out_ctx, video_packet); if (ret < 0) { // 写入视频数据失败 return; } } AVFrame *audio_frame = av_frame_alloc(); // 填充PCM数据到audio_frame中 AVPacket *audio_packet = av_packet_alloc(); ret = avcodec_send_frame(audio_cctx, audio_frame); if (ret < 0) { // 向音频编码器发送数据失败 return; } while (ret >= 0) { ret = avcodec_receive_packet(audio_cctx, audio_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { // 从音频编码器接收数据失败 return; } av_packet_rescale_ts(audio_packet, audio_cctx->time_base, audio_stream->time_base); audio_packet->stream_index = audio_stream->index; ret = av_write_frame(out_ctx, audio_packet); if (ret < 0) { // 写入音频数据失败 return; } } ``` 6. 关闭编码器和输出文件 使用av_write_trailer、avcodec_free_context和avformat_free_context函数释放资源并关闭编码器和输出文件。 ``` av_write_trailer(out_ctx); avcodec_free_context(&video_cctx); avcodec_free_context(&audio_cctx); avformat_free_context(out_ctx); ``` 以上是将YUV和PCM数据编码成视频和音频文件的基本流程,需要注意的是各项参数的设置和数据的填充。如果需要进行更详细的配置和处理,可以参考FFmpeg库的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值