FFmpeg源碼:bytestream_get_byte函數解析

一、引言

FFmpeg源碼中經常使用到bytestream_get_byte這個函數,比如使用FFmpeg對BMP圖片進行解析,其源碼會調用函數bmp_decode_frame,而該函數內部會通過bytestream_get_byte讀取BMP 的header。本文講解函數bytestream_get_byte的作用和內部實現。本文演示用的FFmpeg源碼版本為5.0.3,該ffmpeg在CentOS 7.5上通過10.2.1版本的gcc編譯

二、bytestream_get_byte函數內部實現

?FFmpeg源碼目錄下的libavutil/attributes.h?中存在如下宏定義

#ifdef __GNUC__
#    define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
#    define AV_GCC_VERSION_AT_MOST(x,y)  (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
#else
#    define AV_GCC_VERSION_AT_LEAST(x,y) 0
#    define AV_GCC_VERSION_AT_MOST(x,y)  0
#endif#ifndef av_always_inline
#if AV_GCC_VERSION_AT_LEAST(3,1)
#    define av_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#    define av_always_inline __forceinline
#else
#    define av_always_inline inline
#endif
#endif

其中:__GNUC__ 、__GNUC_MINOR__?分別代表gcc的主版本號,次版本號。

所以下面這段條件編譯指令

#if AV_GCC_VERSION_AT_LEAST(3,1)# ? ?define av_always_inline __attribute__((always_inline)) inline

的意思是如果gcc主版本號不小于3,次版本號不小于1,就執行

#    define av_always_inline __attribute__((always_inline)) inline

我的gcc版本為10.2.1,滿足該條件,所以會定義該宏。__attribute__((always_inline))的意思是強制內聯,具體可以參考:《__attribute__((always_inline))》

FFmpeg源碼目錄下的libavutil/intreadwrite.h中存在宏定義:

#define AV_RB8(x)     (((const uint8_t*)(x))[0])
#define AV_WB8(p, d)  do { ((uint8_t*)(p))[0] = (d); } while(0)

libavcodec/bytestream.h 中存在如下宏定義

#define DEF(type, name, bytes, read, write)                                  \
static av_always_inline type bytestream_get_ ## name(const uint8_t **b)        \
{                                                                              \(*b) += bytes;                                                             \return read(*b - bytes);                                                   \
}
DEF(unsigned int, byte, 1, AV_RB8 , AV_WB8)

語句 static av_always_inline type bytestream_get_ ## name(const uint8_t **b)? 中## 為宏定義的操作連接符。具體可以參考:《define的一些騷操作:##操作連接符、#@字符化操作符、#字符串化操作符、\行繼續操作》

所以宏定義DEF(unsigned int, byte, 1, AV_RB8 , AV_WB8) 等價于:

static __attribute__((always_inline)) inline unsigned int bytestream_get_byte(const uint8_t **b) 
{                                                                              (*b) += 1;                                                            return (((const uint8_t*)(*b - 1))[0]);                                           
}    

不強制內聯,變成普通的函數相當于:

static unsigned int bytestream_get_byte(const uint8_t **b) 
{                                                                              (*b) += 1;                                                            return (((const uint8_t*)(*b - 1))[0]);                                           
}     

編寫測試例子main.c :

#include <stdint.h>
#include "stdio.h"static unsigned int bytestream_get_byte(const uint8_t **b) 
{                                                                              (*b) += 1;                                                            return (((const uint8_t*)(*b - 1))[0]);                                           
}        int main()
{const uint8_t *buf = "ABCDEF";printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));return 0;
}

Linux平臺下使用gcc編譯,輸出為:

通過該例子可以很容易看出來,函數bytestream_get_byte作用就是返回(以形參*b為首地址的)緩沖區中的第一個字符,并將地址(*b)加1,這樣再次調用函數bytestream_get_byte時就會返回緩沖區的第二個字符。以此類推。比如上述測試例子中,最開始buf指向"ABCDEF"。第一次執行printf("%c\n", bytestream_get_byte(&buf))時,會輸出'A',然后buf指向"BCDEF";第二次執行printf("%c\n", bytestream_get_byte(&buf))時,會輸出'B',然后buf指向"CDEF",以此類推。

不將FFmpeg的宏定義展開,則上述測試例子可以修改為 main.c:

#include <stdint.h>
#include "stdio.h"#define av_always_inline __attribute__((always_inline)) inline#define AV_RB8(x)     (((const uint8_t*)(x))[0])
#define AV_WB8(p, d)  do { ((uint8_t*)(p))[0] = (d); } while(0)#define DEF(type, name, bytes, read, write)                                  \
static av_always_inline type bytestream_get_ ## name(const uint8_t **b)        \
{                                                                              \(*b) += bytes;                                                             \return read(*b - bytes);                                                   \
}     DEF(unsigned int, byte, 1, AV_RB8 , AV_WB8)int main()
{const uint8_t *buf = "ABCDEF";printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));printf("%c\n", bytestream_get_byte(&buf));return 0;
}

Linux平臺下使用gcc編譯,輸出為:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/14489.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/14489.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/14489.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spark SQL 中DataFrame DSL的使用

在上一篇文章中已經大致說明了DataFrame APi,下面我們具體介紹DataFrame DSL的使用。DataFrame DSL是一種命令式編寫Spark SQL的方式&#xff0c;使用的是一種類sql的風格語法。 文章鏈接&#xff1a; 一、單詞統計案例引入 import org.apache.spark.sql.{DataFrame, SaveMod…

在SpringBoot中添加自定義增強SpringEvent事件組件

場景說明&#xff1a;在使用SpringBoot時&#xff0c;總是要添加一大堆自定義事件&#xff0c;實現ApplicationEvent&#xff0c;來實現事件發送。 這樣寫代碼量非常大。為了方便和避免出錯&#xff0c;封裝自定義的模塊&#xff0c;快速實現泛型中調用SpringEvent實現事件。省…

Xinstall助力實現App間直接跳轉,提升用戶體驗

在移動互聯網時代&#xff0c;App已成為我們日常生活中不可或缺的一部分。然而&#xff0c;在使用各類App時&#xff0c;我們經常會遇到需要在不同App之間切換的情況&#xff0c;這時如果能夠直接跳轉&#xff0c;將會大大提升用戶體驗。而Xinstall正是這樣一款能夠幫助開發者實…

OpenCV 獲取 RTSP 攝像頭視頻流保存至本地

介紹 Java OpenCV 是一個強大的開源計算機視覺庫&#xff0c;它提供了豐富的圖像處理和分析功能&#xff0c;越來越多的應用需要使用攝像頭來獲取實時視頻流進行處理和分析。 在 Java 中使用 OpenCV 打開攝像頭的基本步驟如下&#xff1a; 確保已經安裝了OpenCV庫使用 OpenC…

Raylib 繪制自定義字體的一種套路

Raylib 繪制自定義字體是真的難搞。我的需求是程序可以加載多種自定義字體&#xff0c;英文中文的都有。 我調試了很久成功了&#xff01; 很有用的參考&#xff0c;建議先看一遍&#xff1a; 瞿華&#xff1a;raylib繪制中文內容 個人筆記&#xff5c;Raylib 的字體使用 - …

W801 實現獲取天氣情況

看了小安派&#xff08;AiPi-Eyes 天氣站&#xff09;的源碼&#xff0c;感覺用W801也可以實現。 一、部分源碼 main.c #include "wm_include.h" #include "Lcd_Driver.h"void UserMain(void) {printf("\n user task \n");Lcd_Init();Lcd_Clea…

MySQL主從復制(五):讀寫分離

一主多從架構主要應用場景&#xff1a;讀寫分離。讀寫分離的主要目標是分攤主庫的壓力。 讀寫分離架構 讀寫分離架構一 架構一結構圖&#xff1a; 這種結構模式下&#xff0c;一般會把數據庫的連接信息放在客戶端的連接層&#xff0c;由客戶端主動做負載均衡。也就是說由客戶…

RabbitMQ 消息隊列安裝及入門

市面常見消息隊列中間件對比 技術名稱吞吐量 /IO/并發時效性&#xff08;類似延遲&#xff09;消息到達時間可用性可靠性優勢應用場景activemq萬級高高高簡單易學中小型企業、項目rabbitmq萬級極高&#xff08;微秒&#xff09;高極高生態好&#xff08;基本什么語言都支持&am…

為什么MySQL推薦使用utf8mb4代替utf8?

前言 在MySQL數據庫的世界里&#xff0c;字符集的選擇直接影響著數據的存儲和檢索方式&#xff0c;尤其是對于多語言支持至關重要的應用而言。近年來&#xff0c;utf8mb4字符集逐漸成為MySQL中存儲Unicode字符的標準選擇&#xff0c;逐步取代了傳統的utf8字符集。本文將詳細探…

leetcode124 二叉樹中的最大路徑和-dp

題目 二叉樹中的 路徑 被定義為一條節點序列&#xff0c;序列中每對相鄰節點之間都存在一條邊。同一個節點在一條路徑序列中 至多出現一次 。該路徑 至少包含一個 節點&#xff0c;且不一定經過根節點。 路徑和 是路徑中各節點值的總和。 給你一個二叉樹的根節點 root &…

【Crypto】Rabbit

文章目錄 一、Rabbit解題感悟 一、Rabbit 題目提示很明顯是Rabbit加密&#xff0c;直接解 小小flag&#xff0c;拿下&#xff01; 解題感悟 提示的太明顯了

redis核心面試題二(實戰優化)

文章目錄 10. redis配置mysql實戰優化[重要]11. redis之緩存擊穿、緩存穿透、緩存雪崩12. redis實現分布式session 10. redis配置mysql實戰優化[重要] // 最初實現OverrideTransactionalpublic Product createProduct(Product product) {productRepo.saveAndFlush(product);je…

MQTT 5.0 報文解析 05:DISCONNECT

歡迎閱讀 MQTT 5.0 報文系列 的第五篇文章。在上一篇中&#xff0c;我們已經介紹了 MQTT 5.0 的 PINGREQ 和 PINGRESP 報文。現在&#xff0c;我們將介紹下一個控制報文&#xff1a;DISCONNECT。 在 MQTT 中&#xff0c;客戶端和服務端可以在斷開網絡連接前向對端發送一個 DIS…

手把手教你搭建一個花店小程序商城

如果你是一位花店店主&#xff0c;想要為你的生意搭建一個精美的小程序商城&#xff0c;以下是你將遵循的五個步驟。 步驟1&#xff1a;登錄喬拓云平臺進入后臺 首先&#xff0c;你需要登錄喬拓云平臺的后臺管理頁面。你可以在電腦或移動設備上的瀏覽器中輸入喬拓云的官方網站…

2024.5.26 機器學習周報

目錄 引言 Abstract 文獻閱讀 1、題目 2、引言 3、創新點 4、Motivation 5、naive Lite-HRNet 6、Lite-HRNet 7、實驗 深度學習 解讀SAM(Segment Anything Model) 1、SAM Task 2、SAM Model 2.1、Patch Embedding 2.2、Positiona Embedding 2.3、Transformer …

移動端適配:vw適配方案

vw (Viewport Width) 是一種長度單位&#xff0c;代表視口寬度的百分比。1vw 等于視口寬度的1%。在網頁設計和前端開發中&#xff0c;vw 單位常用于實現響應式設計和屏幕適配&#xff0c;尤其是針對不同尺寸和分辨率的移動設備。 為什么使用vw適配&#xff1f; 響應式: 使用v…

互聯網醫院開發:引領智慧醫療新時代

隨著科技的迅猛發展和互聯網的普及&#xff0c;傳統醫療模式正在迎來一場深刻的變革。互聯網醫院的崛起&#xff0c;打破了時間和空間的限制&#xff0c;為患者和醫療機構帶來了更加便捷、高效、安全的醫療服務體驗。本文將從技術角度深入探討互聯網醫院的開發&#xff0c;包括…

【openpcdet中yaml文件的DATA_AUGMENTOR學習】

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、代碼二、詳細解釋DISABLE_AUG_LISTAUG_CONFIG_LIST1. gt_sampling2. random_world_flip3. random_world_rotation4. random_world_scaling 總結 前言 提示…

多線程(八)

一、wait和notify 等待 通知 機制 和join的用途類似,多個線程之間隨機調度,引入 wait notify 就是為了能夠從應用層面上,干預到多個不同線程代碼的執行順序.( 這里說的干預,不是影響系統的線程調度策略 內核里的線程調度,仍然是無序的. 相當于是在應用程序…

Pod容器資源限制和探針

目錄 一、資源限制 1.Pod和容器的資源請求和限制 2.CPU 資源單位 案例一 案例二 二、健康檢查&#xff0c;又稱為探針&#xff08;Probe&#xff09; 1.探針的三種規則 2.Probe支持三種檢查方法 3.探測獲得的三種結果 案例一&#xff1a;exec 案例二&#xff1a;htt…