2016-07-25 178 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處理呢?那麼你不必爲此煩惱,一切都將在內部完成。 –