Frage

Ich bin Video-Frames von der Kamera über v4l greifen, und ich brauche sie in MPEG-4 Format umgewandelt werden, um sie nacheinander über RTP zu übertragen.

Alles eigentlich „funktioniert“, aber es gibt etwas, das ich nicht während Re-Encoding: der Eingangsstrom erzeugt 15fps, während der Ausgang bei 25 Bildern pro Sekunde ist, und jeder Eingaberahmen in einer einzigen Videoobjektsequenz umgewandelt (i dies verifiziert mit eine einfache Prüfung auf dem Ausgabe Bitstrom). Ich denke, dass der Empfänger richtig mpeg4 Bitstrom Parsen aber die RTP-Paketierung ist irgendwie falsch. Wie soll ich den codierten Bitstrom in einem oder mehreren AVPacket aufgeteilt? Ich bin die offensichtlich fehlt, und ich brauche nur zu suchen B / P-Frame-Marker Vielleicht, aber ich glaube, ich bin die Encodierung API nicht richtig verwendet wird.

Hier ist ein Auszug aus meinem Code, der auf den zur Verfügung stehenden ffmpeg Proben basieren:

// input frame
AVFrame *picture;
// input frame color-space converted
AVFrame *planar;
// input format context, video4linux2
AVFormatContext *iFmtCtx;
// output codec context, mpeg4
AVCodecContext *oCtx;
// [ init everything ]
// ...
oCtx->time_base.num = 1;
oCtx->time_base.den = 25;
oCtx->gop_size = 10;
oCtx->max_b_frames = 1;
oCtx->bit_rate = 384000;
oCtx->pix_fmt = PIX_FMT_YUV420P;

for(;;)
{
  // read frame
  rdRes = av_read_frame( iFmtCtx, &pkt );
  if ( rdRes >= 0 && pkt.size > 0 )
  {
    // decode it
    iCdcCtx->reordered_opaque = pkt.pts;
    int decodeRes = avcodec_decode_video2( iCdcCtx, picture, &gotPicture, &pkt );
    if ( decodeRes >= 0 && gotPicture )
    {
      // scale / convert color space
      avpicture_fill((AVPicture *)planar, planarBuf.get(), oCtx->pix_fmt, oCtx->width, oCtx->height);
      sws_scale(sws, picture->data, picture->linesize, 0, iCdcCtx->height, planar->data, planar->linesize);
      // encode
      ByteArray encBuf( 65536 );
      int encSize = avcodec_encode_video( oCtx, encBuf.get(), encBuf.size(), planar );
      // this happens every GOP end
      while( encSize == 0 )
        encSize = avcodec_encode_video( oCtx, encBuf.get(), encBuf.size(), 0 );
      // send the transcoded bitstream with the result PTS
      if ( encSize > 0 )
        enqueueFrame( oCtx->coded_frame->pts, encBuf.get(), encSize );
    }
  }
}
War es hilfreich?

Lösung

Die einfache Lösung wäre, zwei Threads zu verwenden. Erster Thread würde alle die Dinge tun, in Ihrer Frage skizzieren (Decodierung, Skalierung / Farbraumkonvertierung, Codierung). Partiell umcodiert Frames würde Zwischenwarteschlange mit zweitem Thread geteilt geschrieben werden. Die maximale Länge dieser Warteschlange würde in diesem speziellen Fall (von niedrigeren zu höheren Bitrate Umwandlung) 1-Rahmen. Zweiter Thread von der Eingangswarteschlange, wie dies in Schleifenleserahmen wäre:

void FpsConverter::ThreadProc()
{

timeBeginPeriod(1);
DWORD start_time = timeGetTime();
int frame_counter = 0;
while(!shouldFinish()) {
    Frame *frame = NULL;
    DWORD time_begin = timeGetTime();
    ReadInputFrame(frame);
    WriteToOutputQueue(frame);
    DWORD time_end = timeGetTime();
    DWORD next_frame_time = start_time + ++frame_counter * frame_time;
    DWORD time_to_sleep = next_frame_time - time_end;
    if (time_to_sleep > 0) {
        Sleep(time_to_sleep);
    }
}
timeEndPeriod(1);
}

Wenn CPU-Leistung ist ausreichend und höhere Genauigkeit und Glätte erforderlich Sie Ausgangsrahmen berechnen können nicht nur von einem Rahmen, sondern mehr Frames durch irgendeine Art von Interpolation (ähnlich Techniken in MPEG-Codecs verwendet). Je näher Ausgangsrahmenzeitstempel Eingangsrahmenzeitstempel, die mehr Gewicht Sie mit diesem Eingaberahmen zuweisen soll.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top