08 - FFmpeg - 解封装 - 提取 h264 + AAC

cpp 复制代码
#define ADTS_HEADER_LEN 7
const int sampleFrequencyTable[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
int getADTSHeader(char *const adtsHeader, const int dataLength, const int profile, const int samplerate, const int channels)
{
    int sampleFrequenceIndex = 3; // 默认使用 48000HZ
    int adtslen = dataLength + 7;

    int frequencise_size = sizeof(sampleFrequencyTable) / sizeof(sampleFrequencyTable[0]);
    int i = 0;
    for (int i = 0; i < frequencise_size; i++)
    {
        if (sampleFrequencyTable[i] == samplerate)
        {
            sampleFrequenceIndex = i;
            break;
        }
    }
    if (i >= frequencise_size)
    {
        av_log(NULL, AV_LOG_ERROR, "不支持次采样率:%d\n", samplerate);
        return -1;
    }
    // syncword :同步头总是OxFFF, all bits must be 1,代表着一个ADTS帧的开始
    adtsHeader[0] = 0xff; // syncword:0xff 高8bits
    adtsHeader[1] = 0xf0; // syncword:0xfff 低4bits

    adtsHeader[1] |= (0 << 3); // MPEG Version:0 for MPEG-4,1 for MPEG-2 1bit
    adtsHeader[1] |= (0 << 1); // Layer:0 2bit
    adtsHeader[1] |= 1;        // protection absent:1 1bit -- 要不要CRC校验 -- 不要

    adtsHeader[2] = (profile << 6);                      // porfile:profile 2bits
    adtsHeader[2] |= (sampleFrequenceIndex & 0x0f) << 2; // sampling frequency index:sampling_frequency_index 4bits
    adtsHeader[2] |= (0 << 1);                           // private bit:0 1bit
    adtsHeader[2] |= (channels & 0x04) >> 2;             // channel configuration:channels 高1bit

    adtsHeader[3] = (channels & 0x03) << 6;      // channel configuration:channels 低2bits
    adtsHeader[3] |= (0 << 5);                   // original:0 1bit
    adtsHeader[3] |= (0 << 4);                   // home:0 1bit
    adtsHeader[3] |= (0 << 3);                   // copyright id bit: 0 1bit
    adtsHeader[3] |= (0 << 2);                   // copyright id start: 0 1bit
    adtsHeader[3] |= ((adtslen & 0x1800) >> 11); // frame length: value 高2bits

    adtsHeader[4] = (uint8_t)((adtslen & 0x7f8) >> 3); // frame length:vale 中间8bits

    adtsHeader[5] = (uint8_t)((adtslen & 0x7) << 5); // frame length:value 低3bits

    adtsHeader[5] |= 0x1f; // buffer fullness: 0x7ff 高5bits
    adtsHeader[6] = 0xfc;  // buffer fullness: 0x7ff 低6bits
    return 0;
}
cpp 复制代码
int DemuxingVideoAndAudio(const char *inFileName, const char *h264FileName, const char *aacFileName)
{
    /*********************************************************************************/
    FILE *aac_fd = fopen(aacFileName, "wb");
    FILE *h264_fd = fopen(h264FileName, "wb");
    if (!aac_fd || !h264_fd)
    {
        av_log(NULL, AV_LOG_ERROR, "open %s or %s file failed or \n", h264FileName, aacFileName);
        goto _end;
    }
    /*********************************************************************************/
    AVFormatContext *inFmtCtx = NULL;
    // 当 inFmtCtx 为空时 avformat_open_input 会给 inFmtCtx 分配空间
    int ret = avformat_open_input(&inFmtCtx, inFileName, NULL, NULL);
    if (ret != 0)
    {
        av_log(NULL, AV_LOG_ERROR, "open input file:%s failed:%s\n", inFileName, av_err2str(ret));
        return -1;
    }

    int videoIndex = av_find_best_stream(inFmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    int audioIndex = av_find_best_stream(inFmtCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (videoIndex < 0 || audioIndex < 0) // 输入的媒体文件必须要有视频和音频流
    {
        av_log(NULL, AV_LOG_ERROR, "find best stream failed,index is %s\n", av_err2str(videoIndex));
        av_log(NULL, AV_LOG_ERROR, "find best stream failed,index is %s\n", av_err2str(audioIndex));
        goto _end;
    }

    AVPacket *packet = NULL;
    packet = av_packet_alloc();
    av_init_packet(packet);

    const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");
    if (bsfilter == NULL)
    {
        av_log(NULL, AV_LOG_ERROR, "get h264_mp4toannexb bsf failed\n");
        goto _end;
    }
    // 用于存储比特流过滤器的上下文信息
    AVBSFContext *bsfCtx = NULL;
    // 比特流过滤器分配内存并初始化上下文结构体。
    ret = av_bsf_alloc(bsfilter, &bsfCtx);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "bsfCtx alloc failed\n");
        goto _end;
    }
    // 输入流的参数复制到比特流过滤器的输入参数中,以便过滤器能够正确处理输入数据。
    ret = avcodec_parameters_copy(bsfCtx->par_in, inFmtCtx->streams[videoIndex]->codecpar);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "avcodec parameters copy failed\n");
        goto _end;
    }
    // 初始化比特流过滤器,准备对输入数据进行处理。
    ret = av_bsf_init(bsfCtx);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "bsfCtx init failed\n");
        goto _end;
    }
    // 不释放packet->buf 如果外部不去释放,会导致内存泄流
    while (av_read_frame(inFmtCtx, packet) == 0)
    {
        if (packet->stream_index == videoIndex)
        {
            // av_bsf_send_packet 发一次
            // av_bsf_receive_packet 读到返回eagain才继续send
            if (av_bsf_send_packet(bsfCtx, packet) == 0) // 这行代码将数据包packet发送给比特流过滤器bsfCtx进行处理。如果返回值为0,表示成功发送数据包到过滤器。
            {                                            // 内部把我们传入的buf转移到自己的bsfCtx 内部
                int outPacketCount = 0;
                while (av_bsf_receive_packet(bsfCtx, packet) == 0)
                {
                    outPacketCount++;                                                  // 因为读到返回eagain才继续send 看下读了
                    size_t writeSize = fwrite(packet->data, 1, packet->size, h264_fd); // 接收到的数据包写入到文件中
                    if (writeSize != packet->size)
                    {
                        av_log(NULL, AV_LOG_ERROR, "wirte file failed!\n");
                        av_packet_unref(packet); // 释放packet资源
                        ret = -1;
                        break;
                    }
                    av_log(NULL, AV_LOG_DEBUG, "outPacketCount%d, writeSize:%ld, packet->size:%u -- line:%d\n", outPacketCount, writeSize, packet->size, __LINE__);
                }
            }
            av_packet_unref(packet); // 释放packet资源
        }
        else if (packet->stream_index == audioIndex) // 处理音频
        {
            char adtsHeader[ADTS_HEADER_LEN] = {0};
            // 如果数据包属于音频流,则会为音频数据包创建一个 ADTS 头部(ADTS 头部是一种用于封装音频数据的格式,可以通过网络传输或存储在文件中)。
            getADTSHeader(adtsHeader, packet->size,
                          inFmtCtx->streams[audioIndex]->codecpar->profile,     // 音频编解码器的配置文件。
                          inFmtCtx->streams[audioIndex]->codecpar->sample_rate, // 音频的采样率
                          inFmtCtx->streams[audioIndex]->codecpar->channels);   // 音频的通道数。
            // 写文件头
            size_t writeSize = fwrite(adtsHeader, 1, ADTS_HEADER_LEN, aac_fd);
            if (writeSize != sizeof(adtsHeader))
            {
                av_log(NULL, AV_LOG_ERROR, "wirte file failed!\n");
                av_packet_unref(packet); // 释放packet资源
                ret = -1;
                break;
            }
            // 写 adts data
            writeSize = fwrite(packet->data, 1, packet->size, aac_fd);
            if (writeSize != packet->size)
            {
                av_log(NULL, AV_LOG_WARNING, "wirte file failed writeSize:%ld packet.size:%d\n", writeSize, packet->size);
                av_packet_unref(packet); // 释放packet资源
                ret = -1;
                break;
            }
            av_packet_unref(packet);
        }
        else
        {
            av_packet_unref(packet);
        }
    }
    av_log(NULL, AV_LOG_INFO, "decapsulation finish\n");
_end: // 释放资源
    if (inFmtCtx != NULL)
        avformat_close_input(&inFmtCtx);
    if (h264_fd != NULL)
        fclose(h264_fd);
    if (aac_fd != NULL)
        fclose(aac_fd);
    if (bsfCtx)
        av_bsf_free(&bsfCtx);
    if (packet)
        av_packet_free(&packet);
    return ret;
}
相关推荐
ZouZou老师5 小时前
FFmpeg性能优化经典案例
性能优化·ffmpeg
aqi008 小时前
FFmpeg开发笔记(九十)采用FFmpeg套壳的音视频转码百宝箱FFBox
ffmpeg·音视频·直播·流媒体
齐齐大魔王9 小时前
FFmpeg
ffmpeg
你好音视频11 小时前
FFmpeg RTSP拉流流程深度解析
ffmpeg
IFTICing1 天前
【环境配置】ffmpeg下载、安装、配置(Windows环境)
windows·ffmpeg
haiy20111 天前
FFmpeg 编译
ffmpeg
aqi001 天前
FFmpeg开发笔记(八十九)基于FFmpeg的直播视频录制工具StreamCap
ffmpeg·音视频·直播·流媒体
八月的雨季 最後的冰吻1 天前
FFmepg--28- 滤镜处理 YUV 视频帧:实现上下镜像效果
ffmpeg·音视频
ganqiuye1 天前
向ffmpeg官方源码仓库提交patch
大数据·ffmpeg·video-codec
草明1 天前
ffmpeg 把 ts 转换成 mp3
ffmpeg