2016-07-25 26 views
1

Ich versuche, eine kleine App zu erstellen, die Frames von Inoming h264 Stream speichern wird. Ich nahm ein TestRTSP-Programm als Beispiel und machte mehrere Änderungen in DummySink::afterGettingFrame Funktion, um Frames mit Hilfe von ffmpeg Bibliothek zu decodieren. Wie ich von frameSize verstehe, sind meine ersten beiden Frames SPS-Einheiten, so dass ich sie mit meinem dritten Frame verkette und dann einen neuen großen Frame an den ffmpeg-Decoder sende. Aber das funktioniert nicht. ffmpeg sagt mir, dass mein erster Frame zu groß für SPS ist und dann sagt er mir, dass es keinen Frame gibt ... Ich weiß nicht, was ich hier ändern muss.LIVE555 wie h264 Framer-Klasse zu verwenden, um nal Einheiten für 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(); 

Antwort

2

Ich habe eine Lösung für mein Problem gefunden.

Es gibt die Funktion void DummySink::afterGettingFrame(...) In "testRTSP" Beispiel von live555. Alles was ich brauche wurde, ist zu tun, um mein Rahmen immer auf Montag, wenn die Funktion Rahmen bekam:

[start_code sps pps start_code frame_data]

frame_data ist fReceiveBuffer an dieser Stelle. Start_code ist Char-Array [0,0,0,1].

und neue Daten zu ffmpeg Decoder drücken:

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) 

keine weiteren Einstellungen für decoderContext! Just init alles wie in Tutorials (http://dranger.com/ffmpeg/ - das ist aktuelle Tutorials für ffmpeg C++ Bibliothek) und Sie gut zu gehen. Ich verwendete memcpy, um Daten in einem großen Array zu vereinen. 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; 

Sie können sps und pps Daten von subsession (Check "openRTSP" für ausführliches Beispiel oder "H264orH264FileSink.h")

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

UPDATE 1 erhalten:

Nach einiger Zeit fand ich Fehler in meinem Ansatz zu ffmpeg. Manchmal funktioniert der ffmpeg-Decoder nicht mit dem H264-Stream, wenn Sie keine extradata-Informationen mit sps und pps-Einheiten angeben. So

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; 

Danach brauchen Sie nicht zu liefern Frame mit sps und pps Daten jedes Mal, nur Startcode benötigt wird.

0

Ihr Code zeigt nicht, wie Sie den Codec initialisieren, aber die SPS und PPS sollten nicht in das Paket gehen. Stattdessen sollten sie bei der Initialisierung über das Feld extradata von AVCodecContext an den Codec übergeben werden. Und dann würden Sie nur die tatsächlichen Rahmen NALs an den Decoder übergeben, um decodierte Bilder zu erhalten.

Id vorschlagen, dass Sie entweder den Decoder beim Empfang des ersten SPS oder Ereignisses von den SDP-Daten in Reaktion auf die DESCRIBE initialisieren.

+0

Sind SPS- und PPS-Daten 50 und 4 Byte groß? Vielleicht sind das meine ersten beiden "Frames"? Und wie könnte ich zwei verschiedene Variablen in "Extradata" referenzieren? – Aleksey

+0

Die Größe von sps und pps ist nicht so festgelegt, wie in der gelöschten Antwort erwähnt, Sie können keine Annahmen basierend auf der Größe machen - Sie müssen den tatsächlichen nal-Typ überprüfen (die fünf rechten Bits des ersten Bytes in der NAL-Einheit).Wie für die "Extradata" - wenn mein Speicher korrekt ist, übergeben Sie sie einfach in einem einzigen Puffer geteilt durch den Präfix-Code, den Sie bereits verwenden, und setzen Sie die 'extradata_size' entsprechend. Übrigens, wenn Sie möchten, dass ffmpeg den Stream entschlüsselt, warum nicht auch ffmpeg (avio_open) für die RTSP-Behandlung verwenden? Dann hättest du dich nicht darum kümmern müssen und alles würde intern erledigt werden. –