前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ffmpeg hevc_ffmpeg视频解码

ffmpeg hevc_ffmpeg视频解码

作者头像
全栈程序员站长
发布2022-11-03 16:46:09
1.2K0
发布2022-11-03 16:46:09
举报
文章被收录于专栏:全栈程序员必看

本次目标:

1)将容器中的音频码流和视频码流分离出来。

2)针对mp4文件中的码流情况进行修复。

解封装的基本过程:

代码语言:javascript
复制
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"

// MPEG-TS文件解封装得到的码流可直接播放
// MP4/FLV/MKV解封装得到的码流不可播放;
// 这与容器的封装方式有关。
void demuxer(const char *url) {
    // 初始化格式上下文
    AVFormatContext *fmt_ctx = avformat_alloc_context();
    if (fmt_ctx == NULL) {
        printf("failed to alloc format context\n");
        goto _Error;
    }

    // 打开输入流
    if (avformat_open_input(&fmt_ctx, url, NULL, NULL) < 0) {
        printf("failed to open input url\n");
        goto _Error;
    }

    // 读取媒体文件信息
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        printf("failed to find stream\n");
        goto _Error;
    }
    av_dump_format(fmt_ctx, 0, url, 0);


    // 寻找音频流和视频流下标
    int video_index = -1, audio_index = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_index = i;
        } else if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_index = i;
        }
    }
    if (video_index < 0 || audio_index < 0) {
        printf("failed to find stream index\n");
        goto _Error;
    }

    // 由打印的视频文件信息确定码流类型
    char audiofile[128], videofile[128];
    printf("type the name of output audiofile:");
    scanf("%s", audiofile);
    printf("\ntype the name of output videofile:");
    scanf("%s", videofile);
    FILE *faudio = fopen(audiofile, "w+");
    FILE *fvideo = fopen(videofile, "w+");
    AVPacket *packet = av_packet_alloc();

    while (av_read_frame(fmt_ctx, packet) == 0) {

        if (packet->stream_index == audio_index) {
            fwrite(packet->data, 1, packet->size, faudio);
        }
        else if (packet->stream_index == video_index) {
            fwrite(packet->data, 1, packet->size, fvideo);
        }
        av_packet_unref(packet);
    }

_Error:
    if (fmt_ctx) avformat_close_input(&fmt_ctx);
    if (faudio)  fclose(faudio);
    if (fvideo)  fclose(fvideo);
    if (packet)  av_packet_free(&packet);
}


int main(int argc, char const* argv[])
{
    demuxer(argv[1]);
    return 0;
}

编译测试:得到视频信息后,根据文件中的编码信息(mp3或aac,h264或mpeg4,mpeg4码流文件后辍为.m4v),命名解封装的码流文件。

image-20210514113830833
image-20210514113830833

mp4文件解封装得到的h264码流和aac码流都不能解码播放!

1、mp4文件中的码流有什么不同
代码语言:javascript
复制
	Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 
	640x352, 748 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
	
	Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 
	44100 Hz, stereo, fltp, 128 kb/s (default)

MP4封装格式是基于QuickTime容器格式定义,媒体描述与媒体数据分开。从MP4得到的H264和AAC码流是ES流,它们缺失解码时必要的起始码/SPS/PPSadts头

我们常规的H264帧数据保存格式是annexb,是具有起始码0x000001或0x00000001;mpeg-ts文件中保存的是视频码流是存在起始码的,而在mp4文件中没有起始码。

H.264视频编码格式主要分为两种形式,即带起始码的H.264码流不带起始码的H.264码流,其中,前者就是我们比较熟悉的H264、X264;后者就是指AVC1

More:MP4中的H264和AAC

2、怎么修复MP4中的码流?

AAC码流(mp4a):保存一帧码流数据前,补充相应的adts头; H264码流(avc1):使用h264_mp4toannexb过滤器,对视频码流进行处理。

①每一帧前补充ADTS头

代码语言:javascript
复制
// 生成7字节的ADTS头
char *adts_header_gen(int len) {
    static char header[7];

    int profile = 2;    // AAC LC
    int freqidx = 4;    // 3 - 48k, 4 - 44.1k 
    int chncfg  = 2;    // 声道数量 

    header[0] = 0xFF;
    header[1] = 0xF1;
    header[2] = ((profile-1) << 6) | (freqidx << 2) | (chncfg >> 2);
    header[3] = ((chncfg & 3) << 6)| (len >> 11);
    header[4] = (len & 0x7FF) >> 3;
    header[5] = ((len & 0x7) << 5) | 0x1F;
    header[6] = 0xFC;
    return header;
}
代码语言:javascript
复制
if (packet->stream_index == audio_index) {
	// packet->size是adts中数据块的长度
	fwrite(adts_header_gen(packet->size+7), 1, 7, faudio);
	fwrite(packet->data, 1, packet->size, faudio);
}

More:AAC音频码流解析

②使用h264_mp4toannexb过滤器处理h264码流

代码语言:javascript
复制
// 初始化过滤器
const AVBitStreamFilter *bsf = av_bsf_get_by_name("h264_mp4toannexb");
if (bsf == NULL) {
	printf("failed to find stream filter\n");
	goto _Error;
}
AVBSFContext *bsf_ctx;
av_bsf_alloc(bsf, &bsf_ctx);
avcodec_parameters_copy(bsf_ctx->par_in, fmt_ctx->streams[video_index]->codecpar);
av_bsf_init(bsf_ctx);
代码语言:javascript
复制
else if (packet->stream_index == video_index) {
    if (av_bsf_send_packet(bsf_ctx, packet) == 0) {
        while (av_bsf_receive_packet(bsf_ctx, packet) == 0) {
            fwrite(packet->data, 1, packet->size, fvideo);
        }
    }
}

经过比特流过滤器的处理,每个AVPacket的data添加了H.264的NALU的起始码{0,0,0,1};每个IDR帧数据前面添加了SPS和PPS。

More:解析h264视频码流

视频播放效果:

原mp4文件:

image-20210514112106240
image-20210514112106240

out.h264码流文件:

image-20210514112149236
image-20210514112149236

out.aac文件:

image-20210514112333031
image-20210514112333031

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/181225.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月16日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本次目标:
  • 解封装的基本过程:
  • mp4文件解封装得到的h264码流和aac码流都不能解码播放!
    • 1、mp4文件中的码流有什么不同
      • 2、怎么修复MP4中的码流?
        • 视频播放效果:
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档