วันพฤหัสบดีที่ 28 เมษายน พ.ศ. 2554

Writing "C" code to encode and decode H.264 by using libavcodec

This example implement base on api-example source code. It will show you how to write "C" code to encode raw data (qcif) to h264 and decoded it to yuv.

Part of the main code is shown below.

int main(int argc, char **argv)
{

/* must be called before using avcodec lib */
avcodec_init();

/* register all the codecs */
avcodec_register_all();

h264_encode_decode("Foreman.qcif","Decoded.yuv");

return 0;
}


We start with registering and initialize codec. And then we call function h264_encode_decode(), this function will encode input file ("Foreman.qcif") to h.264 and then decode it to yuv file ("Decoded.yuv").

I will divide h264_encode_decode function into 5 steps.

1. Declare variable that use in decode and encode.

AVCodec *codecEncode, *codecDecode;
AVCodecContext *ctxEncode= NULL, *ctxDecode = NULL;

FILE *fin, *fout;
AVFrame *pictureEncoded, *pictureDecoded;

uint8_t *encoderOut, *picEncodeBuf;
int encoderOutSize, decoderOutSize;
int pic_size;

AVPacket avpkt;
int got_picture, len;

const int clip_width = 176;
const int clip_height = 144;

int frame = 0;
uint8_t *decodedOut;



2. Initial codec/picture structure for decoder.

codecDecode = avcodec_find_decoder(CODEC_ID_H264);
if (!codecDecode) {
fprintf(stderr, "codec not found\n");
exit(1);
}

ctxDecode= avcodec_alloc_context();
avcodec_get_context_defaults(ctxDecode);
ctxDecode->flags2 |= CODEC_FLAG2_FAST;
ctxDecode->pix_fmt = PIX_FMT_YUV420P;
ctxDecode->width = clip_width;
ctxDecode->height = clip_height;
ctxDecode->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);

if (avcodec_open(ctxDecode, codecDecode) < 0) {
fprintf(stderr, "could not open codec\n");
exit(1);
}

pictureDecoded= avcodec_alloc_frame();
avcodec_get_frame_defaults(pictureDecoded);
pic_size = avpicture_get_size(PIX_FMT_YUV420P, clip_width, clip_height);

decodedOut = (uint8_t *)malloc(pic_size);
fout = fopen(fileout, "wb");
if (!fout) {
fprintf(stderr, "could not open %s\n", fileout);
exit(1);
}



3. Initial codec/picture for encoder.

codecEncode = avcodec_find_encoder(CODEC_ID_H264);
if (!codecEncode) {
printf("codec not found\n");
exit(1);
}

ctxEncode= avcodec_alloc_context();
ctxEncode->coder_type = 0; // coder = 1
ctxEncode->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
ctxEncode->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
ctxEncode->partitions|=X264_PART_I8X8+X264_PART_I4X4+X264_PART_P8X8+X264_PART_B8X8; // partitions=+parti8x8+parti4x4+partp8x8+partb8x8
ctxEncode->me_method=ME_HEX; // me_method=hex
ctxEncode->me_subpel_quality = 0; // subq=7
ctxEncode->me_range = 16; // me_range=16
ctxEncode->gop_size = 30*3; // g=250
ctxEncode->keyint_min = 30; // keyint_min=25
ctxEncode->scenechange_threshold = 40; // sc_threshold=40
ctxEncode->i_quant_factor = 0.71; // i_qfactor=0.71
ctxEncode->b_frame_strategy = 1; // b_strategy=1
ctxEncode->qcompress = 0.6; // qcomp=0.6
ctxEncode->qmin = 0; // qmin=10
ctxEncode->qmax = 69; // qmax=51
ctxEncode->max_qdiff = 4; // qdiff=4
ctxEncode->max_b_frames = 3; // bf=3
ctxEncode->refs = 3; // refs=3
ctxEncode->directpred = 1; // directpred=1
ctxEncode->trellis = 1; // trellis=1
ctxEncode->flags2|=CODEC_FLAG2_FASTPSKIP; // flags2=+bpyramid+mixed_refs+wpred+dct8x8+fastpskip
ctxEncode->weighted_p_pred = 0; // wpredp=2
ctxEncode->bit_rate = 32000;
ctxEncode->width = clip_width;
ctxEncode->height = clip_height;
ctxEncode->time_base.num = 1;
ctxEncode->time_base.den = 30;
ctxEncode->pix_fmt = PIX_FMT_YUV420P;
ctxEncode->dsp_mask = (FF_MM_MMX | FF_MM_MMXEXT | FF_MM_SSE);
ctxEncode->rc_lookahead = 0;
ctxEncode->max_b_frames = 0;
ctxEncode->b_frame_strategy =1;
ctxEncode->chromaoffset = 0;
ctxEncode->thread_count =1;
ctxEncode->bit_rate = (int)(128000.f * 0.80f);
ctxEncode->bit_rate_tolerance = (int) (128000.f * 0.20f);
ctxEncode->gop_size = 30*3; // Each 3 seconds

/* open codec for encoder*/
if (avcodec_open(ctxEncode, codecEncode) < 0) {
printf("could not open codec\n");
exit(1);
}

//open file to read
fin = fopen(filein, "rb");
if (!fin) {
printf("could not open %s\n", filein);
exit(1);
}

/* alloc image and output buffer for encoder*/
pictureEncoded= avcodec_alloc_frame();
avcodec_get_frame_defaults(pictureEncoded);

//encoderOutSize = 100000;
encoderOut = (uint8_t *)malloc(100000);
//int size = ctxEncode->width * ctxEncode->height;
picEncodeBuf = (uint8_t *)malloc(3*pic_size/2); /* size for YUV 420 */
pictureEncoded->data[0] = picEncodeBuf;
pictureEncoded->data[1] = pictureEncoded->data[0] + pic_size;
pictureEncoded->data[2] = pictureEncoded->data[1] + pic_size / 4;
pictureEncoded->linesize[0] = ctxEncode->width;
pictureEncoded->linesize[1] = ctxEncode->width / 2;
pictureEncoded->linesize[2] = ctxEncode->width / 2;


4. Read data from input file and encode it by using avcodec_encode_video, encoded data will send to decode to yuv format by using avcodec_decode_video2. Decoded data will be written to decoded.yuv file.

//encode and decode loop
for(int i=0;i<30;i++)
{
fflush(stdout);
//read qcif 1 frame to buufer
fread(pictureEncoded->data[0],ctxEncode->width * ctxEncode->height, 1, fin);
fread(pictureEncoded->data[1],ctxEncode->width * ctxEncode->height/4, 1, fin);
fread(pictureEncoded->data[2],ctxEncode->width * ctxEncode->height/4, 1, fin);
pictureEncoded->pts = AV_NOPTS_VALUE;

/* encode frame */
encoderOutSize = avcodec_encode_video(ctxEncode, encoderOut, 100000, pictureEncoded);
printf("encoding frame %3d (size=%5d)\n", i, encoderOutSize);
if(encoderOutSize <= 0)
continue;

//send encoderOut to decoder
avpkt.size = encoderOutSize;
avpkt.data = encoderOut;
//decode frame
len = avcodec_decode_video2(ctxDecode, pictureDecoded, &got_picture, &avpkt);
if (len < 0) {
printf("Error while decoding frame %d\n", frame);
exit(1);
}
if (got_picture) {
printf("len = %d saving frame %3d\n", len, frame);
fflush(stdout);

avpicture_layout((AVPicture *)pictureDecoded, ctxDecode->pix_fmt
, clip_width, clip_height, decodedOut, pic_size);
fwrite(decodedOut, pic_size, 1, fout);
frame++;
}
}


5. Free allocated memory and close file pointer.

fclose(fout);
fclose(fin);

avcodec_close(ctxEncode);
avcodec_close(ctxDecode);
av_free(ctxEncode);
av_free(ctxDecode);
av_free(pictureEncoded);
av_free(pictureDecoded);


This implementation will show you how to use libavcodec to encode and decode h264 correctly. hope it will help you to understand encode/decode scenario when using libavcodec.

:)

Compile/build api-example of libavcodec on window with visual c++ 2008.

This post will show, how to compile and build api-exampl.c with visual c++ 2008.

api-example.c is contained in libavcodec folder, this file show you how to use api of libavcodec.
If you can compile it on window you will get idea to use it on your window application.

An example assume, you already have dll, lib and necessary include file on your machine.

1.Create VS C++ with console application project and add api-example.cpp (change file extension form .c to .cpp) to your project.

2.At this step if you compile you will get error message about cannot open include file. you need to set include files path of your visual studio.



- D:\TSL_Project\include; contain inttypes.h and stdint.h these files aren't in VS2008.
- C:\msys\1.0\local\include; contain libavcodec and other necessary include files.

3.Modify your api-example code by
- put extern "C" keywords between include header file of ffmpeg because it come form C coding style
- define "__STDC_CONSTANT_MACROS" this will remove error about "error C3861: 'INT64_C': identifier not found"



4.At this step if you compile code you will get 2 types of error first is cannot convert pointer and second is "snprintf': identifier not found". You need to fix it by.

- For pointer type you can convert it manually by yourself.
- For snprintf you need to use _snprintf instead.

5.At this step you can compile without error, BUT when you build project you will get a error like "unresolved external symbol" , because you still not set linker to your project.

- Add lib directory to VS2008



- Add lib to linker input



6.After add linker you can completely build your project BUT!!! it still not complete process to run your application,

You can build your project without error but you cannot launch your application because you don't have ".dll" in your directory so let's copy it all and paste to your directory and it will work :) .

Simple software for playing/encoding qcif and yuv.

Last post I talk about yuv, qcif player.

So I wanna show my software that I wrote it longtime ago. This software use for playing and encoding h.264. Encoder of this software is very old JM h.264 version.

This software develop by c++builder6 the easy platform to make GUI by C++ language.

The main window after open qcif file is look like this.
















You can play sequence in sequence player of open more clip.
And then if you want to encode it you can set encode parameter by using h.264 option like this.

















And then you can encode by select File->start encoding . Software will encode your sequence to h.264 format. And save it to .264 format and .yuv that already reconstruct.

















This software is not good design and coding but if you wanna demonstrate encode/decode h.264 and wanna see input/output of sequence with simple way you can use it.

p.s. you can email to me if wanna try this software :) .

How to encode and decode h.264 by using ffmpeg.

Today I take time to demonstrate encode and decode process with ffmpeg/x264.

first I will show about how to encode h.264 by ffmpeg and then I will show decode.
This demonstration work on windows7 32bits.

Encode h.264
1.We need to prepare environment like this.
- ffmpeg.exe and its dll.
- libx264
* You can get ffmpeg and libx264 library by download from website or you can get its source code and make install to msys. I will talk about how to build ffmpeg on window on the next post.

2.go to directory that contain your ffmpeg lib and type this command on your console.

ffmpeg.exe -s qcif -i Foreman.qcif -r 25 -an -pass 1 -vcodec libx264 -fpre libx264-lossless_fast.ffpreset -b 32000 -threads 0 test.mp4

parameter meaning will describe here.

-s qcif ; tell ffmpeg that we will encode input yuv video with qcif(176*144) size.

-i Foreman.qcif ; define input sequence I use the test classic test sequence that name foreman.qcif , I think you can get it on google.

-r 25; set frame rate

-an; disable audio we will encode only video data

-pass 1; use 1 pass video coding

-vcodec libx264; use libx264 for codec

-fpre libx264-lossless_fast.ffpreset; define encoding parameter for h.264 that contain in ffpreset file.

-b 32000; set bit rate as 32kbps.

-threads 0; set thread for encoding to zero.

test.mp4; define output file.

And after run this command encoded sequence will be created to test.mp4 and console log will shown like this.









you can play test.mp4 to see the result clip by using player that contain h.264 codec.


Decode h.264

decode process is easier, it just type a few command line to ffmpeg like this.

ffmpeg -i test.mp4 decode_out.yuv

-i test.mp4 ; define input sequence to decode.
- decode_out.yuv; define output sequence in format of yuv.

And the output that show on console will look like this.













decode_out.yuv can open by using yuv player.

That's all for this post, next post I will show about how to write code in c++ to encode/decode h.264 by using libavcodec of ffmpeg.

วันพุธที่ 16 มีนาคม พ.ศ. 2554

วันอาทิตย์ที่ 13 มีนาคม พ.ศ. 2554

My future again.

About 1 year that I have worked at Vizrt Thailand, This company is really cool place you can get free food free drunk free party and beautiful friendship.

But for now I take a look for a new challenge job that can made inspiration to me. If everything is ok, I tihnk I will get that job in soon.

:)