Domanda

Sto afferrando fotogrammi video dalla fotocamera tramite v4l, e ho bisogno di transcodificare loro in formato MPEG4 a loro successivamente lo streaming via RTP.

Tutto effettivamente "opere" ma c'è qualcosa che non mentre ricodifica: il flusso di ingresso produce 15 fps, mentre l'uscita è a 25fps, e ogni frame in ingresso viene convertito in una singola sequenza di oggetto video (i verificato questo con un semplice controllo sul flusso di bit di uscita). Credo che il ricevitore sia correttamente l'analisi del flusso di bit mpeg4 ma la pacchettizzazione RTP è in qualche modo sbagliato. Come faccio a dividere il flusso di bit codificato in uno o più AVPacket? Forse mi manca l'ovvio e ho solo bisogno di cercare indicatori di cornice B / P, ma penso che non sto usando correttamente l'API di codifica.

Ecco un estratto del mio codice, che si basa sui campioni ffmpeg disponibili:

// 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 );
    }
  }
}
È stato utile?

Soluzione

La soluzione più semplice sarebbe quella di utilizzare due thread. Primo thread sarebbe fare tutte le cose descritte nella tua domanda (decodifica, conversione in scala / spazio colore, codifica). Parzialmente telai transcodificati sarebbero scritti alla coda intermedia condiviso con il secondo filo. lunghezza massima di questa coda sarebbe in questo caso particolare (conversione da basso a alto bitrate) 1 fotogramma. Secondo thread sarebbe la lettura in cornici di loop da coda di input in questo modo:

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);
}

Quando la potenza della CPU è sufficiente e maggiore fedeltà e scorrevolezza è necessario si potrebbe calcolare cornice uscita non solo da un fotogramma ma più frame da una sorta di interpolazione (simile alle tecniche utilizzate nella codec MPEG). Quanto più telaio uscita timestamp al telaio ingresso timestamp, il peso è necessario assegnare a questo particolare telaio di ingresso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top