【问题标题】:Programmatically read fMP4 using ffmpeg使用 ffmpeg 以编程方式读取 fMP4
【发布时间】:2019-08-28 18:33:30
【问题描述】:

我解析 fmp4 的所有尝试都失败了,并出现以下错误:

avcodec_send_packet 返回 -10949955291 并打印以下错误:

[h264 @ 0x105001600] No start code is found.
[h264 @ 0x105001600] Error splitting the input into NAL units.

我做了什么?

下载了一个完美可玩的h.264文件并使用以下cmd对其进行分段

ffmpeg -i long.mp4 -an -sn -vcodec libx264 -force_key_frames "expr:gte(t,n_forced*4)" -sc_threshold 0 -r 25 -f hls -hls_time 4 -hls_list_size 99999 -start_number 1 -hls_segment_type fmp4 -hls_fmp4_init_filename init.mp4 -t 30 -threads 0 big_bunny.m3u8

并使用以下AVIO阅读示例

#include <fstream>
#include <iterator>
#include <algorithm>
#include <libavcodec/avcodec.h>

std::vector<unsigned char> b1;
std::vector<unsigned char> b2;
bool initialized = false;
int bytesread = 0;

static int read_packet(void* opaque, uint8_t* buf, int buf_size)
{
    if (b1.size() && !initialized) {
        size_t bytesToRead = std::min(buf_size, (int)b1.size());
        ::memcpy(buf, b1.data(), bytesToRead);
        bytesread += bytesToRead;
        if (bytesread >= b1.size()) {
            initialized = true;
            bytesread = 0;
        }
        return bytesToRead;
    }

    ::memcpy(buf, b2.data() + bytesread, buf_size);
    bytesread += buf_size;
    return buf_size;
}

int main(int argc, char** argv)
{
    AVFormatContext* fmt_ctx = NULL;
    AVIOContext* avio_ctx = NULL;
    uint8_t *buffer = NULL, *buffer2 = nullptr, *avio_ctx_buffer = NULL;
    size_t buffer_size, buffer_size2, avio_ctx_buffer_size = 4096;
    char* input_filename = NULL;
    int ret = 0;
    struct buffer_data bd = {0};

    std::ifstream input1("/Users/x/Downloads/fmp4/init.mp4", std::ios::binary);
    std::ifstream input2("/Users/x/Downloads/fmp4/big_bunny1.m4s", std::ios::binary);

    b1 = std::vector<unsigned char>((std::istreambuf_iterator<char>(input1)), std::istreambuf_iterator<char>());
    b2 = std::vector<unsigned char>((std::istreambuf_iterator<char>(input2)), std::istreambuf_iterator<char>());

    avio_ctx_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);

    fmt_ctx = avformat_alloc_context();
    avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, nullptr, &read_packet, NULL, NULL);
    fmt_ctx->pb = avio_ctx;

    AVDictionary* opts = NULL;
    //   av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
    ret = avformat_open_input(&fmt_ctx, NULL, NULL, &opts);
    ret = avformat_find_stream_info(fmt_ctx, NULL);

    AVCodec* decoder = nullptr;

    decoder = avcodec_find_decoder(fmt_ctx->streams[0]->codecpar->codec_id);
    AVCodecContext* decoderCtx = avcodec_alloc_context3(decoder);

    ret = avcodec_open2(decoderCtx, decoder, nullptr);

    AVPacket pkt;
    av_init_packet(&pkt);

    AVFrame* frame = av_frame_alloc();
    while (true) {
        ret = av_read_frame(fmt_ctx, &pkt);

        ret = avcodec_send_packet(decoderCtx, &pkt);
        if (ret != 0)
            assert(false);

        for (;;) {
            ret = avcodec_receive_frame(decoderCtx, frame);
            if (ret < 0) {
                break;
            }

            int g;
            g = 0;
        }
    }

我什至不确定这是处理fmp4 类型的正确方法。但为了清楚起见,我只是将初始化文件加载到第一个缓冲区,将实际媒体文件加载到第二个缓冲区,然后在缓冲区之间切换到 buf_size 的值。

【问题讨论】:

  • 1.需要使用avcodec_parameters_to_context将编解码器参数从输入流复制到输出编解码器上下文。 2.有时avio_alloc_context需要自定义seek()

标签: c++ ffmpeg mp4 libavcodec libavformat


【解决方案1】:

我不认为在你使用avio_alloc_context demuxer 的情况下获得所有需要的信息 - 请参阅Opening a media file,但这是一个疯狂的猜测,因为你的输出显示 mp4 demuxer 没有错误,但仅来自H.264 比特流解析器,所以我倾向于探测的文件格式可能是原始 H.264,但很难说。但是,如果我弄错了,您没有提供搜索回调,并且解复用 mp4 肯定需要搜索跳过框,然后搜索示例数据。所以,我首先尝试提供一个 seek 回调,看看它是否被调用。但是从示例中我们不确定为什么您甚至需要 I/O 回调,因为数据驻留在文件中而不是内存位置中,所以我建议尝试使用标准的 avformat_open_input 方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-15
    • 2010-09-28
    • 1970-01-01
    • 2012-04-16
    • 1970-01-01
    • 1970-01-01
    • 2016-11-08
    • 1970-01-01
    相关资源
    最近更新 更多