5、MP4解复用---AAC+H264

  • MP4
    MP4同样是一种容器格式,是由一个一个Box组成,每个Box又分为Header与Data,Data又包含很多子Box,具体的MP4文件结构也看过,内部Box结构比较复杂,一般不写MP4解释器的话,Box结构不用了解太细,对MP4封装有MP4V2库可以使用,当然也可以使用FFMPEG库。
    本文从代码的角度上来分析使用FFMPEG对MP4文件解复用。
    完整代码如下:
bash 复制代码
#include <stdio.h>

#include "libavutil/log.h"
#include "libavformat/avformat.h"

#define ERROR_STRING_SIZE 1024

#define ADTS_HEADER_LEN  7;

const int sampling_frequencies[] = {
    96000,
    88200,
    64000,
    48000,
    44100,
    32000,
    24000,
    22050,
    16000,
    12000,
    11025,
    8000
};

int adts_header(char * const p_adts_header, const int data_length,
                const int profile, const int samplerate,
                const int channels)
{

    int sampling_frequency_index = 3;
    int adtsLen = data_length + 7;

    int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);

    for(int i = 0; i < frequencies_size; i++)
    {
        if(sampling_frequencies[i] == samplerate)
        {
            sampling_frequency_index = i;
            break;
        }
    }

    p_adts_header[0] = 0xff;
    p_adts_header[1] = 0xf0;
    p_adts_header[1] |= (0 << 3);
    p_adts_header[1] |= (0 << 1);
    p_adts_header[1] |= 1;

    p_adts_header[2] = (profile)<<6;
    p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2;
    p_adts_header[2] |= (0 << 1);
    p_adts_header[2] |= (channels & 0x04)>>2;

    p_adts_header[3] = (channels & 0x03)<<6;
    p_adts_header[3] |= (0 << 5);
    p_adts_header[3] |= (0 << 4);
    p_adts_header[3] |= (0 << 3);
    p_adts_header[3] |= (0 << 2);
    p_adts_header[3] |= ((adtsLen & 0x1800) >> 11);

    p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);
    p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);
    p_adts_header[5] |= 0x1f;
    p_adts_header[6] = 0xfc;
    return 0;
}



int main(int argc, char **argv)
{

    char *in_filename = "/home/yx/media_file/believe.mp4";
    char *h264_filename = "/home/yx/media_file/believe.h264";
    char *aac_filename = "/home/yx/media_file/believe.aac";

    FILE *aac_fd = NULL;
    FILE *h264_fd = NULL;

    h264_fd = fopen(h264_filename, "wb");
    aac_fd = fopen(aac_filename, "wb");

    AVFormatContext *ifmt_ctx = NULL;
    int video_index = -1;
    int audio_index = -1;
    AVPacket *pkt = NULL;

    ifmt_ctx = avformat_alloc_context();


    avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);                                // 将媒体流与解复用器相关联

    video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);       // 查找视频流标签
    audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);       // 查找音频流标签

    const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");             // 查找H264过滤器

    AVBSFContext *bsf_ctx = NULL;
    av_bsf_alloc(bsfilter, &bsf_ctx);
    avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[video_index]->codecpar);
    av_bsf_init(bsf_ctx);                                                                   // 初始化过滤器

    pkt = av_packet_alloc();
    av_init_packet(pkt);
    while (1)
    {
        if(av_read_frame(ifmt_ctx, pkt) < 0)                                                // 逐个包读取
            break;
        if(pkt->stream_index == video_index)
        {
            av_bsf_send_packet(bsf_ctx, pkt);                                               // 视频包就将包发给过滤器

            while (1)
            {
                if(av_bsf_receive_packet(bsf_ctx, pkt) != 0)                                // 从过滤器接收包
                    break;
                size_t size = fwrite(pkt->data, 1, pkt->size, h264_fd);                     // 将包写到输出文件
                av_packet_unref(pkt);
            }
        }
        else if(pkt->stream_index == audio_index)
        {
            char adts_header_buf[7] = {0};
            adts_header(adts_header_buf, pkt->size,
                        ifmt_ctx->streams[audio_index]->codecpar->profile,
                        ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
                        ifmt_ctx->streams[audio_index]->codecpar->channels);                // 音频包构建ADTS帧头部
            fwrite(adts_header_buf, 1, 7, aac_fd);                                          // 将头部写到输出文件
            size_t size = fwrite( pkt->data, 1, pkt->size, aac_fd);                         // 将AAC包写到输出文件

            av_packet_unref(pkt);                                                           // 引用计数--
        }
        else
        {
            av_packet_unref(pkt);
        }
    }

    printf("while finish\n");
    printf("Hello World!\n");
    return 0;
}
相关推荐
野蛮的大西瓜2 天前
BigBlueButton视频会议 vs 华为云会议的详细对比
人工智能·自动化·音视频·实时音视频·信息与通信·视频编解码
野蛮的大西瓜2 天前
BigBlueButton视频会议 vs 钉钉视频会议系统的详细对比
人工智能·自然语言处理·自动化·音视频·实时音视频·信息与通信·视频编解码
无法雾题啊4 天前
拍摄镜头模式要怎么选择
视频编解码
野蛮的大西瓜4 天前
大模型和呼叫中心的结合如何提高自动化水平?
运维·自然语言处理·机器人·开源·自动化·音视频·视频编解码
野蛮的大西瓜5 天前
BigBlueButton视频会议 vs 华为视频会议系统的详细对比
人工智能·机器人·自动化·音视频·实时音视频·信息与通信·视频编解码
盛世隐者9 天前
ffprobe文档
音视频·视频编解码
无法雾题啊9 天前
一行一行出字的视频怎么做?简单的操作方法
音视频·视频编解码
人才程序员9 天前
ffmpeg C语音 读取视频帧源码
c语言·c++·ffmpeg·音视频·webrtc·实时音视频·视频编解码
PsEmperor11 天前
mp4影像和m4a音频无损合成视频方法
音视频·视频编解码
大耳猫17 天前
Android 屏幕采集并编码为H.264
android·kotlin·视频编解码·h.264