欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Qt編寫全能播放組件的示例代碼

 更新時(shí)間:2023年06月15日 10:40:18   作者:Qt自定義控件  
這篇文章主要為大家詳細(xì)介紹了如何基于Qt編寫全能播放組件,可以支持ffmpeg2/3/4/5/6/Qt4/5/6,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

一、前言

從代碼層面以及自由度來(lái)說(shuō),用ffmpeg來(lái)寫全能播放組件是最佳方案(跨平臺(tái)最好最多、編解碼能力最強(qiáng)),盡管已經(jīng)有優(yōu)秀的vlc/mpv等方案可以直接用,但是vlc/mpv對(duì)標(biāo)主要是播放器應(yīng)用層面,其他層面比如視頻監(jiān)控行業(yè)領(lǐng)域就比較雞肋,所以還是從底層一點(diǎn)一滴做解碼編碼會(huì)讓自己更熟練。關(guān)于網(wǎng)上很多ffmpeg的示例,尤其是播放的示例,數(shù)不勝數(shù),比較適合用來(lái)入門學(xué)習(xí),問(wèn)題是隨著ffmpeg官方不斷的迭代更新,很多代碼都不可用,因?yàn)閍pi變了,尤其是最近5年迭代的特別快,從2017年開(kāi)始直接猛飚版本,現(xiàn)在直接干到了ffmpeg6版本,一般在安排取消或者改動(dòng)某些api接口前幾個(gè)版本,都會(huì)打上對(duì)應(yīng)的標(biāo)記,既有新方法,也兼容舊的api,一般會(huì)放在下一個(gè)大版本將舊的api接口移除,以便減輕歷史包袱,在核心功能編解碼這塊,一直是兼容的,不會(huì)說(shuō)新版本不兼容以前舊版本的一些編解碼格式。

編寫這個(gè)全能播放組件,面對(duì)用戶各種各樣的需求,當(dāng)然需要從ffmpeg2兼容到ffmpeg6以及后續(xù)的版本,現(xiàn)在用的最多的還是ffmpeg4版本,目測(cè)三五年后會(huì)陸續(xù)切換到ffmpeg5/ffmpeg6,主要是支持的格式多了,尤其是某些新標(biāo)準(zhǔn)的編解碼的效率更高。在ffmpeg提供的頭文件接口中,并沒(méi)有提供ffmpeg的大版本號(hào),只提供了字符串版本,所以需要通過(guò)子庫(kù)的主版本號(hào)來(lái)定義一個(gè)ffmpeg的版本號(hào),比如編解碼庫(kù)LIBAVCODEC_VERSION_MAJOR,56=ffmpeg2/57=ffmpeg3/58=ffmpeg4/59=ffmpeg5/60=ffmpeg6,這個(gè)編解碼庫(kù)就是ffmpeg的核心,看家的本領(lǐng)都在里面,個(gè)人覺(jué)得ffmpeg最牛逼的就是編解碼和濾鏡。在兼容各個(gè)版本的這條路上,大致整理了以下幾條:

  • 以前AVStream帶了解碼器參數(shù),stream->codec,現(xiàn)在去掉了,對(duì)應(yīng)放在stream->codecpar中。
  • 拷貝上下文參數(shù)以前是avcodec_copy_context,現(xiàn)在對(duì)應(yīng)avcodec_parameters_copy。
  • 參數(shù)拷貝以前是avcodec_copy_context,現(xiàn)在對(duì)應(yīng)avcodec_parameters_from_context/avcodec_parameters_to_context。
  • 編碼以前是avcodec_encode_video2/avcodec_encode_audio2,現(xiàn)在對(duì)應(yīng)avcodec_send_frame后avcodec_receive_packet(視音頻步驟一樣)。
  • 解碼以前是avcodec_decode_video2/avcodec_decode_audio4,現(xiàn)在對(duì)應(yīng)avcodec_send_packet后avcodec_receive_frame(視音頻步驟一樣)。
  • 打印解碼編碼器名稱以前是av_codec_next,現(xiàn)在對(duì)應(yīng)av_codec_iterate。
  • 獲取和設(shè)置旋轉(zhuǎn)角度以前是av_dict_get(stream->metadata, "rotate", NULL, 0)/av_dict_set(&stream->metadata, "rotate", "90", 0),現(xiàn)在對(duì)應(yīng)av_stream_get_side_data后av_display_rotation_get/av_stream_new_side_data后av_display_rotation_set。

二、效果圖

三、體驗(yàn)地址

國(guó)內(nèi)站點(diǎn):gitee.com/feiyangqingyun

國(guó)際站點(diǎn):github.com/feiyangqingyun

體驗(yàn)地址:pan.baidu.com/s/1l21CXNdhXFexoOwH7RyLmw 提取碼:wvjw 文件名:bin_video_demo。

四、相關(guān)代碼

//通過(guò)avcode版本定義對(duì)應(yīng)主版本
#if (LIBAVCODEC_VERSION_MAJOR == 56)
#define FFMPEG_VERSION_MAJOR 2
#elif (LIBAVCODEC_VERSION_MAJOR == 57)
#define FFMPEG_VERSION_MAJOR 3
#elif (LIBAVCODEC_VERSION_MAJOR == 58)
#define FFMPEG_VERSION_MAJOR 4
#elif (LIBAVCODEC_VERSION_MAJOR == 59)
#define FFMPEG_VERSION_MAJOR 5
#elif (LIBAVCODEC_VERSION_MAJOR == 60)
#define FFMPEG_VERSION_MAJOR 6
#endif
int FFmpegHelper::getRotate(AVStream *stream)
{
    int rotate = 0;
    //測(cè)試發(fā)現(xiàn)ffmpeg2不支持旋轉(zhuǎn)濾鏡
#if (FFMPEG_VERSION_MAJOR < 3)
    return rotate;
#endif
#if (FFMPEG_VERSION_MAJOR < 5)
    AVDictionaryEntry *tag = NULL;
    tag = av_dict_get(stream->metadata, "rotate", NULL, 0);
    if (tag) {
        rotate = atoi(tag->value);
    }
#else
    //從ffplay源碼中找到的方法
    double theta = 0;
    quint8 *displaymatrix = av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, NULL);
    if (displaymatrix) {
        theta = -av_display_rotation_get((qint32 *) displaymatrix);
        theta -= 360 * floor(theta / 360 + 0.9 / 360);
        rotate = theta;
    }
#endif
    return rotate;
}
void FFmpegHelper::setRotate(AVStream *stream, int rotate)
{
#if (FFMPEG_VERSION_MAJOR < 5)
    av_dict_set(&stream->metadata, "rotate", QString::number(rotate).toUtf8().constData(), 0);
#else
    quint8 *sidedata = av_stream_new_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, sizeof(qint32) * 9);
    if (sidedata) {
        av_display_rotation_set((qint32 *)sidedata, rotate);
    }
#endif
}
void FFmpegHelper::getStreamInfo(AVStream *stream, int &id, int &width, int &height, qint64 &bitrate, int &sampleRate, int &channelCount, int &profile)
{
#if (FFMPEG_VERSION_MAJOR < 3)
    id = stream->codec->codec_id;
    width = stream->codec->width;
    height = stream->codec->height;
    bitrate = stream->codec->bit_rate;
    sampleRate = stream->codec->sample_rate;
    channelCount = stream->codec->channels;
    profile = stream->codec->profile;
#else
    id = stream->codecpar->codec_id;
    width = stream->codecpar->width;
    height = stream->codecpar->height;
    bitrate = stream->codecpar->bit_rate;
    sampleRate = stream->codecpar->sample_rate;
    channelCount = stream->codecpar->channels;
    profile = stream->codecpar->profile;
#endif
}
int FFmpegHelper::copyContext(AVStream *streamIn, AVStream *streamOut)
{
    int result = -1;
    //設(shè)置 codec_tag = 0 這個(gè)很關(guān)鍵(不加保存的數(shù)據(jù)可能不正確)
#if (FFMPEG_VERSION_MAJOR < 3)
    result = avcodec_copy_context(streamOut->codec, streamIn->codec);
    streamOut->codec->codec_tag = 0;
#else
    result = avcodec_parameters_copy(streamOut->codecpar, streamIn->codecpar);
    streamOut->codecpar->codec_tag = 0;
#endif
    return result;
}
int FFmpegHelper::copyContext(AVCodecContext *avctx, AVStream *stream, bool from)
{
    int result = -1;
#if (FFMPEG_VERSION_MAJOR < 3)
    if (from) {
        result = avcodec_copy_context(stream->codec, avctx);
    } else {
        result = avcodec_copy_context(avctx, stream->codec);
    }
#else
    if (from) {
        result = avcodec_parameters_from_context(stream->codecpar, avctx);
    } else {
        result = avcodec_parameters_to_context(avctx, stream->codecpar);
    }
#endif
    return result;
}
int FFmpegHelper::decode(FFmpegThread *thread, AVCodecContext *avctx, AVPacket *packet, AVFrame *frame, bool video)
{
    int result = -1;
#ifdef videoffmpeg
    QString flag = video ? "視頻解碼" : "音頻解碼";
#if (FFMPEG_VERSION_MAJOR < 3)
    if (video) {
        avcodec_decode_video2(avctx, frame, &result, packet);
        if (result < 0) {
            thread->debug(result, flag, "avcodec_decode_video2");
            return result;
        }
    } else {
        avcodec_decode_audio4(avctx, frame, &result, packet);
        if (result < 0) {
            thread->debug(result, flag, "avcodec_decode_audio4");
            return result;
        }
    }
    goto end;
#else
    result = avcodec_send_packet(avctx, packet);
    if (result < 0 && (result != AVERROR(EAGAIN)) && (result != AVERROR_EOF)) {
        //if (result < 0) {
        thread->debug(result, flag, "avcodec_send_packet");
        return result;
    }
    while (result >= 0) {
        result = avcodec_receive_frame(avctx, frame);
        if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
            break;
        } else if (result < 0) {
            thread->debug(result, flag, "avcodec_receive_frame");
            break;
        }
        goto end;
    }
#endif
    return result;
end:
    //調(diào)用線程處理解碼后的數(shù)據(jù)
    if (video) {
        thread->decodeVideo2(packet);
    } else {
        thread->decodeAudio2(packet);
    }
#endif
    return result;
}
int FFmpegHelper::encode(FFmpegSave *thread, AVCodecContext *avctx, AVPacket *packet, AVFrame *frame, bool video)
{
    int result = -1;
#ifdef videosave
    QString flag = video ? "視頻編碼" : "音頻編碼";
#if (FFMPEG_VERSION_MAJOR < 3)
    if (video) {
        avcodec_encode_video2(avctx, packet, frame, &result);
        if (result < 0) {
            thread->debug(result, flag, "avcodec_encode_video2");
            return result;
        }
    } else {
        avcodec_encode_audio2(avctx, packet, frame, &result);
        if (result < 0) {
            thread->debug(result, flag, "avcodec_encode_audio2");
            return result;
        }
    }
    goto end;
#else
    result = avcodec_send_frame(avctx, frame);
    if (result < 0) {
        thread->debug(result, flag, "avcodec_send_frame");
        return result;
    }
    while (result >= 0) {
        result = avcodec_receive_packet(avctx, packet);
        if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
            break;
        } else if (result < 0) {
            thread->debug(result, flag, "avcodec_receive_packet");
            break;
        }
        goto end;
    }
#endif
    return result;
end:
    thread->writePacket2(packet, video);
#endif
    return result;
}

五、功能特點(diǎn)

  • 支持各種音視頻文件、本地?cái)z像頭設(shè)備,各種視頻流網(wǎng)絡(luò)流。
  • 支持開(kāi)始播放、暫停播放、繼續(xù)播放、停止播放、設(shè)置播放進(jìn)度、倍速播放。
  • 可設(shè)置音量、靜音切換、抓拍圖片、錄像存儲(chǔ)。
  • 自動(dòng)提取專輯信息比如標(biāo)題、藝術(shù)家、專輯、專輯封面,自動(dòng)顯示專輯封面。
  • 完美支持音視頻同步和倍速播放。
  • 解碼策略支持速度優(yōu)先、質(zhì)量?jī)?yōu)先、均衡處理、最快速度。
  • 支持手機(jī)視頻旋轉(zhuǎn)角度顯示,比如一般手機(jī)拍攝的視頻是旋轉(zhuǎn)了90度的,解碼顯示的時(shí)候需要重新旋轉(zhuǎn)90度才是正的。
  • 自動(dòng)轉(zhuǎn)換yuv420格式,比如本地?cái)z像頭是yuyv422格式,有些視頻文件是xx格式,統(tǒng)一將非yuv420格式轉(zhuǎn)換,然后再進(jìn)行處理。
  • 支持硬解碼dxva2、d3d11va等,性能極高尤其是大分辨率比如4K視頻。
  • 視頻響應(yīng)極低延遲0.2s左右,極速響應(yīng)打開(kāi)視頻流0.5s左右,專門做了優(yōu)化處理。
  • 硬解碼和GPU繪制組合,極低CPU占用,比??荡笕A等客戶端更優(yōu)。
  • 支持視頻流中的各種音頻格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推薦選擇AAC兼容性跨平臺(tái)性最好。
  • 視頻存儲(chǔ)支持yuv、h264、mp4多種格式,音頻存儲(chǔ)支持pcm、wav、aac多種格式。默認(rèn)視頻mp4格式、音頻aac格式。
  • 支持分開(kāi)存儲(chǔ)音頻視頻文件,也支持合并到一個(gè)mp4文件,默認(rèn)策略是無(wú)論何種音視頻文件格式存儲(chǔ),最終都轉(zhuǎn)成mp4及aac格式,然后合并成音視頻一起的mp4文件。
  • 支持本地?cái)z像頭實(shí)時(shí)視頻顯示帶音頻輸入輸出,音視頻錄制合并到一個(gè)mp4文件。
  • 支持H264/H265編碼(現(xiàn)在越來(lái)越多的監(jiān)控?cái)z像頭是H265視頻流格式)生成視頻文件,內(nèi)部自動(dòng)識(shí)別切換編碼格式。
  • 自動(dòng)識(shí)別視頻流動(dòng)態(tài)分辨率改動(dòng),重新打開(kāi)視頻流。
  • 支持用戶信息中包含特殊字符(比如用戶信息中包含+#@等字符)的視頻流播放,內(nèi)置解析轉(zhuǎn)義處理。
  • 純qt+ffmpeg解碼,非sdl等第三方繪制播放依賴,gpu繪制采用qopenglwidget,音頻播放采用qaudiooutput。
  • 同時(shí)支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5、ffmpeg6以及后續(xù)版本,全部做了兼容處理。如果需要支持xp需要選用ffmpeg3或ffmpeg2。
  • 支持濾鏡,源頭帶各種水印及圖形效果,可以將OSD標(biāo)簽信息和各種圖形信息寫入到MP4文件。

以上就是基于Qt編寫全能播放組件的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Qt播放組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++中的while循環(huán)和for循環(huán)語(yǔ)句學(xué)習(xí)教程

    C++中的while循環(huán)和for循環(huán)語(yǔ)句學(xué)習(xí)教程

    這篇文章主要介紹了C++中的while循環(huán)和for循環(huán)語(yǔ)句學(xué)習(xí)教程,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • C++ 設(shè)置透明背景圖片

    C++ 設(shè)置透明背景圖片

    這篇文章主要介紹了C++ 設(shè)置透明背景圖片的相關(guān)資料,需要的朋友可以參考下
    2015-06-06
  • C語(yǔ)言深入探究斐波那契數(shù)列

    C語(yǔ)言深入探究斐波那契數(shù)列

    斐波那契數(shù)一般指斐波那契數(shù)列。 斐波那契數(shù)列(Fibonacci sequence),又稱黃金分割數(shù)列,因數(shù)學(xué)家萊昂納多·斐波那契(Leonardo Fibonacci)以兔子繁殖為例子而引入,故又稱為兔子數(shù)列
    2022-05-05
  • C++非遞歸隊(duì)列實(shí)現(xiàn)二叉樹(shù)的廣度優(yōu)先遍歷

    C++非遞歸隊(duì)列實(shí)現(xiàn)二叉樹(shù)的廣度優(yōu)先遍歷

    這篇文章主要介紹了C++非遞歸隊(duì)列實(shí)現(xiàn)二叉樹(shù)的廣度優(yōu)先遍歷,實(shí)例分析了遍歷二叉樹(shù)相關(guān)算法技巧,并附帶了兩個(gè)相關(guān)算法實(shí)例,需要的朋友可以參考下
    2015-07-07
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的<三子棋>案例

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的<三子棋>案例

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的《三子棋》,本文通過(guò)功能區(qū)分一步步實(shí)現(xiàn)該案例,通過(guò)逐步的解析和代碼列舉,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語(yǔ)言簡(jiǎn)明清晰講解枚舉

    C語(yǔ)言簡(jiǎn)明清晰講解枚舉

    枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡(jiǎn)單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法,感興趣的朋友來(lái)看看吧
    2022-05-05
  • C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之迷宮求解問(wèn)題

    C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之迷宮求解問(wèn)題

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之迷宮求解問(wèn)題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • c++顯式棧實(shí)現(xiàn)遞歸介紹

    c++顯式棧實(shí)現(xiàn)遞歸介紹

    大家好,本篇文章主要講的是c++顯式棧實(shí)現(xiàn)遞歸介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • c++ 臨時(shí)對(duì)象的來(lái)源

    c++ 臨時(shí)對(duì)象的來(lái)源

    大家可能對(duì)這個(gè)臨時(shí)對(duì)象這個(gè)概念還不是很清楚,那么首先我們花一些時(shí)間來(lái)理解臨時(shí)對(duì)象
    2013-01-01
  • 淺析_tmain()與main()的區(qū)別

    淺析_tmain()與main()的區(qū)別

    _tmain()是為了支持unicode所使用的main一個(gè)別名,既然是別名,應(yīng)該有宏定義過(guò)的,在哪里定義的呢?就在那個(gè)讓你困惑的<stdafx.h>里
    2013-03-03

最新評(píng)論