LIVE555如何使用h264成帧器类获取ffmpeg的nal单位

时间:2016-07-25 09:00:46

标签: c++ ffmpeg h.264 live555

我正在尝试创建一个小应用程序,它将保存来自h264流的帧。 我以testRTSP程序为例,在DummySink::afterGettingFrame函数中进行了一些更改,以便在ffmpeg库的帮助下解码帧。 据我所知,从frameSize开始,我的前两帧是SPS单元,所以我将它们连接到第三帧,然后将新的大帧发送到ffmpeg解码器。但那不起作用。 ffmpeg告诉我,我的第一帧对于SPS来说太大了,然后它告诉我没有框架...我不知道我需要在这里改变什么。

void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/)
{
u_int8_t start_code[4] = { 0x00, 0x00, 0x00, 0x01 };
int stCodeLen = 4;

if (frameSize == 50)
{
    //add start code
    memcpy(bufferWithStartCode, start_code, stCodeLen);
    shiftPtr += stCodeLen;
    memcpy(bufferWithStartCode + shiftPtr, fReceiveBuffer, frameSize);
    shiftPtr += frameSize;
}
else if (frameSize == 4)
{
    memcpy(bufferWithStartCode + shiftPtr, fReceiveBuffer, frameSize);
    shiftPtr += frameSize;
}
else
{
    if (shiftPtr == 0)
    {
        memcpy(bufferWithStartCode, start_code, stCodeLen);
        shiftPtr += stCodeLen;
    }
    memcpy(bufferWithStartCode + shiftPtr, fReceiveBuffer, frameSize);
    avpkt.size = frameSize + shiftPtr;
    avpkt.data = bufferWithStartCode;
    shiftPtr = 0;
    if (!avcodec_send_packet(cContext, &avpkt))
    {
        envir() << "error sending to decoder";

    }
    if (!avcodec_receive_frame(cContext, picture))
    {
        envir() << "error rx from decoder";
    }
    if (picture)
    {
        FILE *f;
        char buffer[32]; // The filename buffer.
        snprintf(buffer, sizeof(char) * 32, "file%i.txt", frame_num);
        f = fopen(buffer, "w");
        fprintf(f, "P5\n%d %d\n%d\n", fSubsession.videoWidth(), fSubsession.videoHeight(), 255);
        for (int i = 0;i < fSubsession.videoHeight();i++)
            fwrite(picture->data[0] + i * (picture->linesize[0]), 1, fSubsession.videoWidth(), f);
        fclose(f);
    }
}

envir() << frameSize << "\n";


frame_num++;

// Then continue, to request the next frame of data:
continuePlaying();

2 个答案:

答案 0 :(得分:4)

我找到了解决问题的方法。

在live555的“testRTSP”示例中有函数void DummySink::afterGettingFrame(...)。 我需要做的就是每当函数得到框架时组装我的框架:

[start_code sps pps start_code frame_data]

此时

frame_data为fReceiveBuffer。 start_code是char数组[0,0,0,1]。

将新数据推送到ffmpeg decoder:

m_packet.size = frameBufSize + frameSize; // size of assembled frame
m_packet.data = frameBuf; // assembled frame

if (avcodec_send_packet(m_decoderContext, &m_packet) != 0)
{
    envir() << "error in sending packet to decoder" << "\n";
}
if (avcodec_receive_frame(m_decoderContext, pFrame) == 0)

无需为decoderContext设置其他设置!只需在教程中初始化所有内容(http://dranger.com/ffmpeg/ - 这是ffmpeg c ++库的最新教程),你就可以了。 我使用memcpy来统一一个大数组中的数据。     memcpy(frameBuf,startCode,4);     frameBufSize + = 4;

for (int i = 0; i < numSPropRecords; i++)
{
    memcpy(frameBuf + frameBufSize, sPropRecords[i].sPropBytes, sPropRecords[i].sPropLength);
    frameBufSize += sPropRecords[i].sPropLength;
}

memcpy(frameBuf + frameBufSize, startCode, 4);
frameBufSize += 4;
memcpy(frameBuf + frameBufSize, fReceiveBuffer, frameSize);

m_packet.size = frameBufSize + frameSize;
m_packet.data = frameBuf;

您可以从subsession获取sps和pps数据(详细示例请查看“openRTSP”或“H264orH264FileSink.h”)

spsppsunits = subsession.fmtp_spropparametersets();
sPropRecords = parseSPropParameterSets(spsppsunits, numSPropRecords);

更新1:

过了一段时间后,我发现了ffmpeg方法的错误。如果你不提供sps和pps单位的extradata信息,有时ffmpeg解码器不能使用H264流。所以,

m_decoderContext->extradata =  (uint8_t*)av_malloc(100 + AV_INPUT_BUFFER_PADDING_SIZE);
int extraDataSize = 0;
for (int i = 0; i < numSPropRecords; i++)
{
    memcpy(m_decoderContext->extradata + extraDataSize, startCode, 4);
    extraDataSize += 4;
    memcpy(m_decoderContext->extradata + extraDataSize, sPropRecords[i].sPropBytes, sPropRecords[i].sPropLength);
    extraDataSize += sPropRecords[i].sPropLength;
}
m_decoderContext->extradata_size = extraDataSize;

之后,您不需要每次都为sps和pps数据提供帧,只需要启动代码。

答案 1 :(得分:0)

您的代码未显示您如何初始化编解码器,但SPS和PPS不应该包含在数据包中。相反,它们应该在初始化时通过AVCodecContextextradata字段传递给编解码器。然后你只会将实际帧NAL传递给解码器以获得解码图像。

我建议您在接收到第一个SPS时初始化解码器,或者根据DESCRIBE从SDP数据中初始化事件。