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;
}
相关推荐
韩曙亮11 小时前
【FFmpeg】FFmpeg 内存结构 ③ ( AVPacket 函数简介 | av_packet_ref 函数 | av_packet_clone 函数 )
ffmpeg·音视频·avpacket·av_packet_clone·av_packet_ref·ffmpeg内存结构
oushaojun215 小时前
ubuntu中使用ffmpeg和nginx推流rtmp视频
nginx·ubuntu·ffmpeg·rtmp
莫固执,朋友16 小时前
网络抓包工具tcpdump 在海思平台上的编译使用
网络·ffmpeg·音视频·tcpdump
lxkj_202417 小时前
修改ffmpeg实现https-flv内容加密
网络协议·https·ffmpeg
cuijiecheng201817 小时前
音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现
ffmpeg·音视频
VisionX Lab21 小时前
数据脱敏工具:基于 FFmpeg 的视频批量裁剪
python·ffmpeg·音视频
柳鲲鹏2 天前
全网首发:Ubuntu编译跨平台嵌入式支持ffmpeg的OpenCV
linux·ubuntu·ffmpeg
冰山一脚20132 天前
ffplay音频SDL播放处理
ffmpeg
cuijiecheng20182 天前
音视频入门基础:MPEG2-TS专题(7)——FFmpeg源码中,读取出一个transport packet数据的实现
ffmpeg·音视频
Maxwellhang2 天前
【java-ffmpeg】java 内存测试和集成
java·ffmpeg·jni