2017-03-06 3 views
1

Kontext:
Ich baue einen Recorder für die Aufnahme von Video und Audio in separaten Threads (mit Boost Thread-Gruppen) mit FFmpeg 2.8.6 auf Ubuntu 16.04. Ich folgte dem demuxing_decoding Beispiel hier: https://www.ffmpeg.org/doxygen/2.8/demuxing_decoding_8c-example.htmlC++/C FFmpeg Artefakt Aufbau über Video Frames

Video-Capture-Besonderheiten:
I H264 aus einer Logitech C920 Webcam am Lesen und Schreiben das Video auf eine RAW-Datei. Das Problem, auf das ich bei dem Video stoße, ist, dass anscheinend Artefakte über Bilder hinweg aufgebaut werden, bis ein bestimmter Frame zurückgesetzt wird. Hier ist mein Frame Grabbing und Decodierungsfunktionen:

// Used for injecting decoding functions for different media types, allowing 
// for a generic decode loop 
typedef std::function<int(AVPacket*, int*, int)> PacketDecoder; 

/** 
* Decodes a video packet. 
* If the decoding operation is successful, returns the number of bytes decoded, 
* else returns the result of the decoding process from ffmpeg 
*/ 
int decode_video_packet(AVPacket *packet, 
         int *got_frame, 
         int cached){ 
    int ret = 0; 
    int decoded = packet->size; 

    *got_frame = 0; 

    //Decode video frame 
    ret = avcodec_decode_video2(video_decode_context, 
           video_frame, got_frame, packet); 
    if (ret < 0) { 
     //FFmpeg users should use av_err2str 
     char errbuf[128]; 
     av_strerror(ret, errbuf, sizeof(errbuf)); 
     std::cerr << "Error decoding video frame " << errbuf << std::endl; 
     decoded = ret; 
    } else { 
     if (*got_frame) { 
      video_frame->pts = av_frame_get_best_effort_timestamp(video_frame); 

      //Write to log file 
      AVRational *time_base = &video_decode_context->time_base; 
      log_frame(video_frame, time_base, 
         video_frame->coded_picture_number, video_log_stream); 

#if(DEBUG) 
      std::cout << "Video frame " << (cached ? "(cached)" : "") 
         << " coded:" << video_frame->coded_picture_number 
         << " pts:" << pts << std::endl; 
#endif 

      /*Copy decoded frame to destination buffer: 
      *This is required since rawvideo expects non aligned data*/ 
      av_image_copy(video_dest_attr.video_destination_data, 
          video_dest_attr.video_destination_linesize, 
          (const uint8_t **)(video_frame->data), 
          video_frame->linesize, 
          video_decode_context->pix_fmt, 
          video_decode_context->width, 
          video_decode_context->height); 

      //Write to rawvideo file 
      fwrite(video_dest_attr.video_destination_data[0], 
        1, 
        video_dest_attr.video_destination_bufsize, 
        video_out_file); 

      //Unref the refcounted frame 
      av_frame_unref(video_frame); 
     } 
    } 

    return decoded; 
} 

/** 
* Grabs frames in a loop and decodes them using the specified decoding function 
*/ 
int process_frames(AVFormatContext *context, 
        PacketDecoder packet_decoder) { 
    int ret = 0; 
    int got_frame; 
    AVPacket packet; 

    //Initialize packet, set data to NULL, let the demuxer fill it 
    av_init_packet(&packet); 
    packet.data = NULL; 
    packet.size = 0; 

    // read frames from the file 
    for (;;) { 
     ret = av_read_frame(context, &packet); 
     if (ret < 0) { 
      if (ret == AVERROR(EAGAIN)) { 
       continue; 
      } else { 
       break; 
      } 
     } 

     //Convert timing fields to the decoder timebase 
     unsigned int stream_index = packet.stream_index; 
     av_packet_rescale_ts(&packet, 
          context->streams[stream_index]->time_base, 
          context->streams[stream_index]->codec->time_base); 

     AVPacket orig_packet = packet; 
     do { 
      ret = packet_decoder(&packet, &got_frame, 0); 
      if (ret < 0) { 
       break; 
      } 
      packet.data += ret; 
      packet.size -= ret; 
     } while (packet.size > 0); 
     av_free_packet(&orig_packet); 

     if(stop_recording == true) { 
      break; 
     } 
    } 

    //Flush cached frames 
    std::cout << "Flushing frames" << std::endl; 
    packet.data = NULL; 
    packet.size = 0; 
    do { 
     packet_decoder(&packet, &got_frame, 1); 
    } while (got_frame); 

    av_log(0, AV_LOG_INFO, "Done processing frames\n"); 
    return ret; 
} 


Fragen:

  1. Wie gehe ich über das zugrunde liegende Problem Debuggen?
  2. Ist es möglich, dass das Ausführen des Decodierungscodes in einem anderen Thread als dem, in dem der Decodierungskontext geöffnet wurde, das Problem verursacht?
  3. Mache ich etwas falsch im Decodierungscode?

Dinge, die ich habe versucht/gefunden:

  1. Ich fand diesen Thread, der über das gleiche Problem ist hier: FFMPEG decoding artifacts between keyframes (Ich kann nicht schreiben Proben meiner beschädigten Rahmen wegen Fragen der Privatsphäre, sondern Das mit dieser Frage verknüpfte Bild zeigt das gleiche Problem, das ich habe. Die Antwort auf die Frage wird jedoch vom OP ohne spezifische Details darüber, wie das Problem behoben wurde, gepostet. Das OP erwähnt nur, dass er die Pakete nicht "richtig aufbewahrte", aber nichts darüber, was falsch war oder wie es zu beheben war. Ich habe nicht genug Reputation, um einen Kommentar zur Klärung zu schreiben.

  2. Zuerst übergab ich das Paket per Wert in die Entschlüsselungsfunktion, aber wechselte zum Zeiger mit der Möglichkeit, dass die Paketfreigabe falsch ausgeführt wurde.

  3. fand ich eine andere Frage zu Decodierung Probleme debuggen, konnte aber nichts schlüssig finden: How is video decoding corruption debugged?

ich keine Einsicht würde schätzen. Danke vielmals!

[EDIT] Als Reaktion auf Ronald Antwort, ich bin eine wenig mehr Informationen hinzufügen, die keinen Kommentar passen würden:

  1. ich nur decode_video_packet Aufruf bin() aus dem Thread-Verarbeitung Video-Frames; Der andere Thread, der Audioframes verarbeitet, ruft eine ähnliche Funktion decode_audio_packet() auf. Also ruft nur ein Thread die Funktion auf. Ich sollte erwähnen, dass ich den thread_count im Dekodierungskontext auf 1 gesetzt habe, wobei ich andernfalls einen segfault in malloc.c beim Leeren der zwischengespeicherten Frames erhalten würde.

  2. Ich kann sehen, dass dies ein Problem ist, wenn die Process_frames und die Frame-Decoder-Funktion auf separaten Threads ausgeführt wurden, was nicht der Fall ist. Gibt es einen bestimmten Grund, warum es wichtig wäre, ob die Freigabe innerhalb der Funktion erfolgt oder nach der Rückkehr?Ich glaube, dass der Freigabefunktion eine Kopie des ursprünglichen Pakets übergeben wurde, da mehrere Dekodierungsaufrufe für das Audiopaket erforderlich wären, falls der Dekodierer das gesamte Audiopaket nicht decodieren würde.

  3. Ein generelles Problem ist, dass die Korruption nicht die ganze Zeit auftritt. Ich kann besser debuggen, wenn es deterministisch ist. Ansonsten kann ich nicht einmal sagen, ob eine Lösung funktioniert oder nicht.

Antwort

2

Ein paar Dinge zu überprüfen:

  • werden Sie mehrere Threads ausgeführt wird, die decode_video_packet() fordern? Wenn du bist: tu das nicht! FFmpeg verfügt über integrierte Unterstützung für die Multithread-Dekodierung, und Sie sollten FFmpeg intern und transparent Threading ausführen lassen.
  • Sie rufen av_free_packet() direkt nach dem Aufruf der Rahmendecoder-Funktion, aber zu diesem Zeitpunkt hatte es noch keine Chance, den Inhalt zu kopieren. Sie sollten wahrscheinlich decode_video_packet() freigeben das Paket stattdessen, nach dem Aufruf avcodec_decode_video2().

Allgemeine Debug-Beratung:

  • laufen sie ohne Einfädeln und sehen, ob das funktioniert;
  • Wenn dies der Fall ist und das Threading fehlschlägt, verwenden Sie Thread-Debugger wie z. B. tsan oder helgrind, um bei der Suche nach Rennbedingungen zu helfen, die auf Ihren Code verweisen.
  • es kann auch helfen zu wissen, ob die Ausgabe, die Sie bekommen, reproduzierbar ist (dies deutet auf einen nicht-threading-bezogenen Fehler in Ihrem Code hin) oder ändert sich von einem Lauf zum anderen (dies deutet auf eine Race-Bedingung in Ihrem Code hin) .

Und ja, die regelmäßigen Bereinigungen sind wegen Keyframes.

+0

Vielen Dank für Ihre Antwort! Ich habe die Frage mit zusätzlichen Informationen zu Ihrem Kommentar aktualisiert. Es läuft auch manchmal mit dem Threading ohne Korruption, also ist es schwieriger zu debuggen. In der Zwischenzeit werde ich die Thread-Debugger trotzdem verwenden und melden. Danke noch einmal! – ChiragRaman

+0

Als Antwort auf Ihre Frage 2: Wenn decode_video_packet() und process_frame() in separaten Threads ausgeführt werden, ist das Paket möglicherweise bereits im Hauptthread frei(), während der Decodierungsthread immer noch herumtanzt und andere Dinge tut. Durch das Verschieben von packet_free in den Dekodierungsthread wird dieses Problem gelöst, da ein Paket niemals frei() ed wird, bis avcodec_decode_video2() aufgerufen wird. –