2016-07-25 49 views
1

我想创建一个小应用程序,它将保存来自homing的h264流的帧。 我以一个testRTSP程序为例,在DummySink::afterGettingFrame函数中做了几处修改,在ffmpeg库的帮助下对帧进行解码。 正如我从frameSize了解到的,我的前两帧是SPS单元,所以我将它们与我的第三帧连接起来,然后将新的大帧发送到ffmpeg解码器。但那不起作用。 ffmpeg告诉我,我的第一帧对于SPS来说太大了,然后它告诉我没有帧......我不知道我需要在这里更改什么。LIVE555如何使用h264成帧器类为ffmpeg获得最终单元

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

我发现我的问题的解决方案。

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

[start_code sps pps start_code frame_data]

frame_data是fReceiveBuffer在这一点上。 start_code是char数组[0,0,0,1]。

和新数据推到的ffmpeg解码器:

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; 

您可以从子会话获得SPS和PPS的数据(检查 “openRTSP” 为详细示例或 “H264orH264FileSink.h”)

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

更新1:

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

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数据帧,只需要启动代码。

0

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

建议您在接收到第一个SPS或来自SDP数据的事件时响应DESCRIBE初始化解码器。

+0

SPS和PPS数据是50和4字节大小?也许这是我的前两个“框架”?我怎么能在'extradata'中引用两个不同的变量? – Aleksey

+0

sps和pps的大小不是固定的,因此在删除的答案中提到您不能根据大小做出任何假设 - 您需要检查实际的nal类型(NAL单元中第一个字节的最右边五位)。至于'extradata' - 如果我的记忆是正确的,你只需将它们传入一个单独的缓冲区,除以你已经使用的前缀代码,并相应地设置'extradata_size'。顺便说一句,如果你想ffmpeg解码流,为什么不使用ffmpeg(avio_open)进行RTSP处理呢?那么你不必为此烦恼,一切都将在内部完成。 –