首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

如何通过libavformat将H264流复用成MP4文件

基础概念

libavformat 是 FFmpeg 项目中的一个库,用于处理多媒体文件的格式解析和封装。H.264 是一种视频编码标准,广泛用于高清视频传输和存储。MP4 是一种多媒体容器格式,可以包含视频、音频、字幕等多种媒体流。

相关优势

  1. 兼容性:MP4 格式在各种设备和播放器上都有很好的兼容性。
  2. 灵活性:可以包含多种媒体流,并且可以灵活地添加或删除音轨、字幕等。
  3. 压缩效率:H.264 编码在保证视频质量的同时,能够有效减少文件大小。

类型

  • 视频流:H.264 编码的视频流。
  • 音频流:通常为 AAC 编码的音频流。
  • 字幕流:可选的 SRT 或其他格式的字幕流。

应用场景

  • 视频会议记录。
  • 在线视频流媒体服务。
  • 移动设备上的视频播放。

实现步骤

以下是一个简单的示例代码,展示如何使用 libavformat 将 H.264 流复用成 MP4 文件:

代码语言:txt
复制
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>

int main(int argc, char *argv[]) {
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *video_dec_ctx = NULL, *video_enc_ctx = NULL;
    AVStream *video_st = NULL;
    AVPacket pkt;
    int ret, i;

    // 初始化 libavformat 和注册所有编解码器、格式和协议
    av_register_all();

    // 打开输入文件
    if ((ret = avformat_open_input(&fmt_ctx, "input.h264", NULL, NULL)) < 0) {
        fprintf(stderr, "Could not open input file '%s'", argv[1]);
        goto end;
    }

    // 查找流信息
    if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
        fprintf(stderr, "Failed to retrieve input stream information");
        goto end;
    }

    // 查找视频流
    for (i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_st = fmt_ctx->streams[i];
            break;
        }
    }

    if (!video_st) {
        fprintf(stderr, "Could not find video stream in the input, aborting\n");
        ret = -1;
        goto end;
    }

    // 获取解码器参数并打开解码器
    AVCodec *video_dec = avcodec_find_decoder(video_st->codecpar->codec_id);
    if (!video_dec) {
        fprintf(stderr, "Failed to find decoder for stream #%u\n", video_st->index);
        ret = AVERROR_DECODER_NOT_FOUND;
        goto end;
    }

    video_dec_ctx = avcodec_alloc_context3(video_dec);
    if (!video_dec_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    ret = avcodec_parameters_to_context(video_dec_ctx, video_st->codecpar);
    if (ret < 0) {
        fprintf(stderr, "Failed to copy decoder parameters to input context\n");
        goto end;
    }

    if ((ret = avcodec_open2(video_dec_ctx, video_dec, NULL)) < 0) {
        fprintf(stderr, "Failed to open decoder for stream #%u\n", video_st->index);
        goto end;
    }

    // 创建输出格式上下文
    AVOutputFormat *out_fmt = av_guess_format(NULL, "output.mp4", NULL);
    if (!out_fmt) {
        fprintf(stderr, "Could not deduce output format from file extension: using MPEG-4.\n");
        out_fmt = av_guess_format("mp4", NULL, NULL);
    }

    if (!out_fmt) {
        fprintf(stderr, "Could not find suitable output format\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    AVFormatContext *out_fmt_ctx = NULL;
    avformat_alloc_output_context2(&out_fmt_ctx, out_fmt, NULL, "output.mp4");
    if (!out_fmt_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    // 复制视频流
    AVCodec *video_enc = avcodec_find_encoder(out_fmt->video_codec);
    if (!video_enc) {
        fprintf(stderr, "Necessary encoder not found\n");
        ret = AVERROR_ENCODER_NOT_FOUND;
        goto end;
    }

    video_enc_ctx = avcodec_alloc_context3(video_enc);
    if (!video_enc_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    video_enc_ctx->width = video_dec_ctx->width;
    video_enc_ctx->height = video_dec_ctx->height;
    video_enc_ctx->sample_aspect_ratio = video_dec_ctx->sample_aspect_ratio;
    video_enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
    video_enc_ctx->time_base = video_st->time_base;

    if (out_fmt->flags & AVFMT_GLOBALHEADER)
        video_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    ret = avcodec_parameters_from_context(video_enc_ctx->codecpar, video_dec_ctx);
    if (ret < 0) {
        fprintf(stderr, "Could not copy the stream parameters\n");
        goto end;
    }

    ret = avcodec_open2(video_enc_ctx, video_enc, NULL);
    if (ret < 0) {
        fprintf(stderr, "Cannot open video encoder for stream #%u\n", video_st->index);
        goto end;
    }

    AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
    if (!out_stream) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    ret = avcodec_parameters_copy(out_stream->codecpar, video_st->codecpar);
    if (ret < 0) {
        fprintf(stderr, "Failed to copy encoder parameters to output stream #%u\n", out_stream->index);
        goto end;
    }

    out_stream->time_base = video_enc_ctx->time_base;

    // 打开输出文件
    if (!(out_fmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&out_fmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file '%s'", "output.mp4");
            goto end;
        }
    }

    // 写入文件头
    ret = avformat_write_header(out_fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error occurred when opening output file\n");
        goto end;
    }

    // 读取输入文件并写入输出文件
    while (1) {
        ret = av_read_frame(fmt_ctx, &pkt);
        if (ret < 0)
            break;

        if (pkt.stream_index == video_st->index) {
            ret = avcodec_send_packet(video_dec_ctx, &pkt);
            if (ret < 0) {
                fprintf(stderr, "Error sending a packet for decoding\n");
                goto end;
            }

            while (ret >= 0) {
                ret = avcodec_receive_frame(video_dec_ctx, frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;
                else if (ret < 0) {
                    fprintf(stderr, "Error during decoding\n");
                    goto end;
                }

                frame->pts = frame->pkt_dts;

                ret = avcodec_send_frame(video_enc_ctx, frame);
                if (ret < 0) {
                    fprintf(stderr, "Error sending a frame for encoding\n");
                    goto end;
                }

                while (ret >= 0) {
                    ret = avcodec_receive_packet(video_enc_ctx, &out_pkt);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                        break;
                    else if (ret < 0) {
                        fprintf(stderr, "Error during encoding\n");
                        goto end;
                    }

                    out_pkt.stream_index = out_stream->index;
                    ret = av_interleaved_write_frame(out_fmt_ctx, &out_pkt);
                    if (ret < 0) {
                        fprintf(stderr, "Error while writing output packet\n");
                        goto end;
                    }
                    av_packet_unref(&out_pkt);
                }
            }
        }
        av_packet_unref(&pkt);
    }

    // 写入文件尾
    av_write_trailer(out_fmt_ctx);

end:
    av_packet_unref(&pkt);
    if (video_dec_ctx)
        avcodec_close(video_dec_ctx);
    if (video_enc_ctx)
        avcodec_close(video_enc_ctx);
    if (fmt_ctx && !(fmt_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&fmt_ctx->pb);
    if (out_fmt_ctx && !(out_fmt->flags & AVFMT_NOFILE))
        avio_closep(&out_fmt_ctx->pb);
    avformat_free_context(fmt_ctx);
    avformat_free_context(out_fmt_ctx);

    return ret < 0;
}

参考链接

常见问题及解决方法

  1. 编码器未找到
    • 确保 FFmpeg 编译时包含了所需的编码器。
    • 检查编码器名称是否正确。
  • 解码器未找到
    • 确保 FFmpeg 编译时包含了所需的解码器。
    • 检查解码器名称是否正确。
  • 文件打开失败
    • 确保输入文件存在且可读。
    • 确保输出文件路径可写。
  • 编码或解码错误
    • 检查输入数据的格式是否正确。
    • 确保编码和解码参数一致。

通过以上步骤和示例代码,你可以将 H.264 流复用成 MP4 文件。如果遇到具体问题,请根据错误信息进行排查和解决。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

  • wmv转换mp4格式转换器_avi可以转化成什么格式

    原文:http://www.mworkbox.com/wp/work/314.html MP4的视频H264封装有2种格式:h264和avc1,对于这个细节,很容易被忽略。笔者也是在改编LIVE555流媒体时,增加mp4文件类型支持时遇到了该问题。 (一)首先,从原理上了解一下这2种格式的区别: AVC1 描述:H.264 bitstream without start codes.一般通过ffmpeg转码生成的视频,是不带起始码0×00000001的。 H264 描述:H.264 bitstream with start codes.一般对于一下HDVD等电影的压制格式,是带有起始码0×00000001的。 (二)其次,通过VLC播放器,可以查看到具体的格式。打开视频后,通过菜单【工具】/【编解码信息】可以查看到【编解码器】具体格式,举例如下,编解码器信息: 编码: H264 – MPEG-4 AVC (part 10) (avc1) 编码: H264 – MPEG-4 AVC (part 10) (h264) (三)最后,分享一下ffmpeg demux MP4文件后,转换视频流为live555可直接使用的h264 ES流的经验和方法: 针对(avc1),av_read_frame后,取前四个字节为长度,把前四字节直接替换为0×00,0×00,0×00,0×01即可,但注意每个frame可以有多个NAUL:

    02

    Chrome 浏览器支持4k/h265的定制开发

    官方的Chrome浏览器是不支持h265格式的MP4播放,可能是由于Google处于维护在的VP9编码生态考虑(不要跟我说专利费太重问题,微软的edge,苹果的safari都支持)。实际上chrome最初也不支持h264视频解码,但h264在视频编码媒体领域中已经是势不可当(2003年发布),处于绝对的领导地位,后面不得不支持h264的MP4播放。对于新一代视频编码h265,Google同样持以抵制的态度,至今为止H265商业化8年了(2013年发布),Google的chrome还是不支持。但实际上硬件编码器都已经完全支持h265编码,相反大多数硬编码器都不支持VP9(目前的英伟达,AMD,显卡都不支持VP9编码)。要想实现低流量,高质量的视频传输,加上硬件的加持,编码器只能使用h265了,同时客户端目前所有的显卡(包括Intel核显)都支持h265解码了。而chrome 为了封杀h265,就是不支持h265解码。本文就介绍如何定制开源的chrome,支持h265视频解码。

    03
    领券