?
該例子講了如何輸出一個libavformat庫所支持格式的媒體文件。 (1)av_register_all(),初始化libavcodec庫,并注冊所有的編解碼器和格式。 (2)guess_format(),根據文件名來獲取輸出文件格式,默認為mpeg。 (3)av_alloc_format_context()分配輸出媒體內容。 ov->oformat = fmt; snprintf( oc->filename, sizeof(oc->filename), “%s”, filename ); (4)add_video_stream()使用默認格式的編解碼器來增加一個視頻流,并初始化編解碼器。 (4.1)av_new_stream()增加一個新的流到一個媒體文件。 (4.2)初始化編解碼器: c = st->codec; c->codec_id = codec_id; c->codec_type = CODEC_TYPE_VIDEO; c->bit_rate = 400000; c->width = 352; c->height = 288; c->time_base.den = STREAM_FRAME_RATE; //每秒25副圖像 c->time_base.num = 1; c->gop_size = 12; c->pix_fmt = STREAM_PIX_FMT; //默認格式為PIX_FMT_YUV420P …… …… (5)av_set_parameters()設置輸出參數,即使沒有參數,該函數也必須被調用。 (6)dump_format()輸出格式信息,用于調試。 (7)open_video()打開視頻編解碼器并分配必要的編碼緩存。 (7.1)avcodec_find_encoder()尋找c->codec_id指定的視頻編碼器。 (7.2)avcodec_open()打開編碼器。 (7.3)分配視頻輸出緩存: video_outbuf_size = 200000; video_outbuf = av_malloc( video_outbuf_size ); (7.4)picture = alloc_picture()分配原始圖像。 (7.4.1)avcodec_alloc_frame()分配一個AVFrame并設置默認值。 (7.4.2)size = avpicture_get_size()計算對于給定的圖片格式以及寬和高,所需占用多少內存。 (7.4.3)picture_buf = av_malloc( size )分配所需內存。 (7.4.4)avpicture_fill()填充AVPicture的域。 (7.5)可選。如果輸出格式不是YUV420P,那么臨時的YUV420P格式的圖像也是需要的,由此再轉換為我們所需的格式,因此需要為臨時的YUV420P圖像分配緩存: tmp_picture = alloc_picture() 說明:tmp_picture,picture,video_outbuf。如果輸出格式為YUV420P,則直接通過avcodec_ encode_video()函數將picture緩存中的原始圖像編碼保存到video_outbuf緩存中;如果輸出格式不是YUV420P,則需要先通過sws_scale()函數,將YUV420P格式轉換為目標格式,此時tmp_picture緩存存放的是YUV420P格式的圖像,而picture緩存為轉換為目標格式后保存的圖像,進而再將picture緩存中的圖像編碼保存到video_outbuf緩存中。 (8)url_fopen()打開輸出文件,如果需要的話。 (9)av_write_header()寫流動頭部。 (10)LOOP循環{ 計算當前視頻時間video_pts 是否超時退出循環? write_video_frame()視頻編碼 } (10.1)write_video_frame() 如果圖片不是YUV420P,則需要用sws_scale()函數先進行格式轉換。 若需要原始圖像: av_init_packet()初始化一個包的選項域。 av_write_frame()向輸出媒體文件寫一個包,該包會包含一個視頻幀。 若需要編碼圖像: avcodec_encode_video()編碼一視頻幀。 av_init_packet() av_write_frame() (11)close_video()關閉每個編解碼器。 (12)av_write_trailer()寫流的尾部。 (13)釋放資源 av_freep()釋放AVFormatContext下的AVStream->AVCodecContext和AVStream: for( i = 0; i < oc->nb_streams; i++ ){ av_freep( &oc->streams[i]->codec ); av_freep( &oc->streams[i] ); } url_fclose()關閉輸出文件。 av_free()釋放AVFormatContext。 apiexample.c例子教我們如何去利用ffmpeg庫中的api函數來自己編寫編解碼程序。 (1)首先,main函數中一開始會去調用avcodec_init()函數,該函數的作用是初始化libavcodec,而我們在使用avcodec庫時,該函數必須被調用。 (2)avcodec_register_all()函數,注冊所有的編解碼器(codecs),解析器(parsers)以及碼流過濾器(bitstream filters)。當然我們也可以使用個別的注冊函數來注冊我們所要支持的格式。 (3)video_encode_example()函數用于視頻編碼,由圖可知,所有的編碼工作都在該函數內完成。 (4)avcodec_find_encoder()函數用于查找一個與codec ID相匹配的已注冊的編碼器。 (5)avcodec_alloc_context()函數用于分配一個AVCodecContext并設置默認值,如果失敗返回NULL,并可用av_free()進行釋放。 (6)avcodec_alloc_frame()函數用于分配一個AVFrame并設置默認值,如果失敗返回NULL,并可用av_free()進行釋放。 (7)設置參數: 設置采樣參數,即比特率。 c->bit_rate = 400000; 設置分辨率,必須是2的倍數。 c->width = 352; c->height = 288; 設置幀率。 c->time_base = (AVRational){1,25}; 該幀率為25,其實timebase = 1/framerate,花括號內分別為分子和分母。 設置GOP大小。 c->gop_size = 10; 該值表示每10幀會插入一個I幀(intra frame)。 設置B幀最大數。 c->max_b_frames = 1; 該值表示在兩個非B幀之間,所允許插入的B幀的最大幀數。 設置像素格式。 c->pix_fmt = PIX_FMT_YUV420P; 該值將像素格式設置為YUV420P。 (8)avcodec_open()函數用給定的AVCodec來初始化AVCodecContext。 (9)接著是打開文件,f = fopen( filename, “wb” ); (10)分配圖像和輸出緩存。 申請100KB左右的內存作為輸出緩存。 outbuf_size = 100000; outbuf = malloc( outbuf_size ); 根據幀的大小來確定YUV420所占內存大小,一個像素,RGB格式占用3個字節,而YUV420格式只占用兩個字節。YUV420格式是指,每個像素都保留一個Y(亮度)分量,而在水平方向上,不是每行都取U和V分量,而是一行只取U分量,則其接著一行就只取V分量,以此重復,所以420不是指沒有V,而是指一行采樣只取U,另一行采樣只取V。在取U和V時,每兩個Y之間取一個U或V。但從4x4矩陣列來看,每4個矩陣點Y區域中,只有一個U和V,所以它們的比值是4:1。所以對于一個像素,RGB需要8 * 3 = 24位,即占3個字節;而YUV420P,8 + 8/4 + 8/4 = 12位,即占2個字節,其中8指Y分量,8/4指U和V分量。 size = c->width * c->height; picture_buf = malloc( (size * 3) / 2 ); picture->data[0] = picture_buf; picture->data[1] = picture->data[0] + size; picture->data[2] = picture->data[1] + size / 4; picture->linesize[0] = c->width; picture->linesize[1] = c->width / 2; picture->linesize[2] = c->width / 2; 其中,data[0]存放Y,data[1]存放U,data[2]存放V【FixMe】。linesize[0]表示Y分量的寬度,linesize[1]表示U分量的寬度,linesize[2]表示V分量的寬度。 (11)編碼一秒鐘的視頻,幀率為25,所以需要循環25次,每次編碼一幀。 (11.1)準備一幅偽圖像,即自己自定義往里面塞數據。 for(y=0;yheight;y++){ for(x=0;xwidth;x++){ picture->data[0][y*picture->linesize[0]+x]=x+y+i*3; } } for(y=0;yheight/2;y++){ for(x=0;xwidth/2;x++){ picture->data[1][y*picture->linesize[1]+x]=128+y+i*2; picture->data[2][y*picture->linesize[2]+x]=64+x+i*5; } } (11.2)avcodec_encode_video()從picture中編碼一幀視頻數據,并存入到outbuf中,而期間所使用的編碼器為c。 (11.3)將編碼完的數據寫入到文件里。 (12)對延時的幀數據進行編碼。因為像MPEG4中,I幀、P幀和B幀之間存在一定的延時【FixMe】。同樣是avcodec_encode_video(),然后寫入文件。 (13)添加結束代碼,使其成為一個真正的mpeg文件。 outbuf[0] = 0x00; outbuf[1] = 0x00; outbuf[2] = 0x01; outbuf[3] = 0xb7; fwrite( outbuf, 1, 4, f ); 這個結束代碼表示什么??? (14)釋放資源。 fclose(f); free(picture_buf); free(outbuf); avcodec_close(c); av_free(c); av_free(picture);