音視頻-H264編碼

H264編碼原理和音視頻-AAC編碼原理幾乎一樣露懒, 不同的是就buffer緩沖區(qū)的處理, 編碼的事情都是通過H264編碼器去實(shí)現(xiàn)

AAC編碼的簡略邏輯 :

源文件 ==》 AVFrame ==》編碼器 ==》AVPacket ==> 輸出文件

H264編碼的簡略邏輯

源文件 ==》 AVFrame ==》編碼器 ==》AVPacket ==> 輸出文件

是的, 兩個(gè)是一樣的。不同的是褂始, YUV是以一幀一幀的數(shù)據(jù)讀取。

#include "h264encodethread.h"
#include <QDebug>
#include <QFile>

extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/frame.h>
#include <libavutil/imgutils.h>
}

#define ERROR_BUF(ret) \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof (errbuf));


#define CHECK_IF_ERROR_BUF_END(ret, funcStr) \
    if (ret) { \
        ERROR_BUF(ret); \
        qDebug() << #funcStr << " error :" << errbuf; \
        goto end; \
    }

#ifdef Q_OS_WIN
    #define IN_YUV_FILEPATH "G:/BigBuckBunny_CIF_24fps.yuv"
    #define OUT_H264_FILEPATH "G:/BigBuckBunny_CIF_24fps_h264.h264"
    #define YUV_VIDEO_SIZE_WIDTH 352
    #define YUV_VIDEO_SIZE_HEIGH 288
#else
    #define IN_YUV_FILEPATH "/Users/liliguang/Desktop/in.yuv"
    #define OUT_H264_FILEPATH "/Users/liliguang/Desktop/out.h264"
    #define YUV_VIDEO_SIZE_WIDTH 352
    #define YUV_VIDEO_SIZE_HEIGH 288
#endif



H264EncodeThread::H264EncodeThread(QObject *parent) : QThread(parent) {
    // 當(dāng)監(jiān)聽到線程結(jié)束時(shí)(finished)描函,就調(diào)用deleteLater回收內(nèi)存
    connect(this, &H264EncodeThread::finished,
            this, &H264EncodeThread::deleteLater);
}

H264EncodeThread::~H264EncodeThread() {
    // 斷開所有的連接
    disconnect();
    // 內(nèi)存回收之前崎苗,正常結(jié)束線程
    requestInterruption();
    // 安全退出
    quit();
    wait();
    qDebug() << this << "析構(gòu)(內(nèi)存被回收)";
}

/* check that a given pix_format is supported by the encoder */
static int check_pixel_fmt(const AVCodec *codec,
                           enum AVPixelFormat pix_fmt) {
    const enum AVPixelFormat *p = codec->pix_fmts;
    while (*p != AV_PIX_FMT_NONE) {
        if (*p == pix_fmt) {
            return 1;
        }
        p++;
    }
    return 0;
}


// H264編碼
// 返回負(fù)數(shù):中途出現(xiàn)了錯(cuò)誤
// 返回0:編碼操作正常完成
static int encode(AVCodecContext *ctx,
                  AVFrame *frame,
                  AVPacket *pkt,
                  QFile &outFile) {
    // 發(fā)送數(shù)據(jù)到編碼器
    int ret = avcodec_send_frame(ctx, frame);
    if (ret < 0) {
        ERROR_BUF(ret);
        qDebug() << "avcodec_send_frame error" << errbuf;
        return ret;
    }

    // 不斷從編碼器中取出編碼后的數(shù)據(jù)
    // while (ret >= 0)
    while (true) {
        ret = avcodec_receive_packet(ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            //  output is not available in the current state - user must try to send input
            // 繼續(xù)讀取數(shù)據(jù)到frame,然后送到編碼器
            return 0;
        } else if (ret < 0) { // 其他錯(cuò)誤
            return ret;
        }
        // 成功從編碼器拿到編碼后的數(shù)據(jù)
        // 將編碼后的數(shù)據(jù)寫入文件
        outFile.write((char *) pkt->data, pkt->size);

        // 釋放pkt內(nèi)部的資源
        av_packet_unref(pkt);
    }

}




void H264EncodeThread::run() {
    // H264編碼的思路和Acc編碼的思路差不多
    // 讀取文件內(nèi)容 -> buffer緩沖區(qū) -> 一幀數(shù)據(jù) -> 核心函數(shù)編碼 -> 輸出緩沖區(qū) -> 輸出文件

    qDebug() << "H264EncodeThread run ";

    // 輸入輸出文件
    const char *infilename;
    const char *outfilename;

    // 編碼器
    const AVCodec *codec;
    // 編碼器上下文
    AVCodecContext *codecCtx = nullptr;
    // 源文件數(shù)據(jù)源存儲(chǔ)結(jié)構(gòu)指針
    AVFrame *frame = nullptr;
    // 編碼文件數(shù)據(jù)源存儲(chǔ)結(jié)構(gòu)指針
    AVPacket *pkt = nullptr;

    int check_pixel_fmt_Ret;
    int avcodec_open2_Ret;
    int av_image_alloc_ret;

    int infileOpen_Ret;
    int outfileOpen_Ret;

    int readFile_Ret;
    int encode_ret;

    int ptsIndex = 0;
    int imageSize = YUV_VIDEO_SIZE_WIDTH * YUV_VIDEO_SIZE_HEIGH * 1.5;

    infilename = IN_YUV_FILEPATH;
    outfilename = OUT_H264_FILEPATH;

    QFile inFile(infilename);
    QFile outFile(outfilename);


    // 編碼器
    codec = avcodec_find_encoder_by_name("libx264"); // 用查找編碼器的名稱的方式舀寓。 默認(rèn)的可能找到的不一樣
    CHECK_IF_ERROR_BUF_END(!codec, "avcodec_find_encoder");

    // 創(chuàng)建編碼器上下文
    codecCtx = avcodec_alloc_context3(codec);
    CHECK_IF_ERROR_BUF_END(!codecCtx, "avcodec_alloc_context3");

    // 設(shè)置編碼器上下文對(duì)應(yīng)信息
    codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    codecCtx->width = YUV_VIDEO_SIZE_WIDTH;
    codecCtx->height = YUV_VIDEO_SIZE_HEIGH;
    // 這里要計(jì)算24 pfs
    // fps = codecCtx->time_base.den / codecCtx->time_base.num
    // 24 = 24 / 1
    codecCtx->time_base.num = 1;       //分子
    codecCtx->time_base.den = 24;         //分母

    // 檢查編碼器支持的樣本格式
    check_pixel_fmt_Ret = check_pixel_fmt(codec, codecCtx->pix_fmt);
    CHECK_IF_ERROR_BUF_END(!check_pixel_fmt_Ret, "check_sample_fmt");

    // 打開編碼器
    avcodec_open2_Ret = avcodec_open2(codecCtx, codec, nullptr);
    CHECK_IF_ERROR_BUF_END(avcodec_open2_Ret, "avcodec_open2");

    // 打開源文件
    infileOpen_Ret = !inFile.open(QFile::ReadOnly);
    CHECK_IF_ERROR_BUF_END(infileOpen_Ret, "sourceFile.open");

    // 打開源文件
    outfileOpen_Ret = !outFile.open(QFile::WriteOnly);
    CHECK_IF_ERROR_BUF_END(outfileOpen_Ret, "sourceFile.outFile");

    // 創(chuàng)建輸出Packet
    pkt = av_packet_alloc();
    CHECK_IF_ERROR_BUF_END(!pkt, "av_packet_alloc");

    // 創(chuàng)建AVFrame結(jié)構(gòu)體本身
    frame = av_frame_alloc();
    CHECK_IF_ERROR_BUF_END(!frame, "av_frame_alloc");

    // 為音頻或視頻數(shù)據(jù)分配新的緩沖區(qū)胆数。
    // 在調(diào)用此函數(shù)之前,必須在框架上設(shè)置以下字段:
    // - 格式(視頻的像素格式互墓,音頻的樣本格式)
    // - 視頻的寬度和高度
    // 設(shè)置frame必要信息
    frame->format = codecCtx->pix_fmt;//像素格式
    frame->width = codecCtx->width;//分辨率寬
    frame->height = codecCtx->height; //分辨率高

    // 創(chuàng)建一幀的buffer緩沖區(qū)
    av_image_alloc_ret = av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, (AVPixelFormat)frame->format, 16);
    CHECK_IF_ERROR_BUF_END(av_image_alloc_ret < 0, "av_image_alloc");

    // 編碼
    // 源文件 ==> (AVFrame)輸入緩沖區(qū) ==> 編碼器 ==> (AVPacket)輸出緩沖區(qū) ==> 輸出文件
    // 這里應(yīng)該讀取一幀的大小
    while( (readFile_Ret = inFile.read((char *)frame->data[0], imageSize )) > 0 ) {
        frame->pts = ptsIndex++;
        // 編碼
        encode_ret = encode(codecCtx, frame, pkt, outFile);
        CHECK_IF_ERROR_BUF_END(encode_ret < 0, 
    }

    // 在讀取最后一次必尼, 沖刷緩沖區(qū)
    encode(codecCtx, nullptr, pkt, outFile);

end:
    // 關(guān)閉文件
    inFile.close();
    outFile.close();

    // 釋放資源
    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codecCtx);
    qDebug() << "AACEncodeThread end ";
}

注意點(diǎn), 創(chuàng)建buffer緩沖區(qū)篡撵, 和音頻的有點(diǎn)不一樣胰伍,使用av_image_alloc

av_image_alloc_ret = av_image_alloc(frame->data, frame->linesize, frame->width, frame->height, (AVPixelFormat)frame->format, 16);

分別用命令行和程序運(yùn)行進(jìn)行H264編碼

ffmpeg -video_size 352x288 -pixel_format yuv420p -framerate 24 -i .\BigBuckBunny_CIF_24fps.yuv -c:v libx264 .\ffmpeg_h264_out.h264

# -c:v libx264是指定使用libx264作為編碼器

大小一致齿诞, 證明代碼思路是沒有問題, 現(xiàn)在再接著用ffplay播放一下

ffplay .\ffmpeg_h264_out.h264

ffmpeg_h264_out.h264

ffplay .\BigBuckBunny_CIF_24fps_h264.h264

BigBuckBunny_CIF_24fps_h264.h264
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末骂租,一起剝皮案震驚了整個(gè)濱河市祷杈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渗饮,老刑警劉巖但汞,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異互站,居然都是意外死亡私蕾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門胡桃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來踩叭,“玉大人,你說我怎么就攤上這事翠胰∪荼矗” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵之景,是天一觀的道長斤富。 經(jīng)常有香客問我,道長锻狗,這世上最難降的妖魔是什么满力? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮轻纪,結(jié)果婚禮上油额,老公的妹妹穿的比我還像新娘。我一直安慰自己刻帚,他們只是感情好潦嘶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著我擂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缓艳。 梳的紋絲不亂的頭發(fā)上校摩,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音阶淘,去河邊找鬼衙吩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛溪窒,可吹牛的內(nèi)容都是我干的坤塞。 我是一名探鬼主播冯勉,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼摹芙!你這毒婦竟也來了灼狰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤浮禾,失蹤者是張志新(化名)和其女友劉穎交胚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盈电,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝴簇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匆帚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熬词。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吸重,靈堂內(nèi)的尸體忽然破棺而出互拾,到底是詐尸還是另有隱情,我是刑警寧澤晤锹,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布摩幔,位于F島的核電站,受9級(jí)特大地震影響鞭铆,放射性物質(zhì)發(fā)生泄漏或衡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一车遂、第九天 我趴在偏房一處隱蔽的房頂上張望封断。 院中可真熱鬧,春花似錦舶担、人聲如沸坡疼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柄瑰。三九已至,卻和暖如春剪况,著一層夾襖步出監(jiān)牢的瞬間教沾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工译断, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留授翻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像堪唐,于是被迫代替她去往敵國和親巡语。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354