部落格

  • 美國原油均價下跌,市場憂產油過剩

    美國原油均價下跌,市場憂產油過剩

    美國能源資訊局(EIA)公佈,6月26日全美普通汽油平均零售價格為每加侖2.288美元,創下半年新低;較前週下跌3美分,較去年同期下跌4.1美分。各地區零售汽油均價全面下跌,西岸地區的零售汽油均價最高達每加侖2.826美元,較前週下跌3.4美分;墨西哥灣地區的零售汽油均價最低為每加侖2.053美元,較前週下跌3.8美分。NYMEX原油期貨上週下跌3.9%,因擔憂油市過剩以及美國產量持續增長的影響。

    美國汽車協會(AAA)報告表示,6月26日全美普通無鉛汽油平均零售價格為每加侖2.26美元,較前週下跌3美分,較一個月前下跌11美分,較去年同期下跌4美分。AAA表示,包括美國煉油廠原油加工量處在新高水平、汽油以及原油庫存高企,以及今年以來的需求表現較為疲弱等,都是造成零售汽油均價下跌的主因。美國汽油需求已經有所回升,6月16日當週,美國汽油日均需求較前週926.9萬桶增至981.6萬桶,逼近5月底創下新高的982.2萬桶。

    AAA表示,即將到來的美國獨立紀念日假期(7月4日),預計將有創同期新高的4,420萬人出遊(離家超過50英里),比去年還要增加125萬人或2.9%。其中,預計將有3,750萬人開車出遊,同樣較去年同期增加2.9%。AAA資深副總裁Bill Sutherland表示,就業市場強勁、薪資增加以及消費信心提高等,都是今年出遊人數將創下歷年同期新高的主要原因。AAA表示,當前美國零售汽油均價逼近歷年的同期新低,但鑑於下週的假期來臨,零售汽油均價可能會有小幅上漲。

    《Oilprice.com》報導,相比十年前在油田自然衰竭的影響下,市場認為全球的產油上限即將到來;如今市場更多的是認為石油消費的巔峰將會到來,主要因為電動車興起的影響。一份調查顯示,如果電動車的年增長率維持在60%,則2023年的全球石油日需求量將會比當前減少200萬桶;如果年增長率為30%,則2025年的全球石油日需求量會比當前減少200萬桶。

    不過,實際數據顯示,至少目前為止石油需求增長並未下滑;過去十年全球石油日需求量年均增長110萬桶,過去五年則年均增長140萬桶,2016年則增長160萬桶,而去年的電動車銷售增長41%。報導認為,包括人口以及中產階級增長,以及開發中國家汽車銷售持續增加等,都是令全球石油需求仍持續攀高的主因。

    (本文內容由授權使用。圖片出處:wikipedia)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

  • 市場電動車需求上升,Nissan將對現有車款EV化

    市場電動車需求上升,Nissan將對現有車款EV化

    日經新聞報導,日產汽車(Nissan)將大舉擴充電動車(EV)產品陣容,日產社長兼CEO西川廣人27日於橫濱市舉行的定期股東會上表示,「今年度將推出新型『Leaf』,且中期來看,將推動現行已進行量產販售的車款EV化」。

    因北美、中國加強環保規範,帶動EV有望進一步普及。日產目前的EV車款僅有「Leaf」等少數幾款,而之後計畫將SUV、輕型汽車以及商用車進行EV化。

    另外,日產會長Carlos Ghosn也在股東會上表示,「日產在EV界居領導位置。日產EV累計銷售量超過60萬台、為美國特斯拉(Tesla)的2倍」。

    日本市調機構富士經濟(Fuji Keizai)6月22日公布銷售動向報告指出,EV在2025年以後需求將急速增加,預估2030年時EV年銷售量將增至407萬台、超越油電混合車(HV、2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。在中國需求增加加持下,2035年EV全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。

    (本文內容由授權使用。圖片出處:)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • Json模塊和Pickle模塊的使用

    在對數據進行序列化和反序列化是常見的數據操作,Python提供了兩個模塊方便開發者實現數據的序列化操作,即 json 模塊和 pickle 模塊。這兩個模塊主要區別如下:

    • json 是一個文本序列化格式,而 pickle 是一個二進制序列化格式;
    • json 是我們可以直觀閱讀的,而 pickle 不可以;
    • json 是可互操作的,在 Python 系統之外廣泛使用,而 pickle 則是 Python 專用的;
    • 默認情況下,json 只能表示 Python 內置類型的子集,不能表示自定義的類;但 pickle 可以表示大量的 Python 數據類型。

    Json 模塊

    Json 是一種輕量級的數據交換格式,由於其具有傳輸數據量小、數據格式易解析等特點,它被廣泛應用於各系統之間的交互操作,作為一種數據格式傳遞數據。它包含多個常用函數,具體如下:

    dumps()函數

    dumps()函數可以將 Python 對象編碼成 Json 字符串。例如:

    #字典轉成json字符串 加上ensure_ascii=False以後,可以識別中文, indent=4是間隔4個空格显示
    
    import json         
    d={'小明':{'sex':'男','addr':'上海','age':26},'小紅':{ 'sex':'女','addr':'上海', 'age':24},}
    print(json.dumps(d,ensure_ascii=False,indent=4))
    
    #執行結果:
    {
        "小明": {
            "sex": "男",
            "addr": "上海",
            "age": 26
        },
        "小紅": {
            "sex": "女",
            "addr": "上海",
            "age": 24
        }
    }

    dump()函數

    dump()函數可以將 Python對象編碼成 json 字符串,並自動寫入到文件中,不需要再單獨寫文件。例如:

    #字典轉成json字符串,不需要寫文件,自動轉成的json字符串寫入到‘users.json’的文件中 
    import json                                                                         
    d={'小明':{'sex':'男','addr':'上海','age':26},'小紅':{ 'sex':'女','addr':'上海', 'age':24},}
    #打開一個名字為‘users.json’的空文件
    fw =open('users.json','w',encoding='utf-8')
    
    json.dump(d,fw,ensure_ascii=False,indent=4)

    loads()函數

    loads()函數可以將 json 字符串轉換成 Python 的數據類型。例如:

    #這是users.json文件中的內容
    {
        "小明":{
            "sex":"男",
            "addr":"上海",
            "age":26
        },
        "小紅":{
            "sex":"女",
            "addr":"上海",
            "age":24
        }
    }
    
    #!/usr/bin/python3
    #把json串變成python的數據類型   
    import json  
    #打開‘users.json’的json文件
    f =open('users.json','r',encoding='utf-8')
    #讀文件
    res=f.read()
    print(json.loads(res))
    
    #執行結果:
    {'小明': {'sex': '男', 'addr': '上海', 'age': 26}, '小紅': {'sex': '女', 'addr': '上海', 'age': 24}}

    load()函數

    load()loads()功能相似,load()函數可以將 json 字符串轉換成 Python 數據類型,不同的是前者的參數是一個文件對象,不需要再單獨讀此文件。例如:

    #把json串變成python的數據類型:字典,傳一個文件對象,不需要再單獨讀文件 
    import json   
    #打開文件
    f =open('users.json','r',encoding='utf-8') 
    print(json.load(f))
    
    #執行結果:
    {'小明': {'sex': '男', 'addr': '上海', 'age': 26}, '小紅': {'sex': '女', 'addr': '上海', 'age': 24}}

    Pickle 模塊

    Pickle 模塊與 Json 模塊功能相似,也包含四個函數,即 dump()、dumps()、loads() 和 load(),它們的主要區別如下:

    • dumps 和 dump 的區別在於前者是將對象序列化,而後者是將對象序列化並保存到文件中。
    • loads 和 load 的區別在於前者是將序列化的字符串反序列化,而後者是將序列化的字符串從文件讀取並反序列化。

    dumps()函數

    dumps()函數可以將數據通過特殊的形式轉換為只有python語言認識的字符串,例如:

    import pickle
    # dumps功能
    import pickle
    data = ['A', 'B', 'C','D']  
    print(pickle.dumps(data))
    
    b'\x80\x03]q\x00(X\x01\x00\x00\x00Aq\x01X\x01\x00\x00\x00Bq\x02X\x01\x00\x00\x00Cq\x03X\x01\x00\x00\x00Dq\x04e.'

    dump()函數

    dump()函數可以將數據通過特殊的形式轉換為只有python語言認識的字符串,並寫入文件。例如:

    # dump功能
    with open('test.txt', 'wb') as f:
        pickle.dump(data, f)
    print('寫入成功')
    
    寫入成功

    loads()函數

    loads()函數可以將pickle數據轉換為python的數據結構。例如:

    # loads功能
    msg = pickle.loads(datastr)
    print(msg)
    
    ['A', 'B', 'C', 'D']

    load()函數

    load()函數可以從數據文件中讀取數據,並轉換為python的數據結構。例如:

    # load功能
    with open('test.txt', 'rb') as f:
       data = pickle.load(f)
    print(data)
    
    ['A', 'B', 'C', 'D']

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

  • 樹莓派3B/3B+和4B安裝OpenCV教程 (屢試不爽)

    樹莓派3B/3B+和4B安裝OpenCV教程 (屢試不爽)

        

    安裝前準備

    1. 在樹莓派上拓展文件系統

        如果你使用的樹莓派為新裝的系統,那麼第一件事情就是擴展文件系統,以包括microSD卡上的所有空間。

        具體步驟如下:

          1.在樹莓派終端(或者SSH)上輸入:

    1 $ sudo raspi-config

     

          2.然後選擇“高級選項”菜單項

       

           3.然後選擇“擴展文件系統”:

      

           4. 選擇第一個選項“A1.Expand Filesystem”,按鍵盤上的Enter鍵,完成後點擊“Finish”按鈕,重新啟動樹莓派。

        如果不能重啟,則可以執行以下操作:

    1 $ sudo reboot

     

          重新啟動后,文件系統已經擴展為包括micro-SD卡上的所有空間。可以通過執行 df -h 檢查輸出來驗證磁盤是否已擴展。

    1 $ df -h

     

     

      

     

          5. 此時我的樹莓派文件系統已擴展為包含16GB的micor-SD卡。如果您使用的是8GB卡,則可能使用了將近50%的可用空間,

        因此,一件簡單的事情就是刪除LibreOffice和Wolfram引擎以釋放Pi上的一些空間:

    1 $ sudo apt-get purge wolfram-engine
    2 $ sudo apt-get purge libreoffice*
    3 $ sudo apt-get clean
    4 $ sudo apt-get autoremove

     

     

      2.更換樹莓派源為清華鏡像源,防止後面下載GTK2.0失敗。

        換源方法參考:

    安裝步驟

       1.更新系統

    1 $ sudo apt-get update && sudo apt-get upgrade

       

       2.在樹莓派上安裝OpenCV所需要依賴的工具和一些圖像視頻庫

    • 安裝包括CMake的開發人員工具

      1 // 安裝build-essential、cmake、git和pkg-config
      2 sudo apt-get install build-essential cmake git pkg-config 

       

    • 安裝常用圖像工具包

      1 // 安裝jpeg格式圖像工具包
      2 sudo apt-get install libjpeg8-dev 

       

      1 // 安裝tif格式圖像工具包
      2 sudo apt-get install libtiff5-dev 

       

      1 // 安裝JPEG-2000圖像工具包
      2 sudo apt-get install libjasper-dev 

       

      1 // 安裝png圖像工具包
      2 sudo apt-get install libpng12-dev 

       

    • 安裝常用的視頻庫

    1 //v4l中4後面的是 英文字母“l”
    2 sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev

     

      

    • 安裝GTK2.0

      1 sudo apt-get install libgtk2.0-dev

       

    • 安裝OpenCV數值優化函數包

    1 sudo apt-get install libatlas-base-dev gfortran

     

        

     

        3.下載編譯OpenCV源碼

    • 下載opencv3.4.3和opencv_contrib3.4.3

    1 // 下載OpenCV
    2 wget -O opencv-3.4.3.zip https://github.com/Itseez/opencv/archive/3.4.3.zip

     

       

    1 // 解壓OpenCV
    2 unzip opencv-3.4.3.zip

     

       

    1 // 下載OpenCV_contrib庫:
    2 wget -O opencv_contrib-3.4.3.zip https://github.com/Itseez/opencv_contrib/archive/3.4.3.zip

     

       

     

    1 // 解壓OpenCV_contrib庫:
    2 unzip opencv_contrib-3.4.3.zip

       

    • 配置CMake編譯OpenCV 3環境

        使用CMake設置編譯,然後運行 make 來編譯OpenCV。這是整個過程中耗時最長的步驟,大約4個小時。

        回到OpenCV存儲庫並創建 build 文件夾,用來存放 CMake 編譯時產生的臨時文件。

    1 //具體路徑請以實際為準
    2 cd ~/opencv-3.4.3
    3 
    4 // 新建build文件夾
    5 mkdir build
    6      
    7 // 進入build文件夾
    8 cd build
    9   

       

     

    •  設置CMake編譯參數,安裝目錄默認為/usr/local

        注意參數名、等號和參數值之間不能有空格,每行末尾“\”之前有空格,這裏使用換行符“\”是為了看起來工整,參數值最後是兩個英文的點,意思是上級

      目錄(【注意】如果在root用戶下執行cmake命令,請將OPENCV_EXTRA_MODULES_PATH的值改為絕對路徑,如:/home/pi/opencv_contrib-3.4.3/modules):

    /** CMAKE_BUILD_TYPE是編譯方式
    * CMAKE_INSTALL_PREFIX是安裝目錄
    * OPENCV_EXTRA_MODULES_PATH是加載額外模塊
    * INSTALL_PYTHON_EXAMPLES是安裝官方python例程
    * BUILD_EXAMPLES是編譯例程(這兩個可以不加,不加編譯稍微快一點點,想要C語言的例程的話,在最後一行前加參數INSTALL_C_EXAMPLES=ON,要C++例程的話在最後一行前加參數INSTALL_C_EXAMPLES=ONINSTALL_CXX_EXAMPLES=ON)
    **/
     
    sudo cmake -D CMAKE_BUILD_TYPE=RELEASE \
        -D CMAKE_INSTALL_PREFIX=/usr/local \
        -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.4.3/modules \
        -D INSTALL_PYTHON_EXAMPLES=ON \
        -D INSTALL_CXX_EXAMPLES=ON \
        -D BUILD_EXAMPLES=ON ..
     

       

     

         配置完后如下圖:

       

     

    • 備份build文件中的東西

         因為下一步的編譯會使用build文件中的東西,假如編譯失敗后還要重新進行cmake,比較耽誤時間,這裏可以直接備份一下cmake好的build文件夾,

      命名為build1,重新make的時候可以拿來用。

    1 //返回上層目錄
    2 cd ..
    3 //備份release文件夾
    4 cp -r release ./release1

      

    • 為樹莓派增加SWAP

        在開始編譯之前,建議你增加交換空間。這將使你使用樹莓派的所有四個內核來編譯OpenCV,而不會由於內存耗盡導致編譯掛起。

        打開 etc dphys – swapfile   文件:

    1 $ sudo nano /etc/dphys-swapfile

        然後編輯 CONF_SWAPSIZE  變量:

       

     

          注意:此處我將交換空間從100MB增加到2048MB;如果你不執行此步驟,你的樹莓派編譯時很可能掛起。

        重新啟動交換服務:

    1 $ sudo /etc/init.d/dphys-swapfile stop
    2 $ sudo /etc/init.d/dphys-swapfile start

       

     

         注意:增加交換空間的大小是燒壞樹莓派 microSD卡的好方法。基於閃存的存儲只能執行有限數量的寫操作,直到該卡基本不能夠容納1和0。我們只能在短時間內

      啟動大型交換,所以這沒什麼大問題的。

    • 編譯OpenCV 3

    1 /**
    2 * 以管理員身份,否則容易出錯
    3 * make命令參數-j4指允許make使用最多4個線程進行編譯,這樣編譯速度會更快
    4 * 可以根據自己機器的情況進行更改
    5 * 使用tee命令可以將編譯過程中終端显示的信息保存到make.log文件中,便於查看,這樣即使VNC斷線,終端的* 信息太多看不到,也可以通過make.log文件查看編譯過程。
    6 **/
    7  
    8 sudo make -j4 2>&1 | tee make.log

       

     

        如果看到進度編譯到100%,那麼說明編譯安裝成功。

       

    1 // 安裝
    2 sudo make install
    3  
    4 // 更新動態鏈接庫
    5 sudo ldconfig

        

        注意: 不要忘記回到 /etc/dphysswapfile 文件:

           1.將 CONF_SWAPSIZE 重置為 100MB

           2.重新啟動交換服務

    其他配置

       設置庫的路徑,相當於windows下的環境變量,便於使用OpenCV庫,也可以不進行設置,使用的時候說明路徑也可。例如在編譯時說明使用庫的路徑是 -L/usr/local/lib 

     

      1.配置opencv.conf 

       打開opencv.conf配置文件,在末端加入如下內容: 

     1 //這裏我使用的是樹莓派默認的nano,也可以使用vim、gedit.打開opencv.conf文件
     2 sudo nano /etc/ld.so.conf.d/opencv.conf
     3 
     4 /**
     5 *在末端添加如下內容
     6 *注意:?表示一個空格,可能原因是有的語言要求最後有一個空格才可以編譯通過。
     7 **/
     8 /usr/local/lib
     9 ?
    10 
    11 //加載一下
    12sudo ldconfig

       

      2.打開 bash.bashrc 配置文件 

    1 //打開bash.bashrc配置文件
    2 sudo gedit /etc/bash.bashrc
    3 
    4 
    5 // 在最後添加如下內容
    6 PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig       
    7 export PKG_CONFIG_PATH

        

      3.重啟樹莓派

    1 sudo reboot

     

     檢測OpenCV使用是否正常

      python程序

     1 import cv2
     2 import numpy as np
     3 cv2.namedWindow("gray")
     4 img = np.zeros((512,512),np.uint8)#生成一張空的灰度圖像
     5 cv2.line(img,(0,0),(511,511),255,5)#繪製一條白色直線
     6 cv2.imshow("gray",img)#显示圖像
     7 #循環等待,按q鍵退出
     8 while True:
     9     key=cv2.waitKey(1)
    10     if key==ord("q"):
    11         break
    12 cv2.destoryWindow("gray")

      保存文件為 test.py ,並在終端運行程序

    1 sudo python3 test.py

      運行結果如下:

      

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 精通awk系列(8):awk劃分字段的3種方式

    精通awk系列(8):awk劃分字段的3種方式

    回到:

    詳細分析awk字段分割

    awk讀取每一條記錄之後,會將其賦值給$0,同時還會對這條記錄按照預定義變量FS劃分字段,將劃分好的各個字段分別賦值給$1 $2 $3 $4...$N,同時將劃分的字段數量賦值給預定義變量NF

    引用字段的方式

    $N引用字段:

    • N=0:即$0,引用記錄本身
    • 0<N<=NF:引用對應字段
    • N>NF:表示引用不存在的字段,返回空字符串
    • N<0:報錯

    可使用變量或計算的方式指定要獲取的字段序號。

    awk '{n = 5;print $n}' a.txt
    awk '{print $(2+2)}' a.txt   # 括號必不可少,用於改變優先級
    awk '{print $(NF-3)}' a.txt

    分割字段的方式

    讀取record之後,將使用預定義變量FS、FIELDWIDTHS或FPAT中的一種來分割字段。分割完成之後,再進入main代碼段(所以,在main中設置FS對本次已經讀取的record是沒有影響的,但會影響下次讀取)。

    劃分字段方式(一):FS或-F

    FS或者-F:字段分隔符

    • FS為單個字符時,該字符即為字段分隔符
    • FS為多個字符時,則採用正則表達式模式作為字段分隔符
    • 特殊的,也是FS默認的情況,FS為單個空格時,將以連續的空白(空格、製表符、換行符)作為字段分隔符
    • 特殊的,FS為空字符串””時,將對每個字符都進行分隔,即每個字符都作為一個字段
    • 設置預定義變量IGNORECASE為非零值,正則匹配時表示忽略大小寫(隻影響正則,所以FS為單字時無影響)
    • 如果record中無法找到FS指定的分隔符(例如將FS設置為”\n”),則整個記錄作為一個字段,即$1$0相等
    # 字段分隔符指定為單個字符
    awk -F":" '{print $1}' /etc/passwd
    awk 'BEGIN{FS=":"}{print $1}' /etc/passwd
    
    # 字段分隔符指定為正則表達式
    awk 'BEGIN{FS=" +|@"}{print $1,$2,$3,$4,$5,$6}' a.txt

    劃分字段方式(二):FIELDWIDTHS

    指定預定義變量FIELDWIDTHS按字符寬度分割字段,這是gawk提供的高級功能。在處理某字段缺失時非常好用。

    用法:

    示例1:

    # 沒取完的字符串DDD被丟棄,且NF=3
    $ awk 'BEGIN{FIELDWIDTHS="2 3 2"}{print $1,$2,$3,$4}' <<<"AABBBCCDDDD"
    AA BBB CC 
    
    # 字符串不夠長度時無視
    $ awk 'BEGIN{FIELDWIDTHS="2 3 2 100"}{print $1,$2,$3,$4"-"}' <<<"AABBBCCDDDD"
    AA BBB CC DDDD-
    
    # *號取剩餘所有,NF=3
    $ awk 'BEGIN{FIELDWIDTHS="2 3 *"}{print $1,$2,$3}' <<<"AABBBCCDDDD"      
    AA BBB CCDDDD
    
    # 字段數多了,則取完字符串即可,NF=2
    $ awk 'BEGIN{FIELDWIDTHS="2 30 *"}{print $1,$2,NF}' <<<"AABBBCCDDDD"  
    AA BBBCCDDDD 2

    示例2:處理某些字段缺失的數據。

    如果按照常規的FS進行字段分割,則對於缺失字段的行和沒有缺失字段的行很難統一處理,但使用FIELDWIDTHS則非常方便。

    假設a.txt文本內容如下:

    ID  name    gender  age  email          phone
    1   Bob     male    28   abc@qq.com     18023394012
    2   Alice   female  24   def@gmail.com  18084925203
    3   Tony    male    21   aaa@163.com    17048792503
    4   Kevin   male    21   bbb@189.com    17023929033
    5   Alex    male    18                  18185904230
    6   Andy    female  22   ddd@139.com    18923902352
    7   Jerry   female  25   exdsa@189.com  18785234906
    8   Peter   male    20   bax@qq.com     17729348758
    9   Steven  female  23   bc@sohu.com    15947893212
    10  Bruce   female  27   bcbd@139.com   13942943905

    因為email字段有的是空字段,所以直接用FS劃分字段不便處理。可使用FIELDWIDTHS。

    # 字段1:4字符
    # 字段2:8字符
    # 字段3:8字符
    # 字段4:2字符
    # 字段5:先跳過3字符,再讀13字符,該字段13字符
    # 字段6:先跳過2字符,再讀11字符,該字段11字符
    awk '
    BEGIN{FIELDWIDTHS="4 8 8 2 3:13 2:11"}
    NR>1{
        print "<"$1">","<"$2">","<"$3">","<"$4">","<"$5">","<"$6">"
    }' a.txt
    
    # 如果email為空,則輸出它
    awk '
    BEGIN{FIELDWIDTHS="4 8 8 2 3:13 2:11"}
    NR>1{
        if($5 ~ /^ +$/){print $0}
    }' a.txt

    劃分字段方式(三):FPAT

    FS是指定字段分隔符,來取得除分隔符外的部分作為字段。

    FPAT是取得匹配的字符部分作為字段。它是gawk提供的一個高級功能。

    FPAT根據指定的正則來全局匹配record,然後將所有匹配成功的部分組成$1、$2...,不會修改$0

    • awk 'BEGIN{FPAT="[0-9]+"}{print $3"-"}' a.txt
    • 之後再設置FS或FPAT,該變量將失效

    FPAT常用於字段中包含了字段分隔符的場景。例如,CSV文件中的一行數據如下:

    Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,12345-6789,USA

    其中逗號分隔每個字段,但雙引號包圍的是一個字段整體,即使其中有逗號。

    這時使用FPAT來劃分各字段比使用FS要方便的多。

    echo 'Robbins,Arnold,"1234 A Pretty Street, NE",MyTown,MyState,12345-6789,USA' |\
    awk '
        BEGIN{FPAT="[^,]*|(\"[^\"]*\")"}
        {
            for (i=1;i<NF;i++){
                print "<"$i">"
            }
        }
    '

    最後,patsplit()函數和FPAT的功能一樣。

    檢查字段劃分的方式

    有FS、FIELDWIDTHS、FPAT三種獲取字段的方式,可使用PROCINFO數組來確定本次使用何種方式獲得字段。

    PROCINFO是一個數組,記錄了awk進程工作時的狀態信息。

    如果:

    • PROCINFO["FS"]=="FS",表示使用FS分割獲取字段
    • PROCINFO["FPAT"]=="FPAT",表示使用FPAT匹配獲取字段
    • PROCINFO["FIELDWIDTHS"]=="FIELDWIDTHS",表示使用FIELDWIDTHS分割獲取字段

    例如:

    if(PROCINFO["FS"]=="FS"){
        ...FS spliting...
    } else if(PROCINFO["FPAT"]=="FPAT"){
        ...FPAT spliting...
    } else if(PROCINFO["FIELDWIDTHS"]=="FIELDWIDTHS"){
        ...FIELDWIDTHS spliting...
    }

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    ※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

    ※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

  • ThreadLocal原理分析與代碼驗證

    ThreadLocal提供了線程安全的數據存儲和訪問方式,利用不帶key的get和set方法,居然能做到線程之間隔離,非常神奇。

    比如

    ThreadLocal<String> threadLocal = new ThreadLocal<>();

    in thread 1

    //in thread1
    treadLocal.set("value1");
    .....
    //value的值是value1
    String value = threadLocal.get();

    in thread 2

    //in thread2
    treadLocal.set("value2");
    .....
    //value的值是value2
    String value = threadLocal.get();

    不論thread1和thread2是不是同時執行,都不會有線程安全問題,我們來測試一下。

    線程安全測試

    開10個線程,每個線程內都對同一個ThreadLocal對象set不同的值,會發現ThreadLocal在每個線程內部get出來的值,只會是自己線程內set進去的值,不會被別的線程影響。

    static void testUsage() throws InterruptedException {
        Utils.println("-------------testUsage-------------------");
        ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    
        AtomicBoolean threadSafe = new AtomicBoolean(true);
        int count = 10;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        Random random = new Random(736832);
        for (int i = 0; i < count; i ++){
            new Thread(() -> {
                try {
                    //生成一個隨機數
                    Long value = System.nanoTime() + random.nextInt();
                    threadLocal.set(value);
                    Thread.sleep(1000);
    
                    Long value2 = threadLocal.get();
                    if (!value.equals(value2)) {
                        //get和set的value不一致,說明被別的線程修改了,但這是不可能出現的
                        threadSafe.set(false);
                        Utils.println("thread unsafe, this could not be happen!");
                    }
                } catch (InterruptedException e) {
    
                }finally {
                    countDownLatch.countDown();
                }
    
            }).start();
        }
    
        countDownLatch.await();
    
        Utils.println("all thread done, and threadSafe is " + threadSafe.get());
        Utils.println("------------------------------------------");
    }

    輸出:

    -------------testUsage------------------
    all thread done, and threadSafe is true
    -----------------------------------------

    原理淺析

    翻開ThreadLocal的源碼,會發現ThreadLocal只是一個空殼子,它並不存儲具體的value,而是利用當前線程(Thread.currentThread())的threadLocalMap來存儲value,key就是這個threadLocal對象本身。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    Thread的threadLocals字段是ThreadLocalMap類型(你可以簡單理解為一個key value的Map),key是ThreadLocal對象,value是我們在外層設置的值

    • 當我們調用threadLocal.set(value)方法的時候,會找到當前線程的threadLocals這個map,然後以this作為key去set key value
    • 當我們調用threadLocal.get()方法的時候,會找到當前線程的threadLocals這個map,然後以this作為key去get value
    • 當我們調用threadLocal.remove()方法的時候,會找到當前線程的threadLocals這個map,然後以this作為key去remove

    這就相當於:

    Thread.currentThread().threadLocals.set(threadLocal1, "value1");
    .....
    //value的值是value1
    String value = Thread.currentThread().threadLocals.get(threadLocal1);

    因為每個Thread都是不同的對象,所以他們的threadLocals也是不同的map,threadLocal在不同的線程里工作時,實際上是從不同的map里get/set,這也就是線程安全的原因了,了解到這一點就差不多了。

    再深入一些,ThreadLocalMap的結構

    如果繼續翻ThreadLocalMap的源碼,會發現它有個字段table,是Entry類型的數組。

    我們不妨寫段代碼,把ThreadLocalMap的結構輸出出來。

    由於Thread.threadLocals和ThreadLocalMap類不是public的,我們只有通過反射來獲取它的值。反射的代碼如下(如果嫌長可以不看,直接看輸出):

    static Object getThreadLocalMap(Thread thread) throws NoSuchFieldException, IllegalAccessException {        
        //get thread.threadLocals
        Field threadLocals = Thread.class.getDeclaredField("threadLocals");
        threadLocals.setAccessible(true);
        return threadLocals.get(thread);
    }
    
    static void printThreadLocalMap(Object threadLocalMap) throws NoSuchFieldException, IllegalAccessException {
        String threadName = Thread.currentThread().getName();
        
        if(threadLocalMap == null){
            Utils.println("threadMap is null, threadName:" + threadName);
            return;
        }
    
        Utils.println(threadName);
    
        //get threadLocalMap.table
        Field tableField = threadLocalMap.getClass().getDeclaredField("table");
        tableField.setAccessible(true);
        Object[] table = (Object[])tableField.get(threadLocalMap);
        Utils.println("----threadLocals (ThreadLocalMap), table.length = " + table.length);
    
        for (int i = 0; i < table.length; i ++){
            WeakReference<ThreadLocal<?>> entry = (WeakReference<ThreadLocal<?>>)table[i];
            printEntry(entry, i);
        }
    }
    static void printEntry(WeakReference<ThreadLocal<?>> entry, int i) throws NoSuchFieldException, IllegalAccessException {
        if(entry == null){
            Utils.println("--------table[" + i + "] -> null");
            return;
        }
        ThreadLocal key = entry.get();
        //get entry.value
        Field valueField = entry.getClass().getDeclaredField("value");
        valueField.setAccessible(true);
        Object value = valueField.get(entry);
    
        Utils.println("--------table[" + i + "] -> entry key = " + key + ", value = " + value);
    }

    測試代碼:

    static void testStructure() throws InterruptedException {
        Utils.println("-------------testStructure----------------");
        ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
        ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
    
        Thread thread1 = new Thread(() -> {
            threadLocal1.set("threadLocal1-value");
            threadLocal2.set("threadLocal2-value");
    
            try {
                Object threadLocalMap = getThreadLocalMap(Thread.currentThread());
                printThreadLocalMap(threadLocalMap);
    
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }, "thread1");
    
        thread1.start();
    
        //wait thread1 done
        thread1.join();
    
        Thread thread2 = new Thread(() -> {
            threadLocal1.set("threadLocal1-value");
            try {
                Object threadLocalMap = getThreadLocalMap(Thread.currentThread());
                printThreadLocalMap(threadLocalMap);
    
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }, "thread2");
    
        thread2.start();
        thread2.join();
        Utils.println("------------------------------------------");
    }

    我們在創建了兩個ThreadLocal的對象threadLocal1和threadLocal2,在線程1里為這兩個對象設置值,在線程2里只為threadLocal1設置值。然後分別打印出這兩個線程的threadLocalMap。

    輸出結果為:

    -------------testStructure----------------
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> entry key = java.lang.ThreadLocal@33baa315, value = threadLocal2-value
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> null
    --------table[9] -> null
    --------table[10] -> entry key = java.lang.ThreadLocal@4d42db5c, value = threadLocal1-value
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> null
    thread2
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> null
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> null
    --------table[9] -> null
    --------table[10] -> entry key = java.lang.ThreadLocal@4d42db5c, value = threadLocal1-value
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> null
    ------------------------------------------

    從結果上可以看出:

    • 線程1和線程2的threadLocalMap對象的table字段,是個數組,長度都是16
    • 由於線程1里給兩個threadLocal對象設置了值,所以線程1的ThreadLocalMap里有兩個entry,數組下標分別是1和10,其餘的是null(如果你自己寫代碼驗證,下標不一定是1和10,不需要糾結這個問題,只要前後對的上就行)
    • 由於線程2里只給一個threadLocal對象設置了值,所以線程1的ThreadLocalMap里只有一個entry,數組下標是10,其餘的是null
    • threadLocal1這個對象在兩個線程里都設置了值,所以當它作為key加入二者的threadLocalMap時,key是一樣的,都是java.lang.ThreadLocal@4d42db5c;下標也是一樣的,都是10。

    為什麼是WeakReference

    查看Entry的源碼,會發現Entry繼承自WeakReference:

    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
    
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    構造函數里把key傳給了super,也就是說,ThreadLocalMap中對key的引用,是WeakReference的。

    Weak reference objects, which do not prevent their referents from being
    made finalizable, finalized, and then reclaimed. Weak references are most
    often used to implement canonicalizing mappings.

    通俗點解釋:

    當一個對象僅僅被weak reference(弱引用), 而沒有任何其他strong reference(強引用)的時候, 不論當前的內存空間是否足夠,當GC運行的時候, 這個對象就會被回收。

    看不明白沒關係,還是寫代碼測試一下什麼是WeakReference吧…

    static void testWeakReference(){
        Object obj1 = new Object();
        Object obj2 = new Object();
        WeakReference<Object> obj1WeakRef = new WeakReference<>(obj1);
        WeakReference<Object> obj2WeakRf = new WeakReference<>(obj2);
        //obj32StrongRef是強引用
        Object obj2StrongRef = obj2;
        Utils.println("before gc: obj1WeakRef = " + obj1WeakRef.get() + ", obj2WeakRef = " + obj2WeakRf.get() + ", obj2StrongRef = " + obj2StrongRef);
    
        //把obj1和obj2設為null
        obj1 = null;
        obj2 = null;
        //強制gc
        forceGC();
    
        Utils.println("after gc: obj1WeakRef = " + obj1WeakRef.get() + ", obj2WeakRef = " + obj2WeakRf.get() + ", obj2StrongRef = " + obj2StrongRef);
    }

    結果輸出:

    before gc: obj1WeakRef = java.lang.Object@4554617c, obj2WeakRef = java.lang.Object@74a14482, obj2StrongRef = java.lang.Object@74a14482
    after gc: obj1WeakRef = null, obj2WeakRef = java.lang.Object@74a14482, obj2StrongRef = java.lang.Object@74a14482

    從結果上可以看出:

    • 我們先new了兩個對象(為避免混淆,稱他們為Object1和Object2),分別用變量obj1和obj2指向它們,同時定義了一個obj2StrongRef,也指向Object2,最後把obj1和obj2均指向null
    • 由於Object1沒有變量強引用它了,所以在gc后,Object1被回收了,obj1WeakRef.get()返回了null
    • 由於Object2還有obj2StrongRef在引用它,所以gc后,Object2依然存在,沒有被回收。

    那麼,ThreadLocalMap中對key的引用,為什麼是WeakReference的呢?

    因為大部分情況下,線程不死

    大部分情況下,線程不會頻繁的創建和銷毀,一般都會用線程池。所以線程對象一般不會被清除,線程的threadLocalMap就一直存在。
    如果key對ThreadLocal是強引用,那麼key永遠不會被回收,即使我們程序里再也不用它了。

    但是key是弱引用的話,情況就會得到改善:只要沒有指向threadLocal的強引用了,這個ThreadLocal對象就會被清理。

    我們還是寫代碼測試一下吧。

    /**
     * 測試ThreadLocal對象什麼時候被回收
     * @throws InterruptedException
     */
    static void testGC() throws InterruptedException {
        Utils.println("-----------------testGC-------------------");
        Thread thread1 = new Thread(() -> {
            ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
            ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
    
            threadLocal1.set("threadLocal1-value");
            threadLocal2.set("threadLocal2-value");
    
            try {
                Object threadLocalMap = getThreadLocalMap(Thread.currentThread());
                Utils.println("print threadLocalMap before gc");
                printThreadLocalMap(threadLocalMap);
    
                //set threadLocal1 unreachable
                threadLocal1 = null;
    
                forceGC();
    
                Utils.println("print threadLocalMap after gc");
                printThreadLocalMap(threadLocalMap);
    
    
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }, "thread1");
    
        thread1.start();
        thread1.join();
        Utils.println("------------------------------------------");
    }
    

    我們在一個線程里為兩個ThreadLocal對象賦值,最後把其中一個對象的強引用移除,gc后打印當前線程的threadLocalMap。
    輸出結果如下:

    -----------------testGC-------------------
    print threadLocalMap before gc
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> entry key = java.lang.ThreadLocal@7bf9cebf, value = threadLocal2-value
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> null
    --------table[9] -> null
    --------table[10] -> entry key = java.lang.ThreadLocal@56342d38, value = threadLocal1-value
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> null
    print threadLocalMap after gc
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> entry key = java.lang.ThreadLocal@7bf9cebf, value = threadLocal2-value
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> null
    --------table[9] -> null
    --------table[10] -> entry key = null, value = threadLocal1-value
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> null
    ------------------------------------------

    從輸出結果可以看到,當我們把threadLocal1的強引用移除並gc之後,table[10]的key變成了null,說明threadLocal1這個對象被回收了;threadLocal2的強引用還在,所以table[1]的key不是null,沒有被回收。

    但是你發現沒有,table[10]的key雖然是null了,但value還活着! table[10]這個entry對象,也活着!

    是的,因為只有key是WeakReference….

    無用的entry什麼時候被回收?

    通過查看ThreadLocal的源碼,發現在ThreadLocal對象的get/set/remove方法執行時,都有機會清除掉map中已經無用的entry。

    最容易驗證清除無用entry的場景分別是:

    • remove:這個不用說了,這哥們本來就是做這個的
    • get:當一個新的threadLocal對象(沒有set過value)發生get調用時,也會作為新的entry加入map,在加入的過程中,有機會清除掉無用的entry,邏輯和下面的set相同。
    • set: 當一個新的threadLocal對象(沒有set過value)發生set調用時,會在map中加入新的entry,此時有機會清除掉無用的entry,清除的邏輯是:
      • 清除掉table數組中的那些無用entry中的一部分,記住是一部分,這個一部分可能全部,也可能是0,具體算法請看ThreadLocalMap.cleanSomeSlots,這裏不解釋了。
      • 如果上一步的”一部分”是0(即清除了0個),並且map的size(是真實size,不是table.length)大於等於threshold(table.length的2/3),會執行一次rehash,在rehash的過程中,清理掉所有無用的entry,並減小size,清理后的size如果還大於等於threshold – threshold/4,則把table擴容為原來的兩倍大小。

    還有其他場景,但不好驗證,這裏就不提了。

    ThreadLocal源碼就不貼了,貼了也講不明白,相關邏輯在setInitialValue、cleanSomeSlots、expungeStaleEntries、rehash、resize等方法里。

    在我們寫代碼驗證entry回收邏輯之前,還需要簡單的提一下ThreadLocalMap的hash算法。

    entry數組的下標如何確定?

    每個ThreadLocal對象,都有一個threadLocalHashCode變量,在加入ThreadLocalMap的時候,根據這個threadLocalHashCode的值,對entry數組的長度取余(hash & (len – 1)),餘數作為下標。

    那麼threadLocalHashCode是怎麼計算的呢?看源碼:

    public class ThreadLocal<T>{
        private final int threadLocalHashCode = nextHashCode();
        private static AtomicInteger nextHashCode = new AtomicInteger();
    
        private static final int HASH_INCREMENT = 0x61c88647;
    
        private static int nextHashCode() {
            return nextHashCode.getAndAdd(HASH_INCREMENT);
        }
        ...
    }

    ThreadLocal類維護了一個全局靜態字段nextHashCode,每new一個ThreadLocal對象,nextHashCode都會遞增0x61c88647,作為下一個ThreadLocal對象的threadLocalHashCode。

    這個0x61c88647,是個神奇的数字,只要以它為遞增值,那麼和2的N次方取余時,在有限的次數內不會發生重複。
    比如和16取余,那麼在16次遞增內,不會發生重複。還是寫代碼驗證一下吧。

    int hashCode = 0;
    int HASH_INCREMENT = 0x61c88647;
    int length = 16;
    
    for(int i = 0; i < length ; i ++){
        int h = hashCode & (length - 1);
        hashCode += HASH_INCREMENT;
        System.out.println("h = " + h + ", i = " + i);
    }

    輸出結果為:

    h = 0, i = 0
    h = 7, i = 1
    h = 14, i = 2
    h = 5, i = 3
    h = 12, i = 4
    h = 3, i = 5
    h = 10, i = 6
    h = 1, i = 7
    h = 8, i = 8
    h = 15, i = 9
    h = 6, i = 10
    h = 13, i = 11
    h = 4, i = 12
    h = 11, i = 13
    h = 2, i = 14
    h = 9, i = 15
    

    你看,h的值在16次遞增內,沒有發生重複。 但是要記住,2的N次方作為長度才會有這個效果,這也解釋了為什麼ThreadLocalMap的entry數組初始長度是16,每次都是2倍的擴容。

    驗證新threadLocal的get和set時回收部分無效的entry

    為了驗證出結果,我們需要先給ThreadLocal的nextHashCode重置一個初始值,這樣在測試的時候,每個threadLocal的數組下標才會按照我們設計的思路走。

    static void resetNextHashCode() throws NoSuchFieldException, IllegalAccessException {
        Field nextHashCodeField = ThreadLocal.class.getDeclaredField("nextHashCode");
        nextHashCodeField.setAccessible(true);
        nextHashCodeField.set(null, new AtomicInteger(1253254570));
    }

    然後在測試代碼里,我們先調用resetNextHashCode方法,然後加兩個ThreadLocal對象並set值,gc前把強引用去除,gc后再new兩個新的theadLocal對象,分別調用他們的get和set方法。
    在每個關鍵點打印出threadLocalMap做比較。

    static void testExpungeSomeEntriesWhenGetOrSet() throws InterruptedException {
        Utils.println("----------testExpungeStaleEntries----------");
        Thread thread1 = new Thread(() -> {
            try {
                resetNextHashCode();
    
                //注意,這裏必須有兩個ThreadLocal,才能驗證出threadLocal1被清理
                ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
                ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
    
                threadLocal1.set("threadLocal1-value");
                threadLocal2.set("threadLocal2-value");
    
    
                Object threadLocalMap = getThreadLocalMap(Thread.currentThread());
                //set threadLocal1 unreachable
                threadLocal1 = null;
                threadLocal2 = null;
                forceGC();
    
                Utils.println("print threadLocalMap after gc");
                printThreadLocalMap(threadLocalMap);
    
                ThreadLocal<String> newThreadLocal1 = new ThreadLocal<>();
                newThreadLocal1.get();
                Utils.println("print threadLocalMap after call a new newThreadLocal1.get");
                printThreadLocalMap(threadLocalMap);
    
                ThreadLocal<String> newThreadLocal2 = new ThreadLocal<>();
                newThreadLocal2.set("newThreadLocal2-value");
                Utils.println("print threadLocalMap after call a new newThreadLocal2.set");
                printThreadLocalMap(threadLocalMap);
    
    
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }, "thread1");
    
        thread1.start();
        thread1.join();
        Utils.println("------------------------------------------");
    }

    程序輸出結果為:

    ----------testExpungeStaleEntries----------
    print threadLocalMap after gc
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> entry key = null, value = threadLocal2-value
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> null
    --------table[9] -> null
    --------table[10] -> entry key = null, value = threadLocal1-value
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> null
    print threadLocalMap after call a new newThreadLocal1.get
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> entry key = null, value = threadLocal2-value
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> entry key = java.lang.ThreadLocal@2b63dc81, value = null
    --------table[9] -> null
    --------table[10] -> null
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> null
    print threadLocalMap after call a new newThreadLocal2.set
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> null
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> null
    --------table[7] -> null
    --------table[8] -> entry key = java.lang.ThreadLocal@2b63dc81, value = null
    --------table[9] -> null
    --------table[10] -> null
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> null
    --------table[14] -> null
    --------table[15] -> entry key = java.lang.ThreadLocal@2e93c547, value = newThreadLocal2-value
    ------------------------------------------

    從結果上來看,

    • gc后table[1]和table[10]的key變成了null
    • new newThreadLocal1.get后,新增了table[8],table[10]被清理了,但table[1]還在(這就是cleanSomeSlots中some的意思)
    • new newThreadLocal2.set后,新增了table[15],table[1]被清理了。

    驗證map的size大於等於table.length的2/3時回收所有無效的entry

        static void testExpungeAllEntries() throws InterruptedException {
            Utils.println("----------testExpungeStaleEntries----------");
            Thread thread1 = new Thread(() -> {
                try {
                    resetNextHashCode();
    
                    int threshold = 16 * 2 / 3;
                    ThreadLocal[] threadLocals = new ThreadLocal[threshold - 1];
                    for(int i = 0; i < threshold - 1; i ++){
                        threadLocals[i] = new ThreadLocal<String>();
                        threadLocals[i].set("threadLocal" + i + "-value");
                    }
    
                    Object threadLocalMap = getThreadLocalMap(Thread.currentThread());
    
                    threadLocals[1] = null;
                    threadLocals[8] = null;
                    //threadLocals[6] = null;
                    //threadLocals[4] = null;
                    //threadLocals[2] = null;
                    forceGC();
    
                    Utils.println("print threadLocalMap after gc");
                    printThreadLocalMap(threadLocalMap);
    
                    ThreadLocal<String> newThreadLocal1 = new ThreadLocal<>();
                    newThreadLocal1.set("newThreadLocal1-value");
                    Utils.println("print threadLocalMap after call a new newThreadLocal1.get");
                    printThreadLocalMap(threadLocalMap);
    
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    e.printStackTrace();
                }
    
            }, "thread1");
    
            thread1.start();
            thread1.join();
            Utils.println("------------------------------------------");
        }
    

    我們先創建了9個threadLocal對象並設置了值,然後去掉了其中2個的強引用(注意這2個可不是隨意挑選的)。
    gc后再添加一個新的threadLocal,最後打印出最新的map。輸出為:

    ----------testExpungeStaleEntries----------
    print threadLocalMap after gc
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 16
    --------table[0] -> null
    --------table[1] -> entry key = null, value = threadLocal1-value
    --------table[2] -> entry key = null, value = threadLocal8-value
    --------table[3] -> null
    --------table[4] -> entry key = java.lang.ThreadLocal@60523912, value = threadLocal6-value
    --------table[5] -> null
    --------table[6] -> entry key = java.lang.ThreadLocal@48fccd7a, value = threadLocal4-value
    --------table[7] -> null
    --------table[8] -> entry key = java.lang.ThreadLocal@188bbe72, value = threadLocal2-value
    --------table[9] -> null
    --------table[10] -> entry key = java.lang.ThreadLocal@19e0ebe8, value = threadLocal0-value
    --------table[11] -> entry key = java.lang.ThreadLocal@688bcb6f, value = threadLocal7-value
    --------table[12] -> null
    --------table[13] -> entry key = java.lang.ThreadLocal@46324c19, value = threadLocal5-value
    --------table[14] -> null
    --------table[15] -> entry key = java.lang.ThreadLocal@38f1283, value = threadLocal3-value
    print threadLocalMap after call a new newThreadLocal1.get
    thread1
    ----threadLocals (ThreadLocalMap), table.length = 32
    --------table[0] -> null
    --------table[1] -> null
    --------table[2] -> null
    --------table[3] -> null
    --------table[4] -> null
    --------table[5] -> null
    --------table[6] -> entry key = java.lang.ThreadLocal@48fccd7a, value = threadLocal4-value
    --------table[7] -> null
    --------table[8] -> null
    --------table[9] -> entry key = java.lang.ThreadLocal@1dae16b1, value = newThreadLocal1-value
    --------table[10] -> entry key = java.lang.ThreadLocal@19e0ebe8, value = threadLocal0-value
    --------table[11] -> null
    --------table[12] -> null
    --------table[13] -> entry key = java.lang.ThreadLocal@46324c19, value = threadLocal5-value
    --------table[14] -> null
    --------table[15] -> null
    --------table[16] -> null
    --------table[17] -> null
    --------table[18] -> null
    --------table[19] -> null
    --------table[20] -> entry key = java.lang.ThreadLocal@60523912, value = threadLocal6-value
    --------table[21] -> null
    --------table[22] -> null
    --------table[23] -> null
    --------table[24] -> entry key = java.lang.ThreadLocal@188bbe72, value = threadLocal2-value
    --------table[25] -> null
    --------table[26] -> null
    --------table[27] -> entry key = java.lang.ThreadLocal@688bcb6f, value = threadLocal7-value
    --------table[28] -> null
    --------table[29] -> null
    --------table[30] -> null
    --------table[31] -> entry key = java.lang.ThreadLocal@38f1283, value = threadLocal3-value
    ------------------------------------------

    從結果上看:

    • gc后table[1]和table[2](即threadLocal1和threadLocal8)的key變成了null
    • 加入新的threadLocal后,table的長度從16變成了32(因為此時的size是8,正好等於10 – 10/4,所以擴容),並且threadLocal1和threadLocal8這兩個entry不見了。

    如果在gc前,我們把threadLocals[1、8、6、4、2]都去掉強引用,加入新threadLocal後會發現1、8、6、4、2被清除了,但沒有擴容,因為此時size是5,小於10-10/4。這個邏輯就不貼測試結果了,你可以取消註釋上面代碼中相關的邏輯試試。

    大部分場景下,ThreadLocal對象的生命周期是和app一致的,弱引用形同虛設

    回到現實中。

    我們用ThreadLocal的目的,無非是在跨方法調用時更方便的線程安全地存儲和使用變量。這就意味着ThreadLocal的生命周期很長,甚至和app是一起存活的,強引用一直在。

    既然強引用一直存在,那麼弱引用就形同虛設了。

    所以在確定不再需要ThreadLocal中的值的情況下,還是老老實實的調用remove方法吧!

    代碼地址

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

  • 【故障公告】數據庫服務器 CPU 近 100% 引發的故障(源於 .NET Core 3.0 的一個 bug),雲計算之路-阿里雲上:數據庫連接數過萬的真相,從阿里雲RDS到微軟.NET Core

    【故障公告】數據庫服務器 CPU 近 100% 引發的故障(源於 .NET Core 3.0 的一個 bug),雲計算之路-阿里雲上:數據庫連接數過萬的真相,從阿里雲RDS到微軟.NET Core

    非常抱歉,這次故障給您帶來麻煩了,請您諒解。

    今天早上 10:54 左右,我們所使用的數據庫服務(阿里雲 RDS 實例 SQL Server 2016 標準版)CPU 突然飆升至 90% 以上,應用日誌中出現大量數據庫查詢超時的錯誤。

    Microsoft.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
     ---> System.ComponentModel.Win32Exception (258): Unknown error 258

    我們收到告警通知並確認問題后,在 11:06 啟動了阿里雲 RDS 的主備切換, 11:08 完成切換,數據庫 CPU 恢復正常。但是關鍵時候 docker swarm 總是雪上加霜,在數據庫恢復正常后,部署博客站點的 docker swarm 集群有一個節點出現異常情況,部分請求會出現 50x 錯誤,將這個異常節點退出集群並啟動新的節點后在 11:15 左右才恢復正常。

    通過阿里雲 RDS 控制台的 CloudDBA 發現了 CPU 近 100% 期間執行次數異常多的 SQL 語句。

    SELECT TOP @__p_1 [b].[TagName] AS [Name], [b].[TagID] AS [Id], [b].[UseCount], [b].[BlogId]
    FROM [blog_Tag] [b]
    WHERE [b].[BlogId] = @__blogId_0
        AND @__blogId_0 IS NOT NULL
        AND [b].[UseCount] > ?
    ORDER BY [b].[UseCount] DESC

    上面的 SQL 語句是 EF Core 3.0 生成的,其中加粗的  IS NOT NULL  就是 EF Core 3.0 的一個臭名還沒昭著的 bug —— 生成 SQL 語句時會生成額外的  IS NOT NULL  查詢條件。

    誰也沒想到(連微軟自己也沒想到)這個看似無傷大雅的多此一舉卻存在致命隱患 —— 在某些情況下會讓整個數據庫服務器 CPU 持續 100% (或者近 100%)。一開始遇到這個問題時,我們也沒想到,還因此錯怪了阿里雲(),後來在阿里雲數據庫專家分析了我們遇到的問題后才發現原來罪魁禍首是 EF Core 生成的多餘的 “IS NOT NULL” ,它會在某些情況下會造成 SQL Server 緩存了性能極其低下(很耗CPU)的執行計劃,然後後續的查詢都走這個執行計劃,CPU 就會居高不下。這個錯誤的執行計劃有雙重殺傷力,一邊巨耗數據庫 CPU ,一邊造成對應的查詢無法正常完成從而查詢結果不能被緩存到 memcached ,於是針對這個執行計劃的查詢就越多,雪崩效應就發生了。唯一的解決方法就是清除這個錯誤的執行計劃緩存,主備切換或者重啟服務器只是清除執行計劃緩存的一種簡單粗暴的方法。

    在我們開始遇到這個問題,就已經有人在 github 上了這個問題:

    Yeah this needs to be fixed asap. We just deployed code that uses 3.0 and had to immediately revert to 2.2 because simple queries blew up our SQL Azure CPU usage. Went from under 50% to 100% and stayed there until we rolled back.

    但當時沒有引起微軟的足夠重視,在我們知道錯怪了阿里雲實際是微軟的問題之後,我們向微軟 .NET 團隊反饋了這個問題,這次得到了微軟的重視,很快就修復了,但是是通過 .NET Core 3.0 Preview 版發布的,我們在非生產環境下驗證了  IS NOT NULL 的確修復了,由於是 Preview 版,再加上 .NET Core 3.1 正式版年底前會發布,所以我們沒有在生產環境中更新這個修復,只是將上次出現問題的複雜 SQL 語句改為用 Dapper 調用存儲過程。後來阿里雲數據庫專家進一步對我們的數據庫進行分析,連平時數據庫 CPU 的毛刺(偶爾跑高的波動)都與  IS NOT NULL  有關。

    這就是這次故障的背景,在我們等待 .NET Core 3.1 正式版修復這個 bug 的過程中又被坑了一次,與上次不同的是這次出現問題的 SQL 語句非常簡單,而且只有一個 “IS NOT NULL” ,由此可見這個坑的殺傷力。

    這個坑足以載入 .NET Core 的史冊,另一個讓我們記憶猶新的那次也讓我們錯怪阿里雲的 .NET Core 坑是正式版的 .NET Core 中 SqlClient 竟然漏寫了 Dispose ,詳見 。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

    ※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

    ※帶您來看台北網站建置台北網頁設計,各種案例分享

  • 使用C#+FFmpeg+DirectX+dxva2硬件解碼播放h264流

    使用C#+FFmpeg+DirectX+dxva2硬件解碼播放h264流

    本文門檻較高,因此行文看起來會亂一些,如果你看到某處能會心一笑請馬上聯繫我開始擺龍門陣
    如果你跟隨這篇文章實現了播放器,那你會得到一個高效率,低cpu佔用(單路720p視頻解碼播放佔用1%左右cpu),且代碼和引用精簡(無其他託管和非託管的dll依賴,更無需安裝任何插件,你的程序完全綠色運行);並且如果硬解不可用,切換到軟件是自動過程

      首先需要準備好visual studio/msys2/ffmpeg源碼/dx9sdk。因為我們要自己編譯ffmpeg,並且是改動代碼后編譯,ffmpeg我們編譯時會裁剪。

    • ffmpeg源碼大家使用4.2.1,和我保持同步,這樣比較好對應,下載地址為
    • msys2安裝好后不需要裝mingw和其他東西,只需要安裝make(見下方圖片;我們編譯工具鏈會用msvc而非mingw-gcc)
    • visual studio版本按道理是不需要新版本的,應該是2008-2019都可以(不過還是得看看ffmpeg代碼里是否用了c99 c11等低版本不支持的東西),vs需要安裝c++和c#的模塊(見下方圖片;應該也不需要特意去打開什麼功能)
    • dx9的sdk理論上是不用安裝的(如果你是高手,可以用c#的ilgenerator直接寫calli;亦或者寫unsafe代碼直接進行內存call,文章最後我會為大家揭秘如何用c#調用c++甚至com組件)。我用了directx的managecode,由官方為我們做了dx的調用(見下方圖片)

      第二步是修改ffmpeg源碼並編譯,我們要修改的源碼只有一個文件的十餘行,而且是增量修改。

    修改的文件位於libavutil/hwcontext_dxva2.c文件,我先將修改部分貼出來然後再給大家解釋

    hwcontext_dxva2.c修改部分


    static int dxva2_device_create9_extend(AVHWDeviceContext ctx, UINT adapter, HWND hWnd)
    {
    DXVA2DevicePriv
    priv = ctx->user_opaque;
    D3DPRESENT_PARAMETERS d3dpp = {0};
    D3DDISPLAYMODE d3ddm;
    HRESULT hr;
    pDirect3DCreate9 createD3D = (pDirect3DCreate9 )dlsym(priv->d3dlib, "Direct3DCreate9");
    if (!createD3D) {
    av_log(ctx, AV_LOG_ERROR, "Failed to locate Direct3DCreate9\n");
    return AVERROR_UNKNOWN;
    }

    priv->d3d9 = createD3D(D3D_SDK_VERSION);
    if (!priv->d3d9) {
        av_log(ctx, AV_LOG_ERROR, "Failed to create IDirect3D object\n");
        return AVERROR_UNKNOWN;
    }
    
    IDirect3D9_GetAdapterDisplayMode(priv->d3d9, adapter, &d3ddm);
    
    d3dpp.BackBufferFormat = d3ddm.Format;
    d3dpp.Windowed = TRUE;           // 是否窗口显示   
    d3dpp.hDeviceWindow = hWnd;    // 显示窗口句柄
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // 交換鏈設置,後台緩衝使用后直接丟棄
    d3dpp.Flags = D3DPRESENTFLAG_VIDEO;          // 附加特性,显示視頻
    
    DWORD behaviorFlags = D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE;
    D3DDEVTYPE devType = D3DDEVTYPE_HAL;
    D3DCAPS9 caps;
    
    if (IDirect3D9_GetDeviceCaps(priv->d3d9, D3DADAPTER_DEFAULT, devType, &caps) >= 0)
    {
        if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
        {
            behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
        }
        else
        {
            behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
        }
    }
    
    if(!hWnd)
        hWnd = GetDesktopWindow();
    hr = IDirect3D9_CreateDevice(priv->d3d9, adapter, D3DDEVTYPE_HAL, hWnd,
                                 behaviorFlags,
                                 &d3dpp, &priv->d3d9device);
    if (FAILED(hr)) {
        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device\n");
        return AVERROR_UNKNOWN;
    }
    
    return 0;

    }

    static int dxva2_device_create(AVHWDeviceContext ctx, const char device,
    AVDictionary opts, int flags)
    {
    AVDXVA2DeviceContext
    hwctx = ctx->hwctx;
    DXVA2DevicePriv priv;
    pCreateDeviceManager9
    createDeviceManager = NULL;
    unsigned resetToken = 0;
    UINT adapter = D3DADAPTER_DEFAULT;
    HRESULT hr;
    int err;
    AVDictionaryEntry *t = NULL;
    HWND hWnd = NULL;

    if (device)
        adapter = atoi(device);
    
    priv = av_mallocz(sizeof(*priv));
    if (!priv)
        return AVERROR(ENOMEM);
    
    ctx->user_opaque = priv;
    ctx->free        = dxva2_device_free;
    
    priv->device_handle = INVALID_HANDLE_VALUE;
    
    priv->d3dlib = dlopen("d3d9.dll", 0);
    if (!priv->d3dlib) {
        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D9 library\n");
        return AVERROR_UNKNOWN;
    }
    priv->dxva2lib = dlopen("dxva2.dll", 0);
    if (!priv->dxva2lib) {
        av_log(ctx, AV_LOG_ERROR, "Failed to load DXVA2 library\n");
        return AVERROR_UNKNOWN;
    }
    
    createDeviceManager = (pCreateDeviceManager9 *)dlsym(priv->dxva2lib,
                                                         "DXVA2CreateDirect3DDeviceManager9");
    if (!createDeviceManager) {
        av_log(ctx, AV_LOG_ERROR, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n");
        return AVERROR_UNKNOWN;
    }
    
    t = av_dict_get(opts, "hWnd", NULL, 0);
    if(t) {
        hWnd = (HWND)atoi(t->value);
    }
    if(hWnd) {
        if((err = dxva2_device_create9_extend(ctx, adapter, hWnd)) < 0)
            return err;
    } else {
        if (dxva2_device_create9ex(ctx, adapter) < 0) {
            // Retry with "classic" d3d9
            err = dxva2_device_create9(ctx, adapter);
            if (err < 0)
                return err;
        }
    }
    
    hr = createDeviceManager(&resetToken, &hwctx->devmgr);
    if (FAILED(hr)) {
        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device manager\n");
        return AVERROR_UNKNOWN;
    }
    
    hr = IDirect3DDeviceManager9_ResetDevice(hwctx->devmgr, priv->d3d9device, resetToken);
    if (FAILED(hr)) {
        av_log(ctx, AV_LOG_ERROR, "Failed to bind Direct3D device to device manager\n");
        return AVERROR_UNKNOWN;
    }
    
    hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &priv->device_handle);
    if (FAILED(hr)) {
        av_log(ctx, AV_LOG_ERROR, "Failed to open device handle\n");
        return AVERROR_UNKNOWN;
    }
    
    return 0;

    }

      代碼中dxva2_device_create9_extend函數是我新加入的,並且在dxva2_device_create函數(這個函數是ffmpeg原始流程中的,我的改動不影響原本任何功能)中適時調用;簡單來說,原來的ffmpeg也能基於dxva2硬件解碼,但是它沒法將解碼得到的surface用於前台播放,因為它創建device時並未指定窗口和其他相關參數,大家可以參考我代碼實現,我將窗口句柄傳入后創建過程完全改變(其他人如果使用我們編譯的代碼,他沒有傳入窗口句柄,就執行原來的創建,因此百分百兼容)。

      (ps:在這裏我講一下網絡上另外一種寫法(兩年前我也用的他們的,因為沒時間詳細看ffmpeg源碼),他們是在外面創建的device和surface然後想辦法傳到ffmpeg內部進行替換,這樣做有好處,就是不用自己修改和編譯ffmpeg,壞處是得自己維護device和surface。至於二進制兼容方面考慮,兩種做法都不是太好)

    代碼修改完成后我們使用msys2編譯

    • 首先是需要把編譯器設置為msvc,這個步驟通過使用vs的命令行工具即可,如下圖
    • 然後是設置msys2繼承環境變量(這樣make時才能找到cl/link)

    • 打開msys,查看變量是否正確
    • 編譯ffmpeg
    ./configure --enable-shared --enable-small --disable-all --disable-autodetect --enable-avcodec --enable-decoder=h264 --enable-dxva2 --enable-hwaccel=h264_dxva2 --toolchain=msvc --prefix=host
    make && make install

    編譯完成後頭文件和dll在host文件夾內(編譯產出的dll也是clear的,不依賴msvc**.dll)

      在C#中使用我們產出的方式需要使用p/invoke和unsafe代碼。

    我先貼出我針對ffmpeg寫的一個工具類,然後給大家稍微講解一下

    FFHelper.cs

    
    using System;
    using System.Runtime.InteropServices;
    
    namespace MultiPlayer
    {
        public enum AVCodecID
        {
            AV_CODEC_ID_NONE,
    
            /* video codecs */
            AV_CODEC_ID_MPEG1VIDEO,
            AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding
            AV_CODEC_ID_H261,
            AV_CODEC_ID_H263,
            AV_CODEC_ID_RV10,
            AV_CODEC_ID_RV20,
            AV_CODEC_ID_MJPEG,
            AV_CODEC_ID_MJPEGB,
            AV_CODEC_ID_LJPEG,
            AV_CODEC_ID_SP5X,
            AV_CODEC_ID_JPEGLS,
            AV_CODEC_ID_MPEG4,
            AV_CODEC_ID_RAWVIDEO,
            AV_CODEC_ID_MSMPEG4V1,
            AV_CODEC_ID_MSMPEG4V2,
            AV_CODEC_ID_MSMPEG4V3,
            AV_CODEC_ID_WMV1,
            AV_CODEC_ID_WMV2,
            AV_CODEC_ID_H263P,
            AV_CODEC_ID_H263I,
            AV_CODEC_ID_FLV1,
            AV_CODEC_ID_SVQ1,
            AV_CODEC_ID_SVQ3,
            AV_CODEC_ID_DVVIDEO,
            AV_CODEC_ID_HUFFYUV,
            AV_CODEC_ID_CYUV,
            AV_CODEC_ID_H264,
            AV_CODEC_ID_INDEO3,
            AV_CODEC_ID_VP3,
            AV_CODEC_ID_THEORA,
            AV_CODEC_ID_ASV1,
            AV_CODEC_ID_ASV2,
            AV_CODEC_ID_FFV1,
            AV_CODEC_ID_4XM,
            AV_CODEC_ID_VCR1,
            AV_CODEC_ID_CLJR,
            AV_CODEC_ID_MDEC,
            AV_CODEC_ID_ROQ,
            AV_CODEC_ID_INTERPLAY_VIDEO,
            AV_CODEC_ID_XAN_WC3,
            AV_CODEC_ID_XAN_WC4,
            AV_CODEC_ID_RPZA,
            AV_CODEC_ID_CINEPAK,
            AV_CODEC_ID_WS_VQA,
            AV_CODEC_ID_MSRLE,
            AV_CODEC_ID_MSVIDEO1,
            AV_CODEC_ID_IDCIN,
            AV_CODEC_ID_8BPS,
            AV_CODEC_ID_SMC,
            AV_CODEC_ID_FLIC,
            AV_CODEC_ID_TRUEMOTION1,
            AV_CODEC_ID_VMDVIDEO,
            AV_CODEC_ID_MSZH,
            AV_CODEC_ID_ZLIB,
            AV_CODEC_ID_QTRLE,
            AV_CODEC_ID_TSCC,
            AV_CODEC_ID_ULTI,
            AV_CODEC_ID_QDRAW,
            AV_CODEC_ID_VIXL,
            AV_CODEC_ID_QPEG,
            AV_CODEC_ID_PNG,
            AV_CODEC_ID_PPM,
            AV_CODEC_ID_PBM,
            AV_CODEC_ID_PGM,
            AV_CODEC_ID_PGMYUV,
            AV_CODEC_ID_PAM,
            AV_CODEC_ID_FFVHUFF,
            AV_CODEC_ID_RV30,
            AV_CODEC_ID_RV40,
            AV_CODEC_ID_VC1,
            AV_CODEC_ID_WMV3,
            AV_CODEC_ID_LOCO,
            AV_CODEC_ID_WNV1,
            AV_CODEC_ID_AASC,
            AV_CODEC_ID_INDEO2,
            AV_CODEC_ID_FRAPS,
            AV_CODEC_ID_TRUEMOTION2,
            AV_CODEC_ID_BMP,
            AV_CODEC_ID_CSCD,
            AV_CODEC_ID_MMVIDEO,
            AV_CODEC_ID_ZMBV,
            AV_CODEC_ID_AVS,
            AV_CODEC_ID_SMACKVIDEO,
            AV_CODEC_ID_NUV,
            AV_CODEC_ID_KMVC,
            AV_CODEC_ID_FLASHSV,
            AV_CODEC_ID_CAVS,
            AV_CODEC_ID_JPEG2000,
            AV_CODEC_ID_VMNC,
            AV_CODEC_ID_VP5,
            AV_CODEC_ID_VP6,
            AV_CODEC_ID_VP6F,
            AV_CODEC_ID_TARGA,
            AV_CODEC_ID_DSICINVIDEO,
            AV_CODEC_ID_TIERTEXSEQVIDEO,
            AV_CODEC_ID_TIFF,
            AV_CODEC_ID_GIF,
            AV_CODEC_ID_DXA,
            AV_CODEC_ID_DNXHD,
            AV_CODEC_ID_THP,
            AV_CODEC_ID_SGI,
            AV_CODEC_ID_C93,
            AV_CODEC_ID_BETHSOFTVID,
            AV_CODEC_ID_PTX,
            AV_CODEC_ID_TXD,
            AV_CODEC_ID_VP6A,
            AV_CODEC_ID_AMV,
            AV_CODEC_ID_VB,
            AV_CODEC_ID_PCX,
            AV_CODEC_ID_SUNRAST,
            AV_CODEC_ID_INDEO4,
            AV_CODEC_ID_INDEO5,
            AV_CODEC_ID_MIMIC,
            AV_CODEC_ID_RL2,
            AV_CODEC_ID_ESCAPE124,
            AV_CODEC_ID_DIRAC,
            AV_CODEC_ID_BFI,
            AV_CODEC_ID_CMV,
            AV_CODEC_ID_MOTIONPIXELS,
            AV_CODEC_ID_TGV,
            AV_CODEC_ID_TGQ,
            AV_CODEC_ID_TQI,
            AV_CODEC_ID_AURA,
            AV_CODEC_ID_AURA2,
            AV_CODEC_ID_V210X,
            AV_CODEC_ID_TMV,
            AV_CODEC_ID_V210,
            AV_CODEC_ID_DPX,
            AV_CODEC_ID_MAD,
            AV_CODEC_ID_FRWU,
            AV_CODEC_ID_FLASHSV2,
            AV_CODEC_ID_CDGRAPHICS,
            AV_CODEC_ID_R210,
            AV_CODEC_ID_ANM,
            AV_CODEC_ID_BINKVIDEO,
            AV_CODEC_ID_IFF_ILBM,
            //#define AV_CODEC_ID_IFF_BYTERUN1 AV_CODEC_ID_IFF_ILBM
            AV_CODEC_ID_KGV1,
            AV_CODEC_ID_YOP,
            AV_CODEC_ID_VP8,
            AV_CODEC_ID_PICTOR,
            AV_CODEC_ID_ANSI,
            AV_CODEC_ID_A64_MULTI,
            AV_CODEC_ID_A64_MULTI5,
            AV_CODEC_ID_R10K,
            AV_CODEC_ID_MXPEG,
            AV_CODEC_ID_LAGARITH,
            AV_CODEC_ID_PRORES,
            AV_CODEC_ID_JV,
            AV_CODEC_ID_DFA,
            AV_CODEC_ID_WMV3IMAGE,
            AV_CODEC_ID_VC1IMAGE,
            AV_CODEC_ID_UTVIDEO,
            AV_CODEC_ID_BMV_VIDEO,
            AV_CODEC_ID_VBLE,
            AV_CODEC_ID_DXTORY,
            AV_CODEC_ID_V410,
            AV_CODEC_ID_XWD,
            AV_CODEC_ID_CDXL,
            AV_CODEC_ID_XBM,
            AV_CODEC_ID_ZEROCODEC,
            AV_CODEC_ID_MSS1,
            AV_CODEC_ID_MSA1,
            AV_CODEC_ID_TSCC2,
            AV_CODEC_ID_MTS2,
            AV_CODEC_ID_CLLC,
            AV_CODEC_ID_MSS2,
            AV_CODEC_ID_VP9,
            AV_CODEC_ID_AIC,
            AV_CODEC_ID_ESCAPE130,
            AV_CODEC_ID_G2M,
            AV_CODEC_ID_WEBP,
            AV_CODEC_ID_HNM4_VIDEO,
            AV_CODEC_ID_HEVC,
            //#define AV_CODEC_ID_H265 AV_CODEC_ID_HEVC
            AV_CODEC_ID_FIC,
            AV_CODEC_ID_ALIAS_PIX,
            AV_CODEC_ID_BRENDER_PIX,
            AV_CODEC_ID_PAF_VIDEO,
            AV_CODEC_ID_EXR,
            AV_CODEC_ID_VP7,
            AV_CODEC_ID_SANM,
            AV_CODEC_ID_SGIRLE,
            AV_CODEC_ID_MVC1,
            AV_CODEC_ID_MVC2,
            AV_CODEC_ID_HQX,
            AV_CODEC_ID_TDSC,
            AV_CODEC_ID_HQ_HQA,
            AV_CODEC_ID_HAP,
            AV_CODEC_ID_DDS,
            AV_CODEC_ID_DXV,
            AV_CODEC_ID_SCREENPRESSO,
            AV_CODEC_ID_RSCC,
            AV_CODEC_ID_AVS2,
    
            AV_CODEC_ID_Y41P = 0x8000,
            AV_CODEC_ID_AVRP,
            AV_CODEC_ID_012V,
            AV_CODEC_ID_AVUI,
            AV_CODEC_ID_AYUV,
            AV_CODEC_ID_TARGA_Y216,
            AV_CODEC_ID_V308,
            AV_CODEC_ID_V408,
            AV_CODEC_ID_YUV4,
            AV_CODEC_ID_AVRN,
            AV_CODEC_ID_CPIA,
            AV_CODEC_ID_XFACE,
            AV_CODEC_ID_SNOW,
            AV_CODEC_ID_SMVJPEG,
            AV_CODEC_ID_APNG,
            AV_CODEC_ID_DAALA,
            AV_CODEC_ID_CFHD,
            AV_CODEC_ID_TRUEMOTION2RT,
            AV_CODEC_ID_M101,
            AV_CODEC_ID_MAGICYUV,
            AV_CODEC_ID_SHEERVIDEO,
            AV_CODEC_ID_YLC,
            AV_CODEC_ID_PSD,
            AV_CODEC_ID_PIXLET,
            AV_CODEC_ID_SPEEDHQ,
            AV_CODEC_ID_FMVC,
            AV_CODEC_ID_SCPR,
            AV_CODEC_ID_CLEARVIDEO,
            AV_CODEC_ID_XPM,
            AV_CODEC_ID_AV1,
            AV_CODEC_ID_BITPACKED,
            AV_CODEC_ID_MSCC,
            AV_CODEC_ID_SRGC,
            AV_CODEC_ID_SVG,
            AV_CODEC_ID_GDV,
            AV_CODEC_ID_FITS,
            AV_CODEC_ID_IMM4,
            AV_CODEC_ID_PROSUMER,
            AV_CODEC_ID_MWSC,
            AV_CODEC_ID_WCMV,
            AV_CODEC_ID_RASC,
            AV_CODEC_ID_HYMT,
            AV_CODEC_ID_ARBC,
            AV_CODEC_ID_AGM,
            AV_CODEC_ID_LSCR,
            AV_CODEC_ID_VP4,
    
            /* various PCM "codecs" */
            AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
            AV_CODEC_ID_PCM_S16LE = 0x10000,
            AV_CODEC_ID_PCM_S16BE,
            AV_CODEC_ID_PCM_U16LE,
            AV_CODEC_ID_PCM_U16BE,
            AV_CODEC_ID_PCM_S8,
            AV_CODEC_ID_PCM_U8,
            AV_CODEC_ID_PCM_MULAW,
            AV_CODEC_ID_PCM_ALAW,
            AV_CODEC_ID_PCM_S32LE,
            AV_CODEC_ID_PCM_S32BE,
            AV_CODEC_ID_PCM_U32LE,
            AV_CODEC_ID_PCM_U32BE,
            AV_CODEC_ID_PCM_S24LE,
            AV_CODEC_ID_PCM_S24BE,
            AV_CODEC_ID_PCM_U24LE,
            AV_CODEC_ID_PCM_U24BE,
            AV_CODEC_ID_PCM_S24DAUD,
            AV_CODEC_ID_PCM_ZORK,
            AV_CODEC_ID_PCM_S16LE_PLANAR,
            AV_CODEC_ID_PCM_DVD,
            AV_CODEC_ID_PCM_F32BE,
            AV_CODEC_ID_PCM_F32LE,
            AV_CODEC_ID_PCM_F64BE,
            AV_CODEC_ID_PCM_F64LE,
            AV_CODEC_ID_PCM_BLURAY,
            AV_CODEC_ID_PCM_LXF,
            AV_CODEC_ID_S302M,
            AV_CODEC_ID_PCM_S8_PLANAR,
            AV_CODEC_ID_PCM_S24LE_PLANAR,
            AV_CODEC_ID_PCM_S32LE_PLANAR,
            AV_CODEC_ID_PCM_S16BE_PLANAR,
    
            AV_CODEC_ID_PCM_S64LE = 0x10800,
            AV_CODEC_ID_PCM_S64BE,
            AV_CODEC_ID_PCM_F16LE,
            AV_CODEC_ID_PCM_F24LE,
            AV_CODEC_ID_PCM_VIDC,
    
            /* various ADPCM codecs */
            AV_CODEC_ID_ADPCM_IMA_QT = 0x11000,
            AV_CODEC_ID_ADPCM_IMA_WAV,
            AV_CODEC_ID_ADPCM_IMA_DK3,
            AV_CODEC_ID_ADPCM_IMA_DK4,
            AV_CODEC_ID_ADPCM_IMA_WS,
            AV_CODEC_ID_ADPCM_IMA_SMJPEG,
            AV_CODEC_ID_ADPCM_MS,
            AV_CODEC_ID_ADPCM_4XM,
            AV_CODEC_ID_ADPCM_XA,
            AV_CODEC_ID_ADPCM_ADX,
            AV_CODEC_ID_ADPCM_EA,
            AV_CODEC_ID_ADPCM_G726,
            AV_CODEC_ID_ADPCM_CT,
            AV_CODEC_ID_ADPCM_SWF,
            AV_CODEC_ID_ADPCM_YAMAHA,
            AV_CODEC_ID_ADPCM_SBPRO_4,
            AV_CODEC_ID_ADPCM_SBPRO_3,
            AV_CODEC_ID_ADPCM_SBPRO_2,
            AV_CODEC_ID_ADPCM_THP,
            AV_CODEC_ID_ADPCM_IMA_AMV,
            AV_CODEC_ID_ADPCM_EA_R1,
            AV_CODEC_ID_ADPCM_EA_R3,
            AV_CODEC_ID_ADPCM_EA_R2,
            AV_CODEC_ID_ADPCM_IMA_EA_SEAD,
            AV_CODEC_ID_ADPCM_IMA_EA_EACS,
            AV_CODEC_ID_ADPCM_EA_XAS,
            AV_CODEC_ID_ADPCM_EA_MAXIS_XA,
            AV_CODEC_ID_ADPCM_IMA_ISS,
            AV_CODEC_ID_ADPCM_G722,
            AV_CODEC_ID_ADPCM_IMA_APC,
            AV_CODEC_ID_ADPCM_VIMA,
    
            AV_CODEC_ID_ADPCM_AFC = 0x11800,
            AV_CODEC_ID_ADPCM_IMA_OKI,
            AV_CODEC_ID_ADPCM_DTK,
            AV_CODEC_ID_ADPCM_IMA_RAD,
            AV_CODEC_ID_ADPCM_G726LE,
            AV_CODEC_ID_ADPCM_THP_LE,
            AV_CODEC_ID_ADPCM_PSX,
            AV_CODEC_ID_ADPCM_AICA,
            AV_CODEC_ID_ADPCM_IMA_DAT4,
            AV_CODEC_ID_ADPCM_MTAF,
            AV_CODEC_ID_ADPCM_AGM,
    
            /* AMR */
            AV_CODEC_ID_AMR_NB = 0x12000,
            AV_CODEC_ID_AMR_WB,
    
            /* RealAudio codecs*/
            AV_CODEC_ID_RA_144 = 0x13000,
            AV_CODEC_ID_RA_288,
    
            /* various DPCM codecs */
            AV_CODEC_ID_ROQ_DPCM = 0x14000,
            AV_CODEC_ID_INTERPLAY_DPCM,
            AV_CODEC_ID_XAN_DPCM,
            AV_CODEC_ID_SOL_DPCM,
    
            AV_CODEC_ID_SDX2_DPCM = 0x14800,
            AV_CODEC_ID_GREMLIN_DPCM,
    
            /* audio codecs */
            AV_CODEC_ID_MP2 = 0x15000,
            AV_CODEC_ID_MP3, ///< preferred ID for decoding MPEG audio layer 1, 2 or 3
            AV_CODEC_ID_AAC,
            AV_CODEC_ID_AC3,
            AV_CODEC_ID_DTS,
            AV_CODEC_ID_VORBIS,
            AV_CODEC_ID_DVAUDIO,
            AV_CODEC_ID_WMAV1,
            AV_CODEC_ID_WMAV2,
            AV_CODEC_ID_MACE3,
            AV_CODEC_ID_MACE6,
            AV_CODEC_ID_VMDAUDIO,
            AV_CODEC_ID_FLAC,
            AV_CODEC_ID_MP3ADU,
            AV_CODEC_ID_MP3ON4,
            AV_CODEC_ID_SHORTEN,
            AV_CODEC_ID_ALAC,
            AV_CODEC_ID_WESTWOOD_SND1,
            AV_CODEC_ID_GSM, ///< as in Berlin toast format
            AV_CODEC_ID_QDM2,
            AV_CODEC_ID_COOK,
            AV_CODEC_ID_TRUESPEECH,
            AV_CODEC_ID_TTA,
            AV_CODEC_ID_SMACKAUDIO,
            AV_CODEC_ID_QCELP,
            AV_CODEC_ID_WAVPACK,
            AV_CODEC_ID_DSICINAUDIO,
            AV_CODEC_ID_IMC,
            AV_CODEC_ID_MUSEPACK7,
            AV_CODEC_ID_MLP,
            AV_CODEC_ID_GSM_MS, /* as found in WAV */
            AV_CODEC_ID_ATRAC3,
            AV_CODEC_ID_APE,
            AV_CODEC_ID_NELLYMOSER,
            AV_CODEC_ID_MUSEPACK8,
            AV_CODEC_ID_SPEEX,
            AV_CODEC_ID_WMAVOICE,
            AV_CODEC_ID_WMAPRO,
            AV_CODEC_ID_WMALOSSLESS,
            AV_CODEC_ID_ATRAC3P,
            AV_CODEC_ID_EAC3,
            AV_CODEC_ID_SIPR,
            AV_CODEC_ID_MP1,
            AV_CODEC_ID_TWINVQ,
            AV_CODEC_ID_TRUEHD,
            AV_CODEC_ID_MP4ALS,
            AV_CODEC_ID_ATRAC1,
            AV_CODEC_ID_BINKAUDIO_RDFT,
            AV_CODEC_ID_BINKAUDIO_DCT,
            AV_CODEC_ID_AAC_LATM,
            AV_CODEC_ID_QDMC,
            AV_CODEC_ID_CELT,
            AV_CODEC_ID_G723_1,
            AV_CODEC_ID_G729,
            AV_CODEC_ID_8SVX_EXP,
            AV_CODEC_ID_8SVX_FIB,
            AV_CODEC_ID_BMV_AUDIO,
            AV_CODEC_ID_RALF,
            AV_CODEC_ID_IAC,
            AV_CODEC_ID_ILBC,
            AV_CODEC_ID_OPUS,
            AV_CODEC_ID_COMFORT_NOISE,
            AV_CODEC_ID_TAK,
            AV_CODEC_ID_METASOUND,
            AV_CODEC_ID_PAF_AUDIO,
            AV_CODEC_ID_ON2AVC,
            AV_CODEC_ID_DSS_SP,
            AV_CODEC_ID_CODEC2,
    
            AV_CODEC_ID_FFWAVESYNTH = 0x15800,
            AV_CODEC_ID_SONIC,
            AV_CODEC_ID_SONIC_LS,
            AV_CODEC_ID_EVRC,
            AV_CODEC_ID_SMV,
            AV_CODEC_ID_DSD_LSBF,
            AV_CODEC_ID_DSD_MSBF,
            AV_CODEC_ID_DSD_LSBF_PLANAR,
            AV_CODEC_ID_DSD_MSBF_PLANAR,
            AV_CODEC_ID_4GV,
            AV_CODEC_ID_INTERPLAY_ACM,
            AV_CODEC_ID_XMA1,
            AV_CODEC_ID_XMA2,
            AV_CODEC_ID_DST,
            AV_CODEC_ID_ATRAC3AL,
            AV_CODEC_ID_ATRAC3PAL,
            AV_CODEC_ID_DOLBY_E,
            AV_CODEC_ID_APTX,
            AV_CODEC_ID_APTX_HD,
            AV_CODEC_ID_SBC,
            AV_CODEC_ID_ATRAC9,
            AV_CODEC_ID_HCOM,
    
            /* subtitle codecs */
            AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing at the start of subtitle codecs.
            AV_CODEC_ID_DVD_SUBTITLE = 0x17000,
            AV_CODEC_ID_DVB_SUBTITLE,
            AV_CODEC_ID_TEXT,  ///< raw UTF-8 text
            AV_CODEC_ID_XSUB,
            AV_CODEC_ID_SSA,
            AV_CODEC_ID_MOV_TEXT,
            AV_CODEC_ID_HDMV_PGS_SUBTITLE,
            AV_CODEC_ID_DVB_TELETEXT,
            AV_CODEC_ID_SRT,
    
            AV_CODEC_ID_MICRODVD = 0x17800,
            AV_CODEC_ID_EIA_608,
            AV_CODEC_ID_JACOSUB,
            AV_CODEC_ID_SAMI,
            AV_CODEC_ID_REALTEXT,
            AV_CODEC_ID_STL,
            AV_CODEC_ID_SUBVIEWER1,
            AV_CODEC_ID_SUBVIEWER,
            AV_CODEC_ID_SUBRIP,
            AV_CODEC_ID_WEBVTT,
            AV_CODEC_ID_MPL2,
            AV_CODEC_ID_VPLAYER,
            AV_CODEC_ID_PJS,
            AV_CODEC_ID_ASS,
            AV_CODEC_ID_HDMV_TEXT_SUBTITLE,
            AV_CODEC_ID_TTML,
            AV_CODEC_ID_ARIB_CAPTION,
    
            /* other specific kind of codecs (generally used for attachments) */
            AV_CODEC_ID_FIRST_UNKNOWN = 0x18000,           ///< A dummy ID pointing at the start of various fake codecs.
            AV_CODEC_ID_TTF = 0x18000,
    
            AV_CODEC_ID_SCTE_35, ///< Contain timestamp estimated through PCR of program stream.
            AV_CODEC_ID_BINTEXT = 0x18800,
            AV_CODEC_ID_XBIN,
            AV_CODEC_ID_IDF,
            AV_CODEC_ID_OTF,
            AV_CODEC_ID_SMPTE_KLV,
            AV_CODEC_ID_DVD_NAV,
            AV_CODEC_ID_TIMED_ID3,
            AV_CODEC_ID_BIN_DATA,
    
    
            AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it
    
            AV_CODEC_ID_MPEG2TS = 0x20000, /**< _FAKE_ codec to indicate a raw MPEG-2 TS
                                    * stream (only used by libavformat) */
            AV_CODEC_ID_MPEG4SYSTEMS = 0x20001, /**< _FAKE_ codec to indicate a MPEG-4 Systems
                                    * stream (only used by libavformat) */
            AV_CODEC_ID_FFMETADATA = 0x21000,   ///< Dummy codec for streams containing only metadata information.
            AV_CODEC_ID_WRAPPED_AVFRAME = 0x21001, ///< Passthrough codec, AVFrames wrapped in AVPacket
        }
    
        public enum AVHWDeviceType
        {
            AV_HWDEVICE_TYPE_NONE,
            AV_HWDEVICE_TYPE_VDPAU,
            AV_HWDEVICE_TYPE_CUDA,
            AV_HWDEVICE_TYPE_VAAPI,
            AV_HWDEVICE_TYPE_DXVA2,
            AV_HWDEVICE_TYPE_QSV,
            AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
            AV_HWDEVICE_TYPE_D3D11VA,
            AV_HWDEVICE_TYPE_DRM,
            AV_HWDEVICE_TYPE_OPENCL,
            AV_HWDEVICE_TYPE_MEDIACODEC,
        }
    
        public enum AVPixelFormat
        {
            AV_PIX_FMT_NONE = -1,
            AV_PIX_FMT_YUV420P,   ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
            AV_PIX_FMT_YUYV422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
            AV_PIX_FMT_RGB24,     ///< packed RGB 8:8:8, 24bpp, RGBRGB...
            AV_PIX_FMT_BGR24,     ///< packed RGB 8:8:8, 24bpp, BGRBGR...
            AV_PIX_FMT_YUV422P,   ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
            AV_PIX_FMT_YUV444P,   ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
            AV_PIX_FMT_YUV410P,   ///< planar YUV 4:1:0,  9bpp, (1 Cr & Cb sample per 4x4 Y samples)
            AV_PIX_FMT_YUV411P,   ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
            AV_PIX_FMT_GRAY8,     ///<        Y        ,  8bpp
            AV_PIX_FMT_MONOWHITE, ///<        Y        ,  1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb
            AV_PIX_FMT_MONOBLACK, ///<        Y        ,  1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb
            AV_PIX_FMT_PAL8,      ///< 8 bits with AV_PIX_FMT_RGB32 palette
            AV_PIX_FMT_YUVJ420P,  ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range
            AV_PIX_FMT_YUVJ422P,  ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range
            AV_PIX_FMT_YUVJ444P,  ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range
            AV_PIX_FMT_UYVY422,   ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
            AV_PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3
            AV_PIX_FMT_BGR8,      ///< packed RGB 3:3:2,  8bpp, (msb)2B 3G 3R(lsb)
            AV_PIX_FMT_BGR4,      ///< packed RGB 1:2:1 bitstream,  4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits
            AV_PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1,  8bpp, (msb)1B 2G 1R(lsb)
            AV_PIX_FMT_RGB8,      ///< packed RGB 3:3:2,  8bpp, (msb)2R 3G 3B(lsb)
            AV_PIX_FMT_RGB4,      ///< packed RGB 1:2:1 bitstream,  4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits
            AV_PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1,  8bpp, (msb)1R 2G 1B(lsb)
            AV_PIX_FMT_NV12,      ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
            AV_PIX_FMT_NV21,      ///< as above, but U and V bytes are swapped
    
            AV_PIX_FMT_ARGB,      ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
            AV_PIX_FMT_RGBA,      ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
            AV_PIX_FMT_ABGR,      ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
            AV_PIX_FMT_BGRA,      ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
    
            AV_PIX_FMT_GRAY16BE,  ///<        Y        , 16bpp, big-endian
            AV_PIX_FMT_GRAY16LE,  ///<        Y        , 16bpp, little-endian
            AV_PIX_FMT_YUV440P,   ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
            AV_PIX_FMT_YUVJ440P,  ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
            AV_PIX_FMT_YUVA420P,  ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
            AV_PIX_FMT_RGB48BE,   ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian
            AV_PIX_FMT_RGB48LE,   ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian
    
            AV_PIX_FMT_RGB565BE,  ///< packed RGB 5:6:5, 16bpp, (msb)   5R 6G 5B(lsb), big-endian
            AV_PIX_FMT_RGB565LE,  ///< packed RGB 5:6:5, 16bpp, (msb)   5R 6G 5B(lsb), little-endian
            AV_PIX_FMT_RGB555BE,  ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian   , X=unused/undefined
            AV_PIX_FMT_RGB555LE,  ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined
    
            AV_PIX_FMT_BGR565BE,  ///< packed BGR 5:6:5, 16bpp, (msb)   5B 6G 5R(lsb), big-endian
            AV_PIX_FMT_BGR565LE,  ///< packed BGR 5:6:5, 16bpp, (msb)   5B 6G 5R(lsb), little-endian
            AV_PIX_FMT_BGR555BE,  ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), big-endian   , X=unused/undefined
            AV_PIX_FMT_BGR555LE,  ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), little-endian, X=unused/undefined
    
            /** @name Deprecated pixel formats */
            /**@{*/
            AV_PIX_FMT_VAAPI_MOCO, ///< HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers
            AV_PIX_FMT_VAAPI_IDCT, ///< HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers
            AV_PIX_FMT_VAAPI_VLD,  ///< HW decoding through VA API, Picture.data[3] contains a VASurfaceID
            /**@}*/
            AV_PIX_FMT_VAAPI = AV_PIX_FMT_VAAPI_VLD,
    
            AV_PIX_FMT_YUV420P16LE,  ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
            AV_PIX_FMT_YUV420P16BE,  ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
            AV_PIX_FMT_YUV422P16LE,  ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
            AV_PIX_FMT_YUV422P16BE,  ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
            AV_PIX_FMT_YUV444P16LE,  ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
            AV_PIX_FMT_YUV444P16BE,  ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
            AV_PIX_FMT_DXVA2_VLD,    ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer
    
            AV_PIX_FMT_RGB444LE,  ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), little-endian, X=unused/undefined
            AV_PIX_FMT_RGB444BE,  ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), big-endian,    X=unused/undefined
            AV_PIX_FMT_BGR444LE,  ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), little-endian, X=unused/undefined
            AV_PIX_FMT_BGR444BE,  ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), big-endian,    X=unused/undefined
            AV_PIX_FMT_YA8,       ///< 8 bits gray, 8 bits alpha
    
            AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8
            AV_PIX_FMT_GRAY8A = AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8
    
            AV_PIX_FMT_BGR48BE,   ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian
            AV_PIX_FMT_BGR48LE,   ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian
    
            /**
             * The following 12 formats have the disadvantage of needing 1 format for each bit depth.
             * Notice that each 9/10 bits sample is stored in 16 bits with extra padding.
             * If you want to support multiple bit depths, then using AV_PIX_FMT_YUV420P16* with the bpp stored separately is better.
             */
            AV_PIX_FMT_YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
            AV_PIX_FMT_YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
            AV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
            AV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
            AV_PIX_FMT_YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
            AV_PIX_FMT_YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
            AV_PIX_FMT_YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
            AV_PIX_FMT_YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
            AV_PIX_FMT_YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
            AV_PIX_FMT_YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
            AV_PIX_FMT_YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
            AV_PIX_FMT_YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
            AV_PIX_FMT_GBRP,      ///< planar GBR 4:4:4 24bpp
            AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP, // alias for #AV_PIX_FMT_GBRP
            AV_PIX_FMT_GBRP9BE,   ///< planar GBR 4:4:4 27bpp, big-endian
            AV_PIX_FMT_GBRP9LE,   ///< planar GBR 4:4:4 27bpp, little-endian
            AV_PIX_FMT_GBRP10BE,  ///< planar GBR 4:4:4 30bpp, big-endian
            AV_PIX_FMT_GBRP10LE,  ///< planar GBR 4:4:4 30bpp, little-endian
            AV_PIX_FMT_GBRP16BE,  ///< planar GBR 4:4:4 48bpp, big-endian
            AV_PIX_FMT_GBRP16LE,  ///< planar GBR 4:4:4 48bpp, little-endian
            AV_PIX_FMT_YUVA422P,  ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
            AV_PIX_FMT_YUVA444P,  ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
            AV_PIX_FMT_YUVA420P9BE,  ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), big-endian
            AV_PIX_FMT_YUVA420P9LE,  ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), little-endian
            AV_PIX_FMT_YUVA422P9BE,  ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), big-endian
            AV_PIX_FMT_YUVA422P9LE,  ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), little-endian
            AV_PIX_FMT_YUVA444P9BE,  ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), big-endian
            AV_PIX_FMT_YUVA444P9LE,  ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), little-endian
            AV_PIX_FMT_YUVA420P10BE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian)
            AV_PIX_FMT_YUVA420P10LE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian)
            AV_PIX_FMT_YUVA422P10BE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian)
            AV_PIX_FMT_YUVA422P10LE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian)
            AV_PIX_FMT_YUVA444P10BE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian)
            AV_PIX_FMT_YUVA444P10LE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian)
            AV_PIX_FMT_YUVA420P16BE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian)
            AV_PIX_FMT_YUVA420P16LE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian)
            AV_PIX_FMT_YUVA422P16BE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian)
            AV_PIX_FMT_YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian)
            AV_PIX_FMT_YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian)
            AV_PIX_FMT_YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian)
    
            AV_PIX_FMT_VDPAU,     ///< HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface
    
            AV_PIX_FMT_XYZ12LE,      ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as little-endian, the 4 lower bits are set to 0
            AV_PIX_FMT_XYZ12BE,      ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as big-endian, the 4 lower bits are set to 0
            AV_PIX_FMT_NV16,         ///< interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
            AV_PIX_FMT_NV20LE,       ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
            AV_PIX_FMT_NV20BE,       ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
    
            AV_PIX_FMT_RGBA64BE,     ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian
            AV_PIX_FMT_RGBA64LE,     ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian
            AV_PIX_FMT_BGRA64BE,     ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian
            AV_PIX_FMT_BGRA64LE,     ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian
    
            AV_PIX_FMT_YVYU422,   ///< packed YUV 4:2:2, 16bpp, Y0 Cr Y1 Cb
    
            AV_PIX_FMT_YA16BE,       ///< 16 bits gray, 16 bits alpha (big-endian)
            AV_PIX_FMT_YA16LE,       ///< 16 bits gray, 16 bits alpha (little-endian)
    
            AV_PIX_FMT_GBRAP,        ///< planar GBRA 4:4:4:4 32bpp
            AV_PIX_FMT_GBRAP16BE,    ///< planar GBRA 4:4:4:4 64bpp, big-endian
            AV_PIX_FMT_GBRAP16LE,    ///< planar GBRA 4:4:4:4 64bpp, little-endian
            /**
             *  HW acceleration through QSV, data[3] contains a pointer to the
             *  mfxFrameSurface1 structure.
             */
            AV_PIX_FMT_QSV,
            /**
             * HW acceleration though MMAL, data[3] contains a pointer to the
             * MMAL_BUFFER_HEADER_T structure.
             */
            AV_PIX_FMT_MMAL,
    
            AV_PIX_FMT_D3D11VA_VLD,  ///< HW decoding through Direct3D11 via old API, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer
    
            /**
             * HW acceleration through CUDA. data[i] contain CUdeviceptr pointers
             * exactly as for system memory frames.
             */
            AV_PIX_FMT_CUDA,
    
            AV_PIX_FMT_0RGB,        ///< packed RGB 8:8:8, 32bpp, XRGBXRGB...   X=unused/undefined
            AV_PIX_FMT_RGB0,        ///< packed RGB 8:8:8, 32bpp, RGBXRGBX...   X=unused/undefined
            AV_PIX_FMT_0BGR,        ///< packed BGR 8:8:8, 32bpp, XBGRXBGR...   X=unused/undefined
            AV_PIX_FMT_BGR0,        ///< packed BGR 8:8:8, 32bpp, BGRXBGRX...   X=unused/undefined
    
            AV_PIX_FMT_YUV420P12BE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
            AV_PIX_FMT_YUV420P12LE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
            AV_PIX_FMT_YUV420P14BE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
            AV_PIX_FMT_YUV420P14LE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
            AV_PIX_FMT_YUV422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
            AV_PIX_FMT_YUV422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
            AV_PIX_FMT_YUV422P14BE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
            AV_PIX_FMT_YUV422P14LE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
            AV_PIX_FMT_YUV444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
            AV_PIX_FMT_YUV444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
            AV_PIX_FMT_YUV444P14BE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
            AV_PIX_FMT_YUV444P14LE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
            AV_PIX_FMT_GBRP12BE,    ///< planar GBR 4:4:4 36bpp, big-endian
            AV_PIX_FMT_GBRP12LE,    ///< planar GBR 4:4:4 36bpp, little-endian
            AV_PIX_FMT_GBRP14BE,    ///< planar GBR 4:4:4 42bpp, big-endian
            AV_PIX_FMT_GBRP14LE,    ///< planar GBR 4:4:4 42bpp, little-endian
            AV_PIX_FMT_YUVJ411P,    ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV411P and setting color_range
    
            AV_PIX_FMT_BAYER_BGGR8,    ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples */
            AV_PIX_FMT_BAYER_RGGB8,    ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples */
            AV_PIX_FMT_BAYER_GBRG8,    ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples */
            AV_PIX_FMT_BAYER_GRBG8,    ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples */
            AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian */
            AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian */
            AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian */
            AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian */
            AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian */
            AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian */
            AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian */
            AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian */
    
            AV_PIX_FMT_XVMC,///< XVideo Motion Acceleration via common packet passing
    
            AV_PIX_FMT_YUV440P10LE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian
            AV_PIX_FMT_YUV440P10BE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian
            AV_PIX_FMT_YUV440P12LE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian
            AV_PIX_FMT_YUV440P12BE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian
            AV_PIX_FMT_AYUV64LE,    ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), little-endian
            AV_PIX_FMT_AYUV64BE,    ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), big-endian
    
            AV_PIX_FMT_VIDEOTOOLBOX, ///< hardware decoding through Videotoolbox
    
            AV_PIX_FMT_P010LE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, little-endian
            AV_PIX_FMT_P010BE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, big-endian
    
            AV_PIX_FMT_GBRAP12BE,  ///< planar GBR 4:4:4:4 48bpp, big-endian
            AV_PIX_FMT_GBRAP12LE,  ///< planar GBR 4:4:4:4 48bpp, little-endian
    
            AV_PIX_FMT_GBRAP10BE,  ///< planar GBR 4:4:4:4 40bpp, big-endian
            AV_PIX_FMT_GBRAP10LE,  ///< planar GBR 4:4:4:4 40bpp, little-endian
    
            AV_PIX_FMT_MEDIACODEC, ///< hardware decoding through MediaCodec
    
            AV_PIX_FMT_GRAY12BE,   ///<        Y        , 12bpp, big-endian
            AV_PIX_FMT_GRAY12LE,   ///<        Y        , 12bpp, little-endian
            AV_PIX_FMT_GRAY10BE,   ///<        Y        , 10bpp, big-endian
            AV_PIX_FMT_GRAY10LE,   ///<        Y        , 10bpp, little-endian
    
            AV_PIX_FMT_P016LE, ///< like NV12, with 16bpp per component, little-endian
            AV_PIX_FMT_P016BE, ///< like NV12, with 16bpp per component, big-endian
    
            /**
             * Hardware surfaces for Direct3D11.
             *
             * This is preferred over the legacy AV_PIX_FMT_D3D11VA_VLD. The new D3D11
             * hwaccel API and filtering support AV_PIX_FMT_D3D11 only.
             *
             * data[0] contains a ID3D11Texture2D pointer, and data[1] contains the
             * texture array index of the frame as intptr_t if the ID3D11Texture2D is
             * an array texture (or always 0 if it's a normal texture).
             */
            AV_PIX_FMT_D3D11,
    
            AV_PIX_FMT_GRAY9BE,   ///<        Y        , 9bpp, big-endian
            AV_PIX_FMT_GRAY9LE,   ///<        Y        , 9bpp, little-endian
    
            AV_PIX_FMT_GBRPF32BE,  ///< IEEE-754 single precision planar GBR 4:4:4,     96bpp, big-endian
            AV_PIX_FMT_GBRPF32LE,  ///< IEEE-754 single precision planar GBR 4:4:4,     96bpp, little-endian
            AV_PIX_FMT_GBRAPF32BE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, big-endian
            AV_PIX_FMT_GBRAPF32LE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, little-endian
    
            /**
             * DRM-managed buffers exposed through PRIME buffer sharing.
             *
             * data[0] points to an AVDRMFrameDescriptor.
             */
            AV_PIX_FMT_DRM_PRIME,
            /**
             * Hardware surfaces for OpenCL.
             *
             * data[i] contain 2D image objects (typed in C as cl_mem, used
             * in OpenCL as image2d_t) for each plane of the surface.
             */
            AV_PIX_FMT_OPENCL,
    
            AV_PIX_FMT_GRAY14BE,   ///<        Y        , 14bpp, big-endian
            AV_PIX_FMT_GRAY14LE,   ///<        Y        , 14bpp, little-endian
    
            AV_PIX_FMT_GRAYF32BE,  ///< IEEE-754 single precision Y, 32bpp, big-endian
            AV_PIX_FMT_GRAYF32LE,  ///< IEEE-754 single precision Y, 32bpp, little-endian
    
            AV_PIX_FMT_YUVA422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, big-endian
            AV_PIX_FMT_YUVA422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, little-endian
            AV_PIX_FMT_YUVA444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, big-endian
            AV_PIX_FMT_YUVA444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, little-endian
    
            AV_PIX_FMT_NV24,      ///< planar YUV 4:4:4, 24bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
            AV_PIX_FMT_NV42,      ///< as above, but U and V bytes are swapped
    
            AV_PIX_FMT_NB         ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
        }
    
        ///  /// ffmpeg中AVFrame結構體的前半部分,因為它太長了我不需要完全移植過來 /// 
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 408)]
        public struct AVFrame
        {
            //#define AV_NUM_DATA_POINTERS 8
            //        uint8_t* data[AV_NUM_DATA_POINTERS];
            public IntPtr data1;// 一般是y分量
            public IntPtr data2;// 一般是v分量
            public IntPtr data3;// 一般是u分量
            public IntPtr data4;// 一般是surface(dxva2硬解時)
            public IntPtr data5;
            public IntPtr data6;
            public IntPtr data7;
            public IntPtr data8;
            public int linesize1;// y分量每行長度(stride)
            public int linesize2;// v分量每行長度(stride)
            public int linesize3;// u分量每行長度(stride)
            public int linesize4;
            public int linesize5;
            public int linesize6;
            public int linesize7;
            public int linesize8;
            //uint8_t **extended_data;
            IntPtr extended_data;
            public int width;
            public int height;
            public int nb_samples;
            public AVPixelFormat format;
        }
    
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 128)]
        public struct AVCodec { }
    
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 72)]
        public unsafe struct AVPacket
        {
            fixed byte frontUnused[24]; // 前部無關數據
            public void* data;
            public int size;
        }
    
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 12)]
        public struct AVBufferRef { }
    
        [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 904)]
        public unsafe struct AVCodecContext
        {
            fixed byte frontUnused[880]; // 前部無關數據
            public AVBufferRef* hw_frames_ctx;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct AVDictionary { }
    
        public unsafe static class FFHelper
        {
            const string avcodec = "avcodec-58";
            const string avutil = "avutil-56";
            const CallingConvention callingConvention = CallingConvention.Cdecl;
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static void avcodec_register_all();
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static AVCodec* avcodec_find_decoder(AVCodecID id);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static AVPacket* av_packet_alloc();
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static void av_init_packet(AVPacket* pkt);
    
            //[DllImport(avcodec, CallingConvention = callingConvention)]
            //public extern static void av_packet_unref(AVPacket* pkt);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static void av_packet_free(AVPacket** pkt);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static AVCodecContext* avcodec_alloc_context3(AVCodec* codec);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, AVDictionary** options);
    
            //[DllImport(avcodec, CallingConvention = callingConvention)]
            //public extern static int avcodec_decode_video2(IntPtr avctx, IntPtr picture, ref int got_picture_ptr, IntPtr avpkt);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static void avcodec_free_context(AVCodecContext** avctx);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static int avcodec_send_packet(AVCodecContext* avctx, AVPacket* pkt);
    
            [DllImport(avcodec, CallingConvention = callingConvention)]
            public extern static int avcodec_receive_frame(AVCodecContext* avctx, AVFrame* frame);
    
    
    
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static int av_hwdevice_ctx_create(AVBufferRef** device_ctx, AVHWDeviceType type, string device, AVDictionary* opts, int flags);
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static AVBufferRef* av_buffer_ref(AVBufferRef* buf);
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static void av_buffer_unref(AVBufferRef** buf);
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static AVFrame* av_frame_alloc();
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static void av_frame_free(AVFrame** frame);
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static void av_log_set_level(int level);
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static int av_dict_set_int(AVDictionary** pm, string key, long value, int flags);
    
            [DllImport(avutil, CallingConvention = callingConvention)]
            public extern static void av_dict_free(AVDictionary** m);
        }
    }
    

    上文中主要有幾個地方是知識點,大家做c#的如果需要和底層交互可以了解一下

    • 結構體的使用
        結構體在c#與c/c++基本一致,都是內存連續變量的一種組合方式。與c/c++相同,在c#中,如果我們不知道(或者可以規避,因為結構體可能很複雜,很多無關字段)結構體細節只知道結構體整體大小時,我們可以用Pack=1,SizeConst=來表示一個大小已知的結構體。
    • 指針的使用
        c#中,有兩種存儲內存地址(指針)的方式,一是使用interop體系中的IntPtr類型(大家可以將其想象成void*),一是在不安全的上下文(unsafe)中使用結構體類型指針(此處不討論c++類指針)
    • unsafe和fixed使用
        簡單來說,有了unsafe你才能用指針;而有了fixed你才能確保指針指向位置不被GC壓縮。我們使用fixed達到的效果就是顯式跳過了結構體中前部無關數據(參考上文中AVCodecContext等結構體定義),後文中我們還會使用fixed。

      現在我們開始編寫解碼和播放部分(即我們的具體應用)代碼

    FFPlayer.cs


    using Microsoft.DirectX;
    using Microsoft.DirectX.Direct3D;
    using System;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows.Forms;
    using static MultiPlayer.FFHelper;

    namespace MultiPlayer
    {
    public unsafe partial class FFPlayer : UserControl
    {
    [DllImport("msvcrt", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    static extern void memcpy(IntPtr dest, IntPtr src, int count); // 用於在解碼器和directx間拷貝內存的c函數

        private IntPtr contentPanelHandle;                              // 畫面渲染的控件句柄,因為畫面渲染時可能出於非UI線程,因此先保存句柄避免CLR報錯
    
        private int lastIWidth, lastIHeight;                            // 上次控件大小,用於在控件大小改變時做出判定重新初始化渲染上下文
        private Rectangle lastCBounds;                                  // 臨時變量,存儲上次控件區域(屏幕坐標)
        private Rectangle lastVRect;                                    // 臨時變量,存儲上次解碼出的圖像大小
        private Device device;                                          // 當使用軟解時,這個變量生效,它是IDirect3Device9*對象,用於繪製YUV
        private Surface surface;                                        // 當使用軟解時,這個變量生效,它是IDirect3Surface9*對象,用於接受解碼后的YUV數據
        AVPixelFormat lastFmt;                                          // 上次解碼出的圖像數據類型,這個理論上不會變
    
        AVCodec* codec;                                                 // ffmpeg的解碼器
        AVCodecContext* ctx;                                            // ffmpeg的解碼上下文
        AVBufferRef* hw_ctx;                                            // ffmpeg的解碼器硬件加速上下文,作為ctx的擴展存在
        AVPacket* avpkt;                                                // ffmpeg的數據包,用於封送待解碼數據
        IntPtr nalData;                                                 // 一塊預分配內存,作為avpkt中真正存儲數據的內存地址
        AVFrame* frame;                                                 // ffmpeg的已解碼幀,用於回傳解碼后的圖像
    
        private volatile bool _released = false;                        // 資源釋放標識,與鎖配合使用避免重複釋放資源(由於底層是c/c++,多線程下double free會導致程序崩潰)
        private object _codecLocker = new object();                     // 鎖,用於多線程下的互斥
    
        static FFPlayer()
        {
            avcodec_register_all();                                     // 靜態塊中註冊ffmpeg解碼器
        }
    
        public FFPlayer()
        {
            InitializeComponent();
    
            // 過程中,下列對象只需初始化一次
            frame = av_frame_alloc();
            avpkt = av_packet_alloc();
            av_init_packet(avpkt);
            nalData = Marshal.AllocHGlobal(1024 * 1024);
            codec = avcodec_find_decoder(AVCodecID.AV_CODEC_ID_H264);
            avpkt->data = (void*)nalData;
        }
    
        ~FFPlayer()
        {
            // 過程中,下列對象只需釋放一次
            if (null != frame)
                fixed (AVFrame** LPframe = &frame)
                    av_frame_free(LPframe);
            if (null != avpkt)
                fixed (AVPacket** LPpkt = &avpkt)
                    av_packet_free(LPpkt);
            if (default != nalData)
                Marshal.FreeHGlobal(nalData);
        }
    
        // 釋放資源
        // 此函數並非表示“終止”,更多的是表示“改變”和“重置”,實際上對此函數的調用更多的是發生在界面大小發生變化時和網絡掉包導致硬解異常時
        private void Releases()
        {
            // 過程中,下列對象會重複創建和銷毀多次
            lock (_codecLocker)
            {
                if (_released) return;
                if (null != ctx)
                    fixed (AVCodecContext** LPctx = &ctx)
                        avcodec_free_context(LPctx);
                if (null != hw_ctx)
                    fixed (AVBufferRef** LPhw_ctx = &hw_ctx)
                        av_buffer_unref(LPhw_ctx);
                // (PS:device和surface我們將其置為null,讓GC幫我們調用Finalize,它則會自行釋放資源)
                surface = null;
                device = null;
                lastFmt = AVPixelFormat.AV_PIX_FMT_NONE;
                _released = true;
            }
        }
    
        // Load事件中保存控件句柄
        private void FFPlayer_Load(object sender, EventArgs e)
        {
            contentPanelHandle = Handle; // 這個句柄也可以是你控件內真正要渲染畫面的句柄
            lastCBounds = ClientRectangle; // 同理,區域也不一定是自身显示區域
        }
    
        // 解碼函數,由外部調用,送一一個分片好的nal
        public void H264Received(byte[] nal)
        {
            lock (_codecLocker)
            {
                // 判斷界面大小更改了,先重置一波
                // (因為DirectX中界面大小改變是一件大事,沒得法繞過,只能推倒從來)
                // 如果你的显示控件不是當前控件本身,此處需要做修改
                if (!ClientRectangle.Equals(lastCBounds))
                {
                    lastCBounds = ClientRectangle;
                    Releases();
                }
    
                if (null == ctx)
                {
                    // 第一次接收到待解碼數據時初始化一個解碼器上下文
                    ctx = avcodec_alloc_context3(codec);
                    if (null == ctx)
                    {
                        return;
                    }
                    // 通過參數傳遞控件句柄給硬件加速上下文
                    AVDictionary* dic;
                    av_dict_set_int(&dic, "hWnd", contentPanelHandle.ToInt64(), 0);
                    fixed (AVBufferRef** LPhw_ctx = &hw_ctx)
                    {
                        if (av_hwdevice_ctx_create(LPhw_ctx, AVHWDeviceType.AV_HWDEVICE_TYPE_DXVA2,
                                                        null, dic, 0) >= 0)
                        {
                            ctx->hw_frames_ctx = av_buffer_ref(hw_ctx);
                        }
                    }
                    av_dict_free(&dic);
                    ctx->hw_frames_ctx = av_buffer_ref(hw_ctx);
                    if (avcodec_open2(ctx, codec, null) < 0)
                    {
                        fixed (AVCodecContext** LPctx = &ctx)
                            avcodec_free_context(LPctx);
                        fixed (AVBufferRef** LPhw_ctx = &hw_ctx)
                            av_buffer_unref(LPhw_ctx);
                        return;
                    }
                }
                _released = false;
    
                // 開始解碼
                Marshal.Copy(nal, 0, nalData, nal.Length);
                avpkt->size = nal.Length;
                if (avcodec_send_packet(ctx, avpkt) < 0)
                {
                    Releases(); return; // 如果程序走到了這裏,一般是因為網絡掉包導致nal數據不連續,沒辦法, 推倒從來
                }
            receive_frame:
                int err = avcodec_receive_frame(ctx, frame);
                if (err == -11) return; // EAGAIN
                if (err < 0)
                {
                    Releases(); return; // 同上,一般這裏很少出錯,但一旦發生,只能推倒從來
                }
    
                // 嘗試播放一幀畫面
                AVFrame s_frame = *frame;
                // 這裏由於我無論如何都要加速,而一般顯卡最兼容的是yv12格式,因此我只對dxva2和420p做了處理,如果你的h264解出來不是這些,我建議轉成rgb(那你就需要編譯和使用swscale模塊了)
                if (s_frame.format != AVPixelFormat.AV_PIX_FMT_DXVA2_VLD && s_frame.format != AVPixelFormat.AV_PIX_FMT_YUV420P && s_frame.format != AVPixelFormat.AV_PIX_FMT_YUVJ420P) return;
                try
                {
                    int width = s_frame.width;
                    int height = s_frame.height;
                    if (lastIWidth != width || lastIHeight != height || lastFmt != s_frame.format) // 這個if判定的是第一次嘗試渲染,因為一般碼流的寬高和格式不會變
                    {
                        if (s_frame.format != AVPixelFormat.AV_PIX_FMT_DXVA2_VLD)
                        {
                            // 假如硬解不成功(例如h264是baseline的,ffmpeg新版不支持baseline的dxva2硬解)
                            // 我們就嘗試用directx渲染yuv,至少省去yuv轉rgb,可以略微節省一丟丟cpu
                            PresentParameters pp = new PresentParameters();
                            pp.Windowed = true;
                            pp.SwapEffect = SwapEffect.Discard;
                            pp.BackBufferCount = 0;
                            pp.DeviceWindowHandle = contentPanelHandle;
                            pp.BackBufferFormat = Manager.Adapters.Default.CurrentDisplayMode.Format;
                            pp.EnableAutoDepthStencil = false;
                            pp.PresentFlag = PresentFlag.Video;
                            pp.FullScreenRefreshRateInHz = 0;//D3DPRESENT_RATE_DEFAULT
                            pp.PresentationInterval = 0;//D3DPRESENT_INTERVAL_DEFAULT
                            Caps caps = Manager.GetDeviceCaps(Manager.Adapters.Default.Adapter, DeviceType.Hardware);
                            CreateFlags behaviorFlas = CreateFlags.MultiThreaded | CreateFlags.FpuPreserve;
                            if (caps.DeviceCaps.SupportsHardwareTransformAndLight)
                            {
                                behaviorFlas |= CreateFlags.HardwareVertexProcessing;
                            }
                            else
                            {
                                behaviorFlas |= CreateFlags.SoftwareVertexProcessing;
                            }
                            device = new Device(Manager.Adapters.Default.Adapter, DeviceType.Hardware, contentPanelHandle, behaviorFlas, pp);
                            //(Format)842094158;//nv12
                            surface = device.CreateOffscreenPlainSurface(width, height, (Format)842094169, Pool.Default);//yv12,顯卡兼容性最好的格式
                        }
                        lastIWidth = width;
                        lastIHeight = height;
                        lastVRect = new Rectangle(0, 0, lastIWidth, lastIHeight);
                        lastFmt = s_frame.format;
                    }
                    if (lastFmt != AVPixelFormat.AV_PIX_FMT_DXVA2_VLD)
                    {
                        // 如果硬解失敗,我們還需要把yuv拷貝到surface
                        //ffmpeg沒有yv12,只有i420,而一般顯卡又支持的是yv12,因此下文中uv分量是反向的
                        int stride;
                        var gs = surface.LockRectangle(LockFlags.DoNotWait, out stride);
                        if (gs == null) return;
                        for (int i = 0; i < lastIHeight; i++)
                        {
                            memcpy(gs.InternalData + i * stride, s_frame.data1 + i * s_frame.linesize1, lastIWidth);
                        }
                        for (int i = 0; i < lastIHeight / 2; i++)
                        {
                            memcpy(gs.InternalData + stride * lastIHeight + i * stride / 2, s_frame.data3 + i * s_frame.linesize3, lastIWidth / 2);
                        }
                        for (int i = 0; i < lastIHeight / 2; i++)
                        {
                            memcpy(gs.InternalData + stride * lastIHeight + stride * lastIHeight / 4 + i * stride / 2, s_frame.data2 + i * s_frame.linesize2, lastIWidth / 2);
                        }
                        surface.UnlockRectangle();
                    }
    
                    // 下面的代碼開始燒腦了,如果是dxva2硬解出來的圖像數據,則圖像數據本身就是一個surface,並且它就綁定了device
                    // 因此我們可以直接用它,如果是x264軟解出來的yuv,則我們需要用上文創建的device和surface搞事情
                    Surface _surface = lastFmt == AVPixelFormat.AV_PIX_FMT_DXVA2_VLD ? new Surface(s_frame.data4) : surface;
                    if (lastFmt == AVPixelFormat.AV_PIX_FMT_DXVA2_VLD)
                        GC.SuppressFinalize(_surface);// 這一句代碼是點睛之筆,如果不加,程序一會兒就崩潰了,熟悉GC和DX的童鞋估計一下就能看出門道;整篇代碼,就這句折騰了我好幾天,其他都好說
                    Device _device = lastFmt == AVPixelFormat.AV_PIX_FMT_DXVA2_VLD ? _surface.Device : device;
                    _device.Clear(ClearFlags.Target, Color.Black, 1, 0);
                    _device.BeginScene();
                    Surface backBuffer = _device.GetBackBuffer(0, 0, BackBufferType.Mono);
                    _device.StretchRectangle(_surface, lastVRect, backBuffer, lastCBounds, TextureFilter.Linear);
                    _device.EndScene();
                    _device.Present();
                    backBuffer.Dispose();
                }
                catch (DirectXException ex)
                {
                    StringBuilder msg = new StringBuilder();
                    msg.Append("*************************************** \n");
                    msg.AppendFormat(" 異常發生時間: {0} \n", DateTime.Now);
                    msg.AppendFormat(" 導致當前異常的 Exception 實例: {0} \n", ex.InnerException);
                    msg.AppendFormat(" 導致異常的應用程序或對象的名稱: {0} \n", ex.Source);
                    msg.AppendFormat(" 引發異常的方法: {0} \n", ex.TargetSite);
                    msg.AppendFormat(" 異常堆棧信息: {0} \n", ex.StackTrace);
                    msg.AppendFormat(" 異常消息: {0} \n", ex.Message);
                    msg.Append("***************************************");
                    Console.WriteLine(msg);
                    Releases();
                    return;
                }
                goto receive_frame; // 嘗試解出第二幅畫面(實際上不行,因為我們約定了單次傳入nal是一個,當然,代碼是可以改的)
            }
        }
        
        // 外部調用停止解碼以显示釋放資源
        public void Stop()
        {
            Releases();
        }
    }

    }

    下面講解代碼最主要的三個部分

    • 初始化ffmpeg
        主要在靜態塊和構造函數中,過程中我沒有將AVPacket和AVFrame局部化,很多網上的代碼包括官方代碼都是局部化這兩個對象。我對此持保留意見(等我程序報錯了再說)
    • 將收到的數據送入ffmpeg解碼並將拿到的數據進行展示
        這裏值得一提的是get_format,官方有一個示例,下圖

    它有一個get_format過程(詳見215行和63行),我沒有採用。這裏給大家解釋一下原因:

    這個get_format的作用是ffmpeg給你提供了多個解碼器讓你來選一個,而且它內部有一個機制,如果你第一次選的解碼器不生效(初始化錯誤等),它會調用get_format第二次(第三次。。。)讓你再選一個,而我們首先認定了要用dxva2的硬件解碼器,其次,如果dxva2初始化錯誤,ffmpeg內部會自動降級為內置264軟解,因此我們無需多此一舉。

    • 發現解碼和播放過程中出現異常的解決辦法
      • 不支持硬解
        代碼中已經做出了一部分兼容,因為baseline的判定必須解出sps/pps才能知道,因此這個錯誤可能會延遲爆出(不過不用擔心,如果此時報錯,ffmpeg會自動降級為軟解)
      • 窗體大小改變
        基於DirectX中設備後台緩衝的寬高無法動態重設,我們只能在控件大小改變時推倒重來。如若不然,你繪製的畫面會進行意向不到的縮放
      • 網絡掉包導致硬件解碼器錯誤
        見代碼
      • 其他directx底層異常
        代碼中我加了一個try-catch,捕獲的異常類型是DirectXException,在c/c++中,我們一般是調用完函數後會得到一個HRESULT,並通過FAILED宏判定他,而這個步驟在c#自動幫我們做了,取而代之的是一個throw DirectXException過程,我們通過try-catch進行可能的異常處理(實際上還是推倒重來)

      番外篇:C#對DiretX調用的封裝
    上文中我們使用DirectX的方式看起來即非COM組件,又非C-DLL的P/Invoke,難道DirectX真有託管代碼?
    答案是否定的,C#的dll當然也是調用系統的d3d9.dll。不過我們有必要一探究竟,因為這裏面有一個隱藏副本

    首先請大家準備好ildasm和visual studio,我們打開visual studio,創建一個c++工程(類型隨意),然後新建一個cpp文件,然後填入下面的代碼

    如果你能執行,你會發現輸出是136(0x88);然後我們使用ildasm找到StrechRectangle的代碼

    你會發現也有一個+0x88的過程,那麼其實道理就很容易懂了,c#通過calli(CLR指令)可以執行內存call,而得益於微軟com組件的函數表偏移量約定,我們可以通過頭文件知道函數對於對象指針的偏移(其實就是一個簡單的ThisCall)。具體細節大家查閱d3d9.h和calli的網絡文章即可。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

  • 心裏有紅黑樹

    心裏有紅黑樹

    Why 紅黑樹

    為什麼大家都這麼推崇紅黑樹呢? 這就是數據結構的魅力!!! 下面我簡述一下常用數據結構的優缺點

    • 數組

    大家對數組很熟悉, 都知道對數組來說,它底層的存儲空間是連續的,因此如果我們根據index去獲取元素,速度是相當快, 但是對於數組來說有時候查詢也不見得就一定塊, 比如我們查詢數組中名字叫張三的人, 也不得不從開始遍歷這個數組

    如果我們想往數組中插入一個元素, 也不見得一定就慢, 比如我們往數組中最後的位置插入就很快, 但是要是往開始的位置插入的話, 肯定會很慢, 需要將現有數組中所有的元素往後移動一位, 才能空出開始的位置,給新元素用

    • 鏈表

    一說鏈式存儲, 大家也都知道, 這種數據結構僅僅是邏輯連續, 物理存儲不連續, 因此我們有可以通過玩指針或者引用很快的完成元素的刪除和添加

    對鏈表的查詢來說, 一定是慢的, 無論查詢誰, 查哪個, 都得從第一個節點開始遍歷

    • AVL樹

    AVL樹, 就是二叉平衡樹, 這種有序的樹形結果就將鏈式存儲添加刪除塊, 順序存儲的查找快兩大有點進行了一次中和, 在絕大部分情況下, AVL樹在增刪改查方面的性能都原超過數組和鏈表

    • 紅黑樹

    紅黑樹是對AVL樹是又一次重大升級, AVL樹,對於樹的平衡要求太嚴格了, 每當添加,刪除節點時,都不得不進行調整

    對於AVL樹個紅黑樹來說, 每次添加一個新的節點都是最多進行兩次旋轉(左旋右旋)就能重新使樹變的平衡,

    但是當我們刪除一個恭弘=叶 恭弘子節點時, AVL樹重新調整成平衡狀態時最多需要進行旋轉O(logN)次, 而紅黑樹最多旋轉3次就能重新平衡,時間複雜度是O(1)

    還有就是紅黑樹並不是完全意義上的AVL樹, 也就是說它其實並不是真的像AVL樹那樣嚴格要求對一個節點來說左右子樹的高度差不能超過1, 而是選擇使用染成紅色和黑色進行維護

    簡單來說, 因為紅黑樹並不像AVL樹那樣完全平衡, 可能會導致紅黑樹的讀性能略遜於AVL, 但是紅黑樹的維護成本絕對是遠遠低於AVL, 在空間上的開銷和AVL樹基本持平, 因此紅黑樹被大家極力推崇, 和學習java的同學直接相關的就是jdk8的 hashmap

    紅黑樹的特性

    紅黑樹主要存在下面的7條性質

    1. 節點非紅即黑
    2. 根節點必定是黑色
    3. 恭弘=叶 恭弘子節點全部是黑色, (這裏說的恭弘=叶 恭弘子節點是我們想象在肉眼看到的節點上再多加一層子節點)
    4. 紅節點的子節點必定是黑色
    5. 紅節點的父節點必定是黑色
    6. 從根節點到任意子節點的路徑上,都要經歷相同數目的黑節點
    7. 從根節點到任意子節點的路徑上不可能存在兩個連續相同的紅節點

    常見的誤區

    如上圖, 看着挺像紅黑樹, 其實他不是, 看它node10, 並不滿足上面的性質6. 因為我們認為node10的左子節點是黑色的節點, 這樣的話, 從node20到node10的左子節點就經歷了兩個黑節點, 而其他的 node15, node25, node35 經歷的黑色子節點數都是三個

    如上圖它也不是紅黑樹, 因為我們認為node30的右節點是黑色的節點, 這樣的話從node60到node30的右節點就經歷了三個黑色的節點, 而其他的所有子節點都經歷了4個, 故, 他不是紅黑樹

    紅黑樹與2-3-4樹等價

    如上圖中,當我們將一個紅黑樹中的黑色節點和紅色節點融合在一起時,我們會發現, 這個紅黑樹其實就是一顆2-3-4樹, 一顆四階B樹

    並且, 紅黑樹中黑色節點的每一個合併完成后的節點中都有一個黑色的節點, 換句話說就是紅黑樹中黑色節點的個數等於2-3-4樹中節點的個數

    添加

    添加節點其實就是構造紅黑樹的過程, 只要我們嚴格遵循上面的7條限制, 構造出來的樹就是紅黑樹

    通過上圖其實我們發現, 紅黑樹真的可以和四階B樹之間進行等價代換, 換句話說就是 4階B樹的性質對於紅黑樹來書其實也是存在的, 主要是如下兩條性質

    • 所有新添加進去的節點都被放在了恭弘=叶 恭弘子節點上
    • 2-3-4樹中每一個節點中允許承載的元素的個數 [1,3]

    經驗推薦: 就是新添加的節點盡量全部是紅色, 如果你畫一畫就會發現, 如果我們新添加的節點是紅色的話,上面所說的7條性質中, 除了第四條(紅節點的子節點必定的黑節點). 其他的限制都可以滿足

    於是看一下一顆四階B樹插入節點時有哪些種情況

    數一數: 一共 4+3+3+2 = 12種情況, 換句話說, 只要我們處理好了這12種情況, 我們就完成了添加節點的邏輯

    • 情況1, 就是假設我們添加進去的是紅色的節點, 並且這個紅色節點的父節點是黑色節點時, 直接添加進行,不需要其他任何變換, 就想下圖這樣, 直接簡單粗暴的添加就行

    除去第一種情況外, 還剩下8中情況出現了紅紅節點相鄰, 於是繼續往下看, 我們對他進行一次修復

    • 情況2: 如下圖

    插入的node57, node64, 什麼情況呢? 就是當前節點是node5556, 首先這個節點中現存兩個元素, 並且是往這個黑色的節點的左側的左側插入, 或者是右側的右側插入一個紅色節點

    看上圖出現了兩個紅色節點相鄰,於是我們第一件事就是進行重新染色,

    1. 將插入節點的父節點染成黑色
    2. 將插入節點的祖父節點染成紅色
    3. 將祖父節點進行旋轉, 如果這個新節點被插入在父節點的右側. 左旋轉它的祖父節點

    經過上面的變換后, 我們重新得到標準的紅黑樹如下

    • 情況3: 新添加的節點的叔叔節點不是紅色

    第三種情況和第二種情況相似, 還是插入 node57和node64. 判斷的條件是 插入節點的叔叔節點(父節點的兄弟節點)不是紅節點,

    簡稱 LR , 或者是RL , 需要進行如下的調整

    1. 染色: 將自己染成黑色,祖節點染成紅色
    2. LR: 父節點左旋轉, 祖父節點右旋轉
    3. RL: 祖父點右旋轉, 父節點左旋轉

    LR舉例:

    經過上面的變化,我們重新得到平衡的紅黑樹

    接着往下看剩下的四種情況

    • 情況4: 新添加的節點的叔叔節點是紅色, 其實就是需要上溢的情況, 也很好處理

    像上圖這樣, 新添加的紅色節點 node15, 它本身的父節點是node20, 父節點的叔叔節點是紅色的node25, 我們比較node15和node20的大小, 發現node15本來是應該放在node20的左邊的, 但是對於一顆2-3-4樹來說, 單個節點最多就有3個元素, 如果再加上node15 就會出現上溢的情況, 怎麼辦呢? 我們上溢調整, 選擇這個節點中間位置的元素向上和父節點合併, 選擇node20, node30其實都是可以的, 為了方便我們選擇node30

    好,下面開始修復這個紅黑樹

    1. 將插入的節點的父節點和它的叔叔節點染成黑色
    2. 發生了上溢, 將他的父節點的染成紅色, 遞歸插入到根節點上, 這時候根節點可能又會發生上溢

    然後上溢

    當我們將新插入的節點的父節點node30染成紅色時, 再插入到根節點, 實際上就是重複我們枚舉出來的這12種情況中的一種. 紅黑樹一定會被修復, 當然這時候很可能會出現根節點也容納不了新的元素, 需要根節點也進行上溢, 然後將根節點染黑

    還有一種情況是像下面這樣, 同樣是在情況4下的新插入的節點的叔叔節點是紅色

    像下面這樣調整:

    1. 將父節點和叔叔節點染成黑色
    2. 祖父節點上溢

    然後就是這種情況

    調整的思路和前面一樣

    1. 將父節點和叔叔節點染成黑色
    2. 將祖父節點上溢

    至此紅黑樹的添加的12種情況就全部枚舉完成了

    刪除

    對於刪除來說總共兩大種四小種情況

    • 第一種就是刪除的節點就是紅色節點, 如果真是這樣的話,直接刪除就ok
    • 第二種是刪除的節點是黑色節點
      • 刪除擁有1個red節點的黑色節點
      • 刪除擁有2個red節點的黑色節點,
      • 刪除黑色節點

    如果一個像下面這樣, 下面的黑色節點有兩個子節點, 這種情況下,黑色節點肯定不會直接被刪除的, 需要進行變換,讓他的恭弘=叶 恭弘子節點去替換他,進而實現刪除的目的

    • 情況1: 刪除擁有1個紅節點的黑色節點,像下圖這樣

    怎麼判斷這就是我們想刪除的情況呢? 當我們確定用來替代這個被刪除的黑節點是紅色,則符合當前的情況

    也就是說我們想刪除 node40 和 node70, 於是我們這樣做

    1. 讓這個指向被刪除的節點的指針指向這個被刪除的節點的子節點
    2. 將替代它的節點染成黑色

    於是我們接得到下圖這樣的結果

    • 情況2: 刪除的節點是黑色的恭弘=叶 恭弘子節點, 並且可向兄弟節點借

    首先,如果這個恭弘=叶 恭弘子節點就是根節點的話,直接刪除就ok

    看下面的這個圖, 我們就刪除其中node90, 即,刪除黑色恭弘=叶 恭弘子節點

    如果想刪除上圖中的node90也是由竅門的,規律和2-3-4樹是擦不多的

    假設它就是2-3-4樹, 如果我們將node90刪了, 我們計算一下, 對於2-3-4樹來說, 每一個節點位置上至少有 ⌈ 4/2 ⌉ -1 = 1個元素, 但是把node90刪除了這個位置上的節點中沒有元素, 因此產生了 下溢

    出現下溢,我們首先考慮的情況就是看看可不可以向它的兄弟節點借一個,但是和B樹是有取別的, 多了下面的限制

    1. 被刪除的這個節點的兄弟節點必須是黑色的
    2. 被刪除的這個節點的兄弟節點一定的有紅色的子節點才ok, 就像上圖那樣, 可以在左邊,右邊,或者都有
    3. 直接刪除掉指定的node(因為它在恭弘=叶 恭弘子節點的位置上)
    4. 進行旋轉,旋轉時注意, 兩點:第一點: 比如下面的原來根節點位置上的元素88是紅色的, 經過旋轉上來替換它的節點的顏色必須染成紅色, 如果node88是黑色, 那麼經過旋轉上來替換他的節點的顏色必須染成黑色 ,第二點: 旋轉完成后,新的跟節點的直接左右子節點的顏色轉換為黑色

    怎麼進行旋轉呢? 就像下圖這樣

    • 情況3: 刪除的節點是黑色的恭弘=叶 恭弘子節點, 並且它的兄弟是黑色,而且它的兄弟節點不能借給他元素

    像這種情況:我像刪除node99,但是沒辦法像他的兄弟節點借元素,於是

    1. 將父節點向下合併,父節點染成黑色
    2. 將它的兄弟節點染成紅色

    也有特殊的情況, 就是它的父節點只有一個,還是黑色

    這時候,我們將他的父節點下溢, 原位置的節點捨棄

    • 還有最後一種情況就是, 刪除的是黑色的節點, 它的兄弟節點的是紅色的節點

    就像上圖那樣,我們想刪除node99, 但是node99的兄弟節點其實是node55, 而不是node77, 我們怎麼樣才能轉換為前面說的那些情況呢?

    1. 將被刪除節點的父節點染成紅色, 兄弟節點染黑

    2. 讓被刪除的父節點進行右旋轉(node88右轉)

      得到下圖

    於是我們就將這種兄弟節點為紅節點的情況轉化成了兄弟節點為黑色節點的樣子, 按照原來的方式進行刪除修整即可

    1. 讓原父節點下溢
    2. 原染成黑色
    3. 兄弟節點,染成紅色

    至此本文就結束, 歡迎關注我,後續我更新更多的關於開發相關的筆記

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

    ※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

  • 特斯拉正式打造出第一輛Model 3!馬斯克大秀美照

    特斯拉正式打造出第一輛Model 3!馬斯克大秀美照

    千呼萬喚始出來!特斯拉(Tesla Inc.)終於在上週六(7月8日)順利生產出第一輛平價電動車「Model 3」(見圖),執行長馬斯克(Elon Musk)透過Twitter發布這個訊息後(),還特地秀出兩張官方照片(、)。

    馬斯克在推文中表示,創投機構DBL Partners創辦人Ira Ehrenpreis原本已經下了訂金、買下第一輛Model 3,但Ehrenpreis決定把擁有第一輛Model 3的權利讓給馬斯克,作為他46歲的生日禮物。

    Model 3定價35,000美元,不少人將之視為特斯拉豪華電動車「Model S」的平價版。這款全新轎車體型嬌小,但同樣也會有自駕功能,預料每次充電的里程數將有215英里。

    特斯拉預定7月底生產30輛Model 3,12月會將月產能拉高至2萬輛,等於是一年生產24萬輛。至少已有38萬人支付1,000美元的訂金(可退款),但特斯拉從去年初就未曾更新過這項數據。第一批顧客只有兩種選擇:顏色和輪胎尺寸。

    過去一週對特斯拉來說並不好過,第2季交貨量不如預期,再加上富豪集團(Volvo)宣布2019年起所有車款都會是電動車、成為第一家這麼做的傳統車廠,導致特斯拉股價從兩週前的386.99美元歷史高一路大跌近20%。

    特斯拉才剛於7月3日公布,第2季的電動車交貨量僅略高於22,000台,不如前季的25,000台,主要是受到100 kWh電池組產能嚴重短缺的影響。特斯拉說,截至6月初為止,電池組的生產量平均比需求短少了40%。

    不過,特斯拉股價在連續大跌三個交易日後,上週五(7月7日)反彈1.42%、收313.22美元。路透社報導,特斯拉表示,第二季底大約有3,500輛電動車還在運送途中、尚未交貨給客戶,這些車可在Q3計入交貨量。

    (本文內容由授權使用。圖片出處:Elon Musk Twitter)  

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益