1. 程式人生 > >最簡單的視訊編碼器:基於libvpx(編碼YUV為VP8)

最簡單的視訊編碼器:基於libvpx(編碼YUV為VP8)

=====================================================

最簡單的視訊編碼器系列文章列表:

最簡單的視訊編碼器:編譯

=====================================================

本文記錄一個最簡單的基於libvpx的VP8視訊編碼器。這個例子是從官方的示例程式碼中精簡出來的例子。我發現與H.264不同,VP8的裸流(即不包含封裝格式的純視訊資料流)是不能播放的。換言之,VP8的裸流必須存放在容器中才可以播放。官方示例程式碼中儲存VP8視訊流的封裝格式是IVF。IVF這種封裝格式不是很常見,相關的資料可以查詢有關的文件。

此外,這個工程中的libvpx也可以編碼VP9格式的視訊。但是封裝格式那裡有點問題目前還沒有解決,所以暫時沒有包含編碼VP9的程式碼。編碼VP9和編碼VP8的函式呼叫是一模一樣的。
 

流程圖

呼叫libvpx進行視訊編碼的流程圖如下所示。
流程圖中主要的函式如下所示。
vpx_img_alloc():為影象結構體vpx_image_t分配記憶體。
vpx_codec_enc_config_default():設定引數集結構體vpx_codec_enc_cfg_t的預設值。
vpx_codec_enc_init():開啟編碼器。
vpx_codec_encode():編碼一幀影象。
vpx_codec_get_cx_data():獲取一幀壓縮編碼資料。
vpx_codec_destroy():關閉編碼器。
 
儲存資料的結構體如下所示。
vpx_image_t:儲存壓縮編碼前的畫素資料。
vpx_codec_cx_pkt_t:儲存壓縮編碼後的碼流資料。
 
IVF封裝格式處理的函式如下所示。
write_ivf_file_header():寫IVF封裝格式的檔案頭。
write_ivf_frame_header():寫IVF封裝格式中每幀資料的幀頭。
 
此外流程圖中還包括一個“flush_encoder”模組,該模組使用的函式和編碼模組是一樣的。唯一的不同在於不再輸入視訊畫素資料。它的作用是輸出編碼器中剩餘的碼流資料。
 

原始碼

/**
 * 最簡單的基於VPX的視訊編碼器
 * Simplest VPX Encoder
 *
 * 雷霄驊 Lei Xiaohua
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程式精簡了libvpx中的一個示例程式碼。
 * 可以YUV格式的畫素資料編碼為VPx(VP8/VP9)碼流,是最簡單的
 * 基於libvpx的視訊編碼器
 * 需要注意的是,編碼輸出的封裝格式是IVF
 *
 * This example modified from an example from vpx project.
 * It encode YUV data to VPX(VP8/VP9) bitstream.
 * It's the simplest encoder example based on libvpx.
 */
#include <stdio.h>
#include <stdlib.h>
 
 
#define VPX_CODEC_DISABLE_COMPAT 1
 
#include "vpx/vpx_encoder.h"
#include "vpx/vp8cx.h"
 
#define interface (&vpx_codec_vp8_cx_algo)
 
#define fourcc    0x30385056
 
#define IVF_FILE_HDR_SZ  (32)
#define IVF_FRAME_HDR_SZ (12)
 
static void mem_put_le16(char *mem, unsigned int val) {
    mem[0] = val;
    mem[1] = val>>8;
}
 
static void mem_put_le32(char *mem, unsigned int val) {
    mem[0] = val;
    mem[1] = val>>8;
    mem[2] = val>>16;
    mem[3] = val>>24;
}
 
 
static void write_ivf_file_header(FILE *outfile,
                                  const vpx_codec_enc_cfg_t *cfg,
                                  int frame_cnt) {
    char header[32];
 
    if(cfg->g_pass != VPX_RC_ONE_PASS && cfg->g_pass != VPX_RC_LAST_PASS)
        return;
    header[0] = 'D';
    header[1] = 'K';
    header[2] = 'I';
    header[3] = 'F';
    mem_put_le16(header+4,  0);                   /* version */
    mem_put_le16(header+6,  32);                  /* headersize */
    mem_put_le32(header+8,  fourcc);              /* headersize */
    mem_put_le16(header+12, cfg->g_w);            /* width */
    mem_put_le16(header+14, cfg->g_h);            /* height */
    mem_put_le32(header+16, cfg->g_timebase.den); /* rate */
    mem_put_le32(header+20, cfg->g_timebase.num); /* scale */
    mem_put_le32(header+24, frame_cnt);           /* length */
    mem_put_le32(header+28, 0);                   /* unused */
 
    fwrite(header, 1, 32, outfile);
}
 
 
static void write_ivf_frame_header(FILE *outfile,
                                   const vpx_codec_cx_pkt_t *pkt)
{
    char             header[12];
    vpx_codec_pts_t  pts;
 
    if(pkt->kind != VPX_CODEC_CX_FRAME_PKT)
        return;
 
    pts = pkt->data.frame.pts;
    mem_put_le32(header, pkt->data.frame.sz);
    mem_put_le32(header+4, pts&0xFFFFFFFF);
    mem_put_le32(header+8, pts >> 32);
 
    fwrite(header, 1, 12, outfile);
}
 
int main(int argc, char **argv) {
 
    FILE *infile, *outfile;
    vpx_codec_ctx_t codec;
    vpx_codec_enc_cfg_t cfg;
    int frame_cnt = 0;
    unsigned char file_hdr[IVF_FILE_HDR_SZ];
    unsigned char frame_hdr[IVF_FRAME_HDR_SZ];
    vpx_image_t raw;
    vpx_codec_err_t ret;
    int width,height;
         int y_size;
    int frame_avail;
    int got_data;
    int flags = 0;
 
    width = 640;
    height = 360;
 
         /* Open input file for this encoding pass */
         infile = fopen("../cuc_ieschool_640x360_yuv420p.yuv", "rb");
         outfile = fopen("cuc_ieschool.ivf", "wb");
 
         if(infile==NULL||outfile==NULL){
                   printf("Error open files.\n");
                   return -1;
         }
 
         if(!vpx_img_alloc(&raw, VPX_IMG_FMT_I420, width, height, 1)){
        printf("Fail to allocate image\n");
                   return -1;
         }
 
    printf("Using %s\n",vpx_codec_iface_name(interface));
 
    /* Populate encoder configuration */
    ret = vpx_codec_enc_config_default(interface, &cfg, 0);
    if(ret) {
        printf("Failed to get config: %s\n", vpx_codec_err_to_string(ret));
        return -1;                                                 
    }
 
    /* Update the default configuration with our settings */
    cfg.rc_target_bitrate =800;
    cfg.g_w = width;                                                          
    cfg.g_h = height;                                                        
 
    write_ivf_file_header(outfile, &cfg, 0);
 
    /* Initialize codec */                                               
    if(vpx_codec_enc_init(&codec, interface, &cfg, 0)){
        printf("Failed to initialize encoder\n");
                   return -1;
         }
 
    frame_avail = 1;
    got_data = 0;
 
         y_size=cfg.g_w*cfg.g_h;
 
    while(frame_avail || got_data) {
        vpx_codec_iter_t iter = NULL;
        const vpx_codec_cx_pkt_t *pkt;
                  
                   if(fread(raw.planes[0], 1, y_size*3/2, infile)!=y_size*3/2){
                            frame_avail=0;
                   }
 
                   if(frame_avail){
                            ret=vpx_codec_encode(&codec,&raw,frame_cnt,1,flags,VPX_DL_REALTIME);
                   }else{
                            ret=vpx_codec_encode(&codec,NULL,frame_cnt,1,flags,VPX_DL_REALTIME);
                   }
 
                   if(ret){
            printf("Failed to encode frame\n");
                            return -1;
                   }
        got_data = 0;
        while( (pkt = vpx_codec_get_cx_data(&codec, &iter)) ) {
            got_data = 1;
            switch(pkt->kind) {
            case VPX_CODEC_CX_FRAME_PKT:
                write_ivf_frame_header(outfile, pkt);
                fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz,outfile);
                break;
            default:
                break;
            }
        }
                   printf("Succeed encode frame: %5d\n",frame_cnt);
        frame_cnt++;
    }
 
    fclose(infile);
 
    vpx_codec_destroy(&codec);
 
    /* Try to rewrite the file header with the actual frame count */
    if(!fseek(outfile, 0, SEEK_SET))
        write_ivf_file_header(outfile, &cfg, frame_cnt-1);
 
    fclose(outfile);
 
    return 0;
}


執行結果

程式的輸入為一個YUV檔案(已經測試過YUV420P格式)。


輸出為IVF封裝格式的VP8碼流檔案。


VP8碼流檔案的資訊如下所示。



下載


Simplest Encoder
 

專案主頁

 
該解決方案包含了幾個常見的編碼器的使用示例:
simplest_vpx_encoder:最簡單的基於libvpx的視訊編碼器
simplest_x264_encoder:最簡單的基於libx264的視訊編碼器
simplest_x265_encoder:最簡單的基於libx265的視訊編碼器
 
 
 

相關推薦

簡單視訊編碼基於libvpx編碼YUVVP8

=====================================================最簡單的視訊編碼器系列文章列表:最簡單的視訊編碼器:編譯=====================================================本文記錄

用Python實現簡單的文字識別基於百度雲文字識別API

Python版本:3.6.5 百度雲提供的文字識別技術,準確率還是非常高的,而且每天還有5w次免費的呼叫量,對於用來學習或者偶爾拿來用用,已經完全足夠了。文章提供一個模板,稍加修改就可以直接套用。註釋中提到必須輸入的地方,你都正確地輸入了的話,就可以完成一次簡單的文字識別了

Keras學習之3迴歸問題boston_housing資料

     本實驗使用boston_housing資料集對房價資料進行迴歸分析,資料來自1970年代,波斯頓周邊地區的房價,是用於機器學習的經典資料集。該資料集很小,共計506條資料,分為404個訓練樣本和102個測試樣本,因此需要採用K-Fold,這裡取K=4。每條資料包含1

簡單基於FFMPEG+SDL的視訊播放拆分-解碼和播放

=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================本文補充記錄《

基於MFC的OpenCV簡單視訊播放

1.程式功能簡介 我們點選OpenFile按鈕,選取路徑,然後點選Ok,顯示或播放所選的圖片或視訊。 2.功能實現 2.1 Dlg類標頭檔案中新增以下項: public: CString FileName;//記錄所選檔案路徑CRect rect;//關聯影象控制元

簡單的感知學習到的一些有趣的現象

google 增加 隨著 展示 初始 成了 src img ogl 看了一些深度學習神經網絡的視頻,最近有了一點新的體會,在google的一個小工具上,地址:http://playground.tensorflow.org 一個神經網絡訓練的模擬器,發現了一些有意思的事情,

Ubuntu超簡單文書編輯nano

幫助 指令 ctr 字符 技術 打開 常用指令 列數 info nano 的使用很簡單,可以直接加上檔名就能夠開啟一個舊檔或新檔! 直接在終端輸入指令:nano text.txt,如下圖所示打開的是已有的文檔! 第一部分反白部分,是nano的版本與檔名 第二部分可以編輯文

使用Django建立一個簡單的服務

pytho eight 工程 django inf 新工程 加載 qlite sqlit Django作為python一個靈活性很強的網絡框架,在搭建服務器方面非常的方便,通過以下幾步就可以建立一個屬於自己的web服務器: 1.新建一個文件夾(盡量不要選擇在系統盤,在搭建虛

VC小工具.簡單TCP服務

error 接收數據 target ise style net socket span ++ 1、C++基於TCP_IP簡單的客戶端、服務器通信程序實例 - happy_xiahuixiax的博客 - CSDN博客.html(https://blog.csdn.net/ha

簡單的vue入門基礎語法學習

新建index.html,直接複製以下程式碼,雙擊瀏覽器執行即可。程式碼包含Vue的基礎語法,可對照練習。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">

#資料結構與演算法學習筆記#劍指Offer30把陣列排成小的數 + 自定義比較 + 測試用例Java、C/C++

2018.10.6 1.求全排列最小。事實上用全排列硬剛這道題確實是最直接的辦法,因為乍一眼看上去實在不好歸納數字之間的順序關係,全排列具體實現原理可以參考上述文章。 2.自定義比較器。為什麼說

【H.264/AVC視訊編解碼技術詳解】二十三、幀間預測編碼(1)幀間預測編碼的基本原理

《H.264/AVC視訊編解碼技術詳解》視訊教程已經在“CSDN學院”上線,視訊中詳述了H.264的背景、標準協議和實現,並通過一個實戰工程的形式對H.264的標準進行解析和實現,歡迎觀看! “紙上得來終覺淺,絕知此事要躬行”,只有自己按照標準文件以程式碼的形式操作一遍,才能對視訊壓

Android簡單視訊播放之VideoView

早上起來有時間,發一篇博文,最近在開發電視機頂盒的視訊播放,涉及到Android當中比較常見的視訊播放器控制元件的使用,以此為例,記錄下來。 首先,上效果圖: 通過VideoView播放視訊的步驟: 實現方式:使用XML佈局和java程式碼控制元

libjpeg庫編碼圖片jpg(ffmpeg解碼視訊儲存圖片RGB格式點陣圖壓縮jpg格式圖片)

壓縮流程(結合網上大家的資料以及libjpeg庫的example.c示例:都大同小異): 1、如何分配和初始化一個jpeg壓縮物件:      在libjpeg中jpeg壓縮物件被定義為struct

簡單音樂播放,還有歌詞

不是來說播放器的(前面我有一篇VLC的,可以倒回去看),這篇是來分享 這個顯示歌詞的,還是用了前一篇的PickerView的原理進行放大歌詞 使用知識點 歌詞動態放大

基於FFmpeg的視訊播放開發系列教程

   本篇開始講解音訊解碼播放,該專案用Qt的音訊類QAudioFormat, QAudioOutput等進行解碼,先講解一些關於音訊的知識。 1.取樣頻率   指每秒鐘取得聲音樣本的次數。取樣的過程就是抽取某點的頻率值,很顯然,在一秒中內抽取的點越多,獲取得頻率資

基於FFmpeg的視訊播放開發系列教程

        本節課程的目的:讀幀解碼顯示視訊         開始進入ffmepg的開發之旅。音視訊的細節知識不統一講解,我在教程中逐點滲透,容我以雷神的話開篇。       &nb

基於FFmpeg的視訊播放開發系列教程

前言        在各大部落格,論壇,看到很多人對流媒體音視訊的開發感興趣,可是不知道怎麼入門,對音視訊的瞭解也很少,寫程式碼更不用說了,經過一段時間的整理,我準備在csdn上寫一套ffmpeg音視訊播放器的開發教程,希望對想從事ffmpeg音視訊領域的

簡單的redis教程centos6.5下redis單機版安裝《一》

安裝 命令如下: wget http://download.redis.io/releases/redis-3.0.0.tar.gz tar -zxvf redis-3.0.0.tar.gz cd

兩臺電腦簡單的連線方法直連

今日我所在的部門電腦升級,由於安全級別較高,所有的裝置輸出埠都被封了,老電腦裡的東西如何匯入新電腦成了一個問題!上傳到公用的檔案伺服器吧,一是如果大家都把自己常年的資料上傳的話估計檔案伺服器的容量會是個問題;另外就是先上傳到檔案伺服器再下載到新電腦這時間可不會很短......