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;
}
相关推荐
Everbrilliant892 天前
音视频编解码全流程之用Extractor后Muxer生成MP4
视频编解码·amediamuxer·ffmpeg数据包提取·amediaextractor·ffmpeg数据包的写入·提取器extractor·复用器muxer
骄傲的心别枯萎6 天前
RV1126 NO.30:RV1126多线程获取音频AI的PCM数据
linux·ffmpeg·音视频·pcm·视频编解码
Byron Loong6 天前
MP4和WMV2压缩机制对比
视频编解码
风间琉璃•7 天前
FFmpeg视频编解码
ffmpeg·视频编解码
沃达德软件7 天前
AI数字人视频图像音频生成服务
图像处理·人工智能·计算机视觉·ai作画·音视频·实时音视频·视频编解码
Everbrilliant897 天前
Android音视频编解码全流程之Muxer
视频编解码·ffmpeg帧写入·ndkmediamuxer·muxer·muxer复用器·amediamuxer·音视频编解码全流程
xiaohouzi11223319 天前
Python读取视频-硬解和软解
python·opencv·ffmpeg·视频编解码·gstreamer
TSINGSEE1 个月前
数据库选择有讲究?SQLite、PostgreSQL还是MySQL?
mysql·音视频·实时音视频·数据库架构·视频编解码
Font Tian1 个月前
FFmpeg 不同编码的压缩命令详解
ffmpeg·音视频·视频编解码·视频·视频压缩
Natsume17101 个月前
音视频开发入门:FFmpeg vs GStreamer,新手该如何选择?
c语言·c++·ffmpeg·音视频·webrtc·实时音视频·视频编解码