great code, worked perfectly on my app. :) just one thing, I'm moving all initializers globally as I don't need to change quality, but I need it to be really fast.
anyway, thanks alot :) Bogdan Coanda On Thu, Apr 2, 2009 at 7:29 PM, Clive Taylor <[email protected]> wrote: > Stas, > > An update (quite a long one I'm afraid). I changed my code so that all > of the JPEG writing stuff goes into its own function as below (once > again, the e-mail system has ruined all of my beautiful code > formatting): > > // > // Write JPEG > // > // Converts and writes the video frame in pFrame to a JPEG file > // > // Parameters: > // pCodexCtx Pointer to the video stream codec > context > // pFrame Pointer to Frame exracted from the video stream > // FrameNo FrameNumber > // Quality Image quality as % > // > // Returns: > // Size of buffer written or 0 on error > // > int > WriteJPEG ( AVCodecContext *pCodecCtx, AVFrame *pFrame, int FrameNo, int > Quality ) > { > AVCodecContext *pOCodecCtx; > AVCodec *pOCodec; > BYTE *Buffer; > int BufSiz; > int BufSizActual; > int ImgFmt = PIX_FMT_YUVJ420P; > FILE *JPEGFile; > char JPEGFName[256]; > > BufSiz = avpicture_get_size ( ImgFmt, > pCodecCtx->width, > pCodecCtx->height ); > // > // Allocate local RGB frame buffer > // > Buffer = (BYTE *)malloc ( BufSiz ); > if ( Buffer == NULL ) > return ( 0 ); > memset ( Buffer, 0, BufSiz ); > // > // Allocate the output codec context > // > pOCodecCtx = avcodec_alloc_context ( ); > if ( !pOCodecCtx ) { > free ( Buffer ); > return ( 0 ); > } > // > // Initialise format parameters > // > pOCodecCtx->bit_rate = pCodecCtx->bit_rate; > pOCodecCtx->width = pCodecCtx->width; > pOCodecCtx->height = pCodecCtx->height; > pOCodecCtx->pix_fmt = ImgFmt; > pOCodecCtx->codec_id = CODEC_ID_MJPEG; > pOCodecCtx->codec_type = CODEC_TYPE_VIDEO; > pOCodecCtx->time_base.num = pCodecCtx->time_base.num; > pOCodecCtx->time_base.den = pCodecCtx->time_base.den; > // > // Allocate the JPEG encoder > // > pOCodec = avcodec_find_encoder ( pOCodecCtx->codec_id ); > if ( !pOCodec ) { > free ( Buffer ); > return ( 0 ); > } > // > // Open the JPEG encoder > // > if ( avcodec_open ( pOCodecCtx, pOCodec ) < 0 ) { > free ( Buffer ); > return ( 0 ); > } > // > // Initialise the "VBR" settings > // > pOCodecCtx->qmin = pOCodecCtx->qmax = Quality; > pOCodecCtx->mb_lmin = pOCodecCtx->lmin = > pOCodecCtx->qmin * > FF_QP2LAMBDA; > pOCodecCtx->mb_lmax = pOCodecCtx->lmax = > pOCodecCtx->qmax * > FF_QP2LAMBDA; > pOCodecCtx->flags = CODEC_FLAG_QSCALE; > pOCodecCtx->global_quality = pOCodecCtx->qmin * FF_QP2LAMBDA; > // > // Set the timestamp and quality parameters > // > pFrame->pts = 1; > pFrame->quality = pOCodecCtx->global_quality; > BufSizActual = avcodec_encode_video ( pOCodecCtx, > Buffer, > BufSiz, > pFrame ); > // > // Write JPEG to file > // > sprintf ( JPEGFName, "f%06dq%02d.jpg", FrameNo, Quality ); > JPEGFile = fopen ( JPEGFName, "wb" ); > fwrite ( Buffer, 1, BufSizActual, JPEGFile ); > fclose ( JPEGFile ); > // > // Clean up and exit > // > avcodec_close ( pOCodecCtx ); > free ( Buffer ); > return ( BufSizActual ); > } > > I then tried invoking it with different values for the Quality > parameter, in three different ways: > > 1) > == > for ( QNo = 1; QNo < 100; QNo++ ) { > // > // Encode the frame as a JPEG image > // > Quality = (100 - QNo) * FF_LAMBDA_MAX / 100; > WriteJPEG ( pCodecCtx, pFrame, i, Quality ); > } > > 2) > == > for ( QNo = 1; QNo < 32; QNo++ ) { > // > // Encode the frame as a JPEG image > // > WriteJPEG ( pCodecCtx, pFrame, i, QNo ); > } > > 3) > == > for ( QNo = 1; QNo < 150; QNo++ ) { > // > // Encode the frame as a JPEG image > // > WriteJPEG ( pCodecCtx, pFrame, i, QNo ); > } > > 1) produced all 100 files at 11556 bytes and no differences between them > (using fc to compare the files). > > 2) produced files 31 files (f008000q01.jpg - f008000q31.jpg), but with > sizes in the range 113,112 bytes (QNo = 1) to 17,381 bytes (QNo = 31) > and very different visual results - the compression artefacts were > getting much more visible as the size decreased QNo increased (ah ha, > we're getting somewhere) > > 3) In an attempt to see what the maximum value for QNo would be to > continue to affect the output, I first tried using 1 - FF_LAMBDA_MAX > (32767), but got bored after about 10,000 files were produced. So I cut > the limit down to 150 and tried again. I found decreasing output file > sizes in the range 1-128, after which the file did not change in size. > As before with QNo = 1 I got a file of 113,112 bytes and with 128 a file > of 11556 bytes (above 128, all files were 11556 - even when I allowed it > to output 10,000 files with changing QNo). > > My conclusion is that setting qmin and qmax to a value between 1 and 128 > gives us the variable quality, that the FF_LAMBDA_MAX value is a red > herring, that to scale the quality so it's expressed as a percentage > we'd use (100 - Qp) * 128 / 100. > > It is necessary to close the codec when the required quality changes (or > as VOid pointed out when the height and/or width changes, too), and to > encode more than one frame at the same quality it is necessary to change > the pts value - whether it's just a simple increment I'm not sure. > That's how I changed it in previous experiments, and it seems reasonable > as the encoder would expect sequentially numbered frames. > > So I will now finish off my frame extraction program, allowing the > customer to specify any number of frames from any number of video files > at any given quality and any given width/height. Luckily I've written it > as a scripting language for him, so all I need to do is collect all of > the same input file, quality and dimensions together and process those > in one hit, close the codec and so on. > > Clive > > > -----Original Message----- > From: [email protected] > [mailto:[email protected]] On Behalf Of Stas Oskin > Sent: 02 April 2009 16:01 > To: Libav* user questions and discussions > Subject: Re: [libav-user] Writing single frames to JPEG > > 2009/4/2 Clive Taylor <[email protected]> > > > Oh yes. I also tried it with values in the range 1 & 31 and no > > difference. > > > > Clive > > > > Ahh, so Lambda really needs to be used it seems? > _______________________________________________ > libav-user mailing list > [email protected] > https://lists.mplayerhq.hu/mailman/listinfo/libav-user > > > _______________________________________________ > libav-user mailing list > [email protected] > https://lists.mplayerhq.hu/mailman/listinfo/libav-user > -- Bogdan COANDA AIESEC Iasi AIESEC in Romania Mobile: +40-724 013 068 E-mail: [email protected] Website: www.aieseciasi.ro _____________________________________________________________________________ AIESEC - The international platform for young people to discover and develop their potential _______________________________________________ libav-user mailing list [email protected] https://lists.mplayerhq.hu/mailman/listinfo/libav-user
