2010-11-16 29 views
3

Je récupère des images vidéo de la caméra via v4l, et j'ai besoin de les transcoder au format mpeg4 pour les diffuser successivement via RTP. Tout fonctionne réellement mais il y a quelque chose que je ne fais pas en recodant: le flux d'entrée produit 15fps, tandis que la sortie est à 25fps, et chaque trame d'entrée est convertie en une seule séquence d'objets vidéo (je l'ai vérifié avec une vérification simple sur le flux de bits de sortie). Je suppose que le récepteur analyse correctement le flux binaire mpeg4 mais la mise en paquets RTP est en quelque sorte fausse. Comment est-ce que je suis supposé diviser le bitstream encodé dans un ou plusieurs AVPacket? Peut-être que je manque l'évidence et j'ai juste besoin de chercher des marqueurs de cadre B/P, mais je pense que je n'utilise pas l'API d'encodage correctement.libavcodec, comment transcoder la vidéo avec différentes fréquences d'images?

Voici un extrait de mon code, qui est basé sur les échantillons de ffmpeg disponibles:

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

Répondre

0

solution la plus simple serait d'utiliser deux fils. Le premier thread ferait toutes les choses décrites dans votre question (décodage, conversion d'échelle/couleur-espace, codage). Les trames partiellement transcodées seront écrites dans la file d'attente intermédiaire partagée avec le second thread. La longueur maximale de cette file d'attente serait dans ce cas particulier (conversion de plus bas à plus haut débit) 1 trame. Deuxième fil serait en train de lire dans des cadres de la boucle de la file d'entrée comme ceci:

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

Lorsque la puissance du processeur est suffisante et plus la fidélité et la douceur est nécessaire, vous pouvez calculer l'image de sortie non seulement d'un cadre, mais plus d'images par une sorte de interpolation (similaire aux techniques utilisées dans les codecs mpeg). Plus l'horodatage du cadre de sortie est proche de l'horodatage du cadre d'entrée, plus vous devez attribuer de poids à ce cadre d'entrée particulier.