2016-07-25 5 views
1

J'essaie de créer une petite application qui permettra d'enregistrer des images à partir du flux h264 inoming. J'ai pris un programme testRTSP comme exemple et fait plusieurs changements dans la fonction DummySink::afterGettingFrame pour décoder les trames à l'aide de la bibliothèque ffmpeg. D'après ce que j'ai compris de frameSize, mes deux premières images sont des unités SPS, donc je les concatène avec ma troisième image et j'envoie une nouvelle image au décodeur ffmpeg. Mais ça ne marche pas. ffmpeg me dit que ma première image est trop grande pour SPS et ensuite elle me dit qu'il n'y a pas de frame ... Je ne sais pas ce que j'ai besoin de changer ici.LIVE555 comment utiliser h264 framer class pour obtenir des unités nal pour 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(); 

Répondre

2

J'ai trouvé une solution à mon problème.

Il existe la fonction void DummySink::afterGettingFrame(...) Dans l'exemple "testRTSP" de live555. Tout ce que je dois faire est de montage mon cadre à chaque fois lorsque la fonction a cadre:

[start_code sps pps start_code frame_data]

frame_data est fReceiveBuffer à ce stade. start_code est char array [0,0,0,1].

et pousser de nouvelles données au décodeur 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) 

Pas des paramètres supplémentaires pour decoderContext! Juste init tout comme dans les tutoriels (http://dranger.com/ffmpeg/ - ce sont des tutoriels à jour pour la bibliothèque ffmpeg C++) et vous êtes prêt à partir. J'ai utilisé memcpy pour réunir des données dans un grand tableau. 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; 

Vous pouvez obtenir des données sanitaires et phytosanitaires et pps de SubSession (cochez la case "openRTSP" par exemple détaillé ou "H264orH264FileSink.h")

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

MISE À JOUR 1:

Après un certain temps, j'ai trouvé erreurs dans mon approche de ffmpeg. Parfois, le décodeur ffmpeg ne fonctionnera pas avec le flux H264 si vous ne fournissez pas d'informations d'extradata avec les unités sps et pps. Ainsi,

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; 

Après cela, vous ne avez pas besoin de fournir des données de cadre et pps chaque sps fois, commencez uniquement le code est nécessaire.

0

Votre code ne montre pas comment vous initialisez le codec mais le SPS et le PPS ne doivent pas aller dans le paquet. Au lieu de cela, ils doivent être transmis au codec via le champ extradata de AVCodecContext lors de l'initialisation. Et alors vous ne passeriez que les NAL de l'image réelle au décodeur pour obtenir des images décodées.

Id vous suggérons d'initialiser le décodeur lors de la réception du premier SPS ou d'un événement à partir des données SDP en réponse au DESCRIBE.

+0

Les données SPS et PPS ont-elles une taille de 50 et 4 octets? Peut-être que ce sont mes deux premiers "cadres"? Et comment pourrais-je référencer deux variables différentes dans 'extradata'? – Aleksey

+0

La taille de sps et pps n'est pas fixe, comme mentionné dans la réponse supprimée vous ne pouvez pas faire de suppositions basées sur la taille - vous devez vérifier le type nal réel (les cinq bits les plus à droite du premier octet).En ce qui concerne le 'extradata' - si ma mémoire est correcte, il suffit de les passer dans un seul tampon divisé par le code de préfixe que vous utilisez déjà et de définir le' extradata_size' en conséquence. Btw, si vous voulez que ffmpeg décode le flux, pourquoi ne pas utiliser aussi ffmpeg (avio_open) pour la gestion RTSP? Ensuite, vous n'auriez pas à vous embêter avec cela et tout se ferait en interne. –