c++ - 从 ffmpeg 1.1 升级到 3.3 后没有帧解码

标签 c++ ffmpeg h.264 decoding libavcodec

我有一个 C++ 源代码,使用 libavcodec 解码 h264 rtsp 流帧。此源代码是使用 ffmpeg 1.1 编写的。现在,当我升级到 ffmpeg 3.3 时,除了解码帧不起作用之外,一切似乎都正常工作。 在旧版本中,我使用的是 avcodec_decode_video2。升级后,avcodec_decode_video2 始终将 got_picture 设置为 0,返回值等于输入数据包的大小(这意味着使用了所有数据)。并且永远不会解码帧。 我还删除了 avcodec_decode_video2 并使用 avcodec_send_packet 和 avcodec_receive_frame 完成解码,但 avcodec_send_packet 始终返回 0,而 avcodec_receive_frame 始终返回 -11 (EAGAIN)。

这是我用来解码的代码:

#include "stdafx.h"
#include <stdlib.h>
#include <string>
#include <iostream>
using namespace std;

extern "C"{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/pixfmt.h"
}

int extraDataSize;
static const int MaxExtraDataSize = 1024;
uint8_t extraDataBuffer[MaxExtraDataSize];

void AddExtraData(uint8_t* data, int size)
{
    auto newSize = extraDataSize + size;
    if (newSize > MaxExtraDataSize){
        throw "extradata exceeds size limit";
    }
    memcpy(extraDataBuffer + extraDataSize, data, size);
    extraDataSize = newSize;
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::string strFramesPath("g:\\frames\\");

    AVCodec* avCodec;
    AVCodecContext* avCodecContext;
    AVFrame* avFrame;
    AVCodecID codecId = AV_CODEC_ID_H264;
    unsigned char sprops_part_1[9] = { 0x27, 0x42, 0x80, 0x1f, 0xda, 0x02, 0xd0, 0x49, 0x10 };
    unsigned char sprops_part_2[4] = { 0x28, 0xce, 0x3c, 0x80 };

    av_register_all();
    avcodec_register_all();
    avCodec = avcodec_find_decoder(codecId);
    avCodecContext = avcodec_alloc_context3(avCodec);
    if (!avCodecContext)
    {
        cout << "avcodec_alloc_context3 failed." << endl;
        return 0;
    }
    uint8_t startCode[] = { 0x00, 0x00, 0x01 };

    // sprops
    {
        // sprops 1
        AddExtraData(startCode, sizeof(startCode));
        AddExtraData(sprops_part_1, 9);
        // sprops 2
        AddExtraData(startCode, sizeof(startCode));
        AddExtraData(sprops_part_2, 4);

        avCodecContext->extradata = extraDataBuffer;
        avCodecContext->extradata_size = extraDataSize;
    }

    AddExtraData(startCode, sizeof(startCode));
    avCodecContext->flags = 0;
    if (avcodec_open2(avCodecContext, avCodec, NULL) < 0)
    {
        cout << "failed to open codec" << endl;
        return 0;
    }
    avFrame = av_frame_alloc();
    if (!avFrame)
    {
        cout << "failed to alloc frame" << endl;
        return 0;
    }

    void *buffer = malloc(100 * 1024);  // 100 KB buffer - all frames fit in this buffer
    for (int nFrameIndex = 0; nFrameIndex < 257; nFrameIndex++)
    {
        std::string strFilename = std::string("g:\\frames\\" + std::to_string(nFrameIndex));
        FILE* f = fopen(strFilename.c_str(), "rb");
        fseek(f, 0, SEEK_END);
        long nFileSize = ftell(f);
        fseek(f, 0, SEEK_SET);
        size_t nReadSize = fread(buffer, 1, nFileSize, f);
        // cout << strFilename << endl;
        if (nReadSize != nFileSize)
        {
            cout << "Error reading file data" << endl;
            continue;
        }
        AVPacket avpkt;
        avpkt.data = (uint8_t*)buffer;
        avpkt.size = nReadSize;

        while (avpkt.size > 0)
        {
            int got_frame = 0;
            auto len = avcodec_decode_video2(avCodecContext, avFrame, &got_frame, &avpkt);
            if (len < 0) {
                //TODO: log error
                cout << "Error decoding - error code: " << len << endl;
                break;
            }
            if (got_frame)
            {
                cout << "* Got 1 Decoded Frame" << endl;
            }
            avpkt.size -= len;
            avpkt.data += len;
        }
    }

    getchar();
    return 0;
}

测试帧数据可以从这个链接下载: Frames.zip (~3.7MB)

我使用了来自 Builds - Zeranoe FFmpeg 的 Windows 版本 如果将此代码复制粘贴到 IDE 中,代码将成功编译。使用 libavcodec 新版本,没有帧被解码。使用旧版本的 libavcodec (20141216-git-92a596f),在输入帧 2 时开始解码。

有什么想法吗?

最佳答案

FFmpeg/libAV 的描述相当复杂,就可理解性而言可能不是最好的(如果不是说它很糟糕的话)。你应该仔细阅读它。

但是,您缺少的是使用 av_init_packet 初始化 AVPacket。或者使用 av_packet_alloc() 来创建将初始化数据包的数据包(并且更好地复制它以进行编码)。

// code ...
AVPacket avpkt;
av_init_packet(&avpkt);         // initialize packet
avpkt.data = (uint8_t*)buffer;
avpkt.size = nReadSize;
// code ...

// code ...
AVPacket* avpkt = av_packet_alloc();
avpkt->data = (uint8_t*)buffer;
avpkt->size = nReadSize;
// use ...
auto len = avcodec_decode_video2(avCodecContext, avFrame, &got_frame, avpkt);

使用 av_packet_alloc() 在代码中也更加一致,因为您还使用 AVFrame 作为指针。

记得把packets/frames/pictures也一起free,不然会内存泄露。

还有 here是一个(或多或少)好的/“最新”的 ffmpeg 教程。

关于c++ - 从 ffmpeg 1.1 升级到 3.3 后没有帧解码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50113717/

相关文章:

c++ - 插入 int 集合时崩溃

c++ - 为什么 std::hex 会导致内存损坏 vector.size()?

android - 将图像编码为电影文件

php - 在 php 中从服务器中的视频创建缩略图

ffmpeg - 从 Raspberry Pi 上的 USB 摄像头获取原始 h264 数据包

height - H264 从序列参数集 (SPS) NAL 单元获取帧高度和宽度

c++ - Linux 线程函数中的局部变量?

ffmpeg - 使用 FFMPEG 将 YUV 帧转换为 RGBA 帧

flv - 如何从可以由 Actionscript NetStream 播放的原始 h264 生成 FLV 流?

c++ - 图形驱动程序如何以编程方式从 CPU 到 GPU 进行通信?