標籤: 網頁設計公司推薦

  • Tesla計畫於上海建電動車廠,關稅考量為主因

    Tesla計畫於上海建電動車廠,關稅考量為主因

    電動車製造商特斯拉(Tesla)在Model X 的銷售和Model 3 的產能上都面臨著巨大挑戰,在全球最大的電動車市場──中國,Tesla 則看到了電動車需求持續成長帶來的機會,並希望能夠透過投資建廠、本土化製造等方式在中國電動車市場分一杯羹,據悉Tesla 將在上海建造海外第一座電動車製造工廠,未來可能會用於Model 3 電動車的生產。

    據知情人士透露,特斯拉已經與上海市政府達成合作協議,將首次在中國生產製造電動車,此次合作將有助於特斯拉進一步提升在中國市場的銷售,目前中國是全球最大的電動車市場,政府對於電動車的銷售和生產有許多優惠政策和補貼。

    特斯拉的製造工廠將建在上海臨港開發區,細節正在確認中,將在本月晚些時候對外公開,據悉上海市政府要求該製造工廠必須由特斯拉和上海的合作夥伴共同投資建造,但是否持有控股權還不得而知。市場諮詢公司Dunne Automotive 總裁Michael Dune 表示,特斯拉有機會佔據中國電動車市場的領先地位,許多有實力和知名度的品牌公司都會選擇在上海建造生產基地。

    特斯拉選擇在上海生產電動車,有助於直接與中國汽車廠商的產品競爭,這比在美國生產再進口到中國市場銷售,至少能夠降低25% 的進口關稅,正是由於關稅的成本,Tesla Model S 和Model X 電動車在中國市場的價格比美國市場高一倍。

    中國政府已經將電動車產業做為戰略性的新興產業,目標是在未來10 年內將混合式和全電動車的銷量提升10 倍,2016 年中國市場電動車的銷量約為28.3 萬台,佔比全球銷量的41%,Tesla 2016 年營收大約為70 億美元,其中15% 來自中國市場。目前大約有200 家公司宣布在中國製造電動車的計畫,其中北汽汽車和比亞迪公司的電動車佔總銷量的89%。

    特斯拉CEO 伊隆·馬斯克(Elon Musk)早在3 年前就表示希望能夠在中國建設製造工廠,自2014 年以後每次到訪中國都會與大量政府官員見面。2017 年6 月初特斯拉宣布2017 年下半年在中國超級充電站建設計畫。目前特斯拉在中國大約有117 個超級充電站。

    (合作媒體:。圖片出處:public domain CC0)

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

    【其他文章推薦】

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

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

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

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

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

    美國能源資訊局(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/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

  • 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方法吧!

    代碼地址

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

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

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

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

  • 使用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的網絡文章即可。

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

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

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

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

  • 全球Q2電動汽車發展指數 中國首度躍居整體排名第一

    全球Q2電動汽車發展指數 中國首度躍居整體排名第一

    中證網報導,羅蘭貝格與德國汽車研究機構亞琛汽車工程技術公司共同發佈《2017年第二季全球電動汽車發展指數》。報告中顯示,中國大陸首次在電動汽車發展指數的整體排名中躍居全球第一,並直指儘管政府新能源政策收緊,補貼力道減弱,中國電動汽車和電池製造市場份額仍將保持強有力的增長,進一步擴大領先優勢。

    該報告對中國、德國、法國、義大利、美國、日本和韓國電動汽車的發展現狀進行比較。整體而言,中國首次躍居指數整體排名第一,美國與德國分居第二、三位,而在上一季指數排名中位列第一的日本則失去領先地位。報告預測,在可預見的未來,中國將統領電動汽車的行業與市場。

    在技術層面,法國超越德國,位居首位,主要由於有更多的德國整車廠大批量生產續航能力和最高電動時速都較低的插電式混合動力汽車,導致其電動汽車技術能力略有下降;日本排名第三,其整車廠的電動汽車技術水準較高且價格更加實惠;中國整車廠則仍主要定位於技術含量較低的領域。

    在行業總量層面,中國正在逐步擴大其領先優勢;在電池製造領域,中國的優勢也更加明顯;反觀日本在電動汽車產量和全球電池產量份額上都處於不利地位,排名維持在第三;美國行業成績有所提升,位居第二。至於在市場規模層面,中國的需求進一步急劇增長,但電動汽車所占市場份額仍略低於法國,排在第二,美國名列第三。

    資料顯示,2016年中國生產了超過35萬輛插電式混合動力和純電動乘用車,銷售額保持兩位數增長,市佔率從0.8%上升至1.3%;同年,德國、法國與美國電動汽車的註冊數量均實現了兩位數的增長。但整體而言,2016年僅有法國與中國兩個國家的純電動和插電式混合動力汽車市場份額超過1%。

    報告認為,中國電動汽車銷量的快速增長主要得益於政府大幅度補貼和主要城市對汽油車的限牌政策,但政府對於汽車廠商的政策正在收緊。對此,羅蘭貝格合夥人鄭贇表示,雖然大陸政府的激勵政策在初期對行業發展起到了重要的推動作用,但難以長久維持,政府需要控制成本,也有意讓本土廠商培育自身能力,電動汽車產業的發展將由政府推動向市場推動轉變,其最新版的新能源汽車雙積分管理意見徵求稿就明確地傳達了此訊號。

    根據羅蘭貝格的估算,要達到新能源汽車積分比例2020年12%的目標,該年電動汽車的總銷量需達到約160萬輛。鄭贇指出,汽車設計、配件整合以及供應商管理能力將成為大陸本土廠商所面臨的重大挑戰,想要在政府退補的情況下實現增長、完成積分目標,成本控制是關鍵;只有成本控制能力和價格競爭力的提升才能幫助其本土廠商在國際電動汽車市場上保持長期的競爭優勢。

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

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

    【其他文章推薦】

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

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

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

  • 上海新能源汽車展8月舉行 分時租賃成熱門話題

    共用經濟無疑是當前最為熱門的話題之一,共用單車、共用汽車、共用雨傘、共用充電寶等各種共用話題層出不窮。但是時下最最熱門的,莫過於共用汽車的話題了。

    據瞭解,由充電設施線上網、廣東省充電設施協會、廣東省新能源汽車產業協會、中國土木工程學會城市公共交通學會和振威展覽股份聯合舉辦的2017上海國際新能源汽車產業博覽會將於8月23-25日在上海新國際博覽中心舉行。本次展會邀請了國內200多家分時租賃運營商參會交流,在我國新能源汽車產業快速發展的背景下,本次展會的舉行對於推動新能源汽車分時租賃發展具有重要意義。

    分時租賃的發展不但對於推動新能源汽車的普及應用具有重要的作用,而且有助於緩解交通堵塞,以及公路的磨損,減少空氣污染,降低對能量的依賴性,發展前景極為廣闊。

    據交通運輸部最新統計,截至目前分時租賃企業40餘家,車輛總數超過4萬輛,95%以上為新能源車輛。其中77%的分時租賃車輛出自整車廠背景的分時租賃企業。市場普遍預測,未來5年汽車分時租賃市場將以超過50%的增幅發展,行業有望在2020年之前迎來突破性發展,保守估計在2020年中國整體車隊規模有望達到17萬輛以上,交易金額將從9億元增長到47億元。到2025年中國的分時租賃汽車數量將達到60萬輛。

    日前,由交通運輸部會同住房和城鄉建設部制定的《關於促進汽車租賃業健康發展的指導意見(徵求意見稿)》發佈。該意見稿釋放出的重要資訊是,國家層面開始鼓勵汽車分時租賃業態的發展。地方層面,廣州、深圳、上海、成都等地均發佈了有關於促進分時租賃產業發展的《指導意見》。這些指導意見無一例外都對發展規劃及扶持政策有了明確的指示。

    以成都市為例,為推動成都市分時租賃的發展,由交委牽頭起草的《成都市關於鼓勵和規範新能源汽車分時租賃業發展的指導意見(徵求意見稿)》中明確指出:到2018年底,基本形成新能源汽車分時租賃服務網路,服務網點達到2500個,充電樁達到10000個;到2020年底,形成覆蓋廣泛的新能源汽車分時租賃服務網路,服務網點達到5000個,充電樁達到20000個。扶持政策方面將按照《成都市進一步支持新能源汽車推廣應用的若干政策》等有關規定執行。

    由此可以看出,分時租賃的發展規劃能夠極大的促進新能源汽車推廣及充電基礎設施建設工作。而本次展會恰合時宜的融合了新能源整車生產與運營、核心三電、充電設備製造與運營等環節,為推動新能源汽車產業的發展提供了一個集交流,採購,瞭解新技術、新模式,品牌宣傳推廣等為一體的綜合展示平臺。

    參觀預登記,請點擊:

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

    【其他文章推薦】

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

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

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

  • 馬斯克看好電動車市場,Tesla 將在美國建 3 座超級工廠

    馬斯克看好電動車市場,Tesla 將在美國建 3 座超級工廠

    電動車製造商特斯拉(Tesla)和伊隆·馬斯克(Elon Musk)在過去幾個月中都多次提及了投資建造超級工廠的計畫,新製造工廠的選址將在年底公開,據悉 Tesla 將建造 5 座超級工廠,其中美國將有 2 到 3 座全新的超級工廠。   特斯拉將在海外建造超級工廠的消息已經傳了幾個月的時間了,在美國本土的超級工廠建造計畫則一直沒有進展。該公司 CEO 馬斯克確認將有 2 到 3 座超級工廠選址在美國本土。   2017 年 6 月特斯拉公司在股東大會上確認有 3 座超級工廠選址已經啟動,這些工廠包括了電動車和電池生產線。   據之前媒體曝光的消息顯示,特斯拉至少將在海外市場建造兩座超級工廠,分別位於歐洲和中國,特斯拉已經與中國上海市政府簽訂合作協議,共同建造電動車製造工廠。   在美國州長協議的會議上,馬斯克公開表示,將會有 2 到 3 座超級工廠選址在美國本土,他面對所有州長做出這一表態,也是希望政府部門能夠提供工廠建造和電動車生產方便的優惠政策,顯然許多州長都對 Tesla 超級工廠非常感興趣。   特斯拉在內華達州的超級工廠給該州帶來了超過 50 億美元的投資,創造了一萬個工作職缺,馬斯克表示,吸引 Tesla 把超級工廠建造內華達州的因素很多,包括稅收方面的優惠。   馬斯克希望政府部門能夠在立法上做出更多進步,讓新的技術能夠更快地商業化,之前他曾多次公開表示內華達州政府具有前瞻性,在超級工廠的建造過程中展示了前所未有的高效。   Tesla 未來的超級工廠將把電動車製造和電池的生產放在同一座工廠,有效地提升電動車的產能,而不是像現在這樣電池和電動車分開製造,再運往組裝工廠。   (合作媒體:。圖片出處:Tesla)  

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

    【其他文章推薦】

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

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

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

  • 加州拚減排、擬給予電動車30億美元購車退稅補助

    加州拚減排、擬給予電動車30億美元購車退稅補助

    華爾街日報15日報導,加州下議院已通過規模達30億美元的電動車購車退稅折扣法案,後續還得過上議院與州長Jerry Brown這兩關。負責起草這項法案的舊金山民主黨籍議員Phil Ting表示,加州若想落實氣候變遷目標(2025年讓150萬輛零排放車輛上路)、勢必得想辦法給電動車產業打強心針才行。根據加州空氣資源局的統計,加州已有超過25萬輛零排放汽車(包括電動車)上路、佔美國約半數的比重。根據加州新車經銷商協會發表的報告,2017年第1季電動車僅佔加州整體汽車銷售比重的2.7%。

    Edmunds(汽車銷售追蹤網站)汽車業分析師Jessica Caldwell指出,加州若推新補貼方案、對銷售應該有所幫助,特別是Chevrolet Bolt、特斯拉Model 3(見圖)擁有更好的續航力。加州自2010年起給予每台全電動車2,500美元的折抵稅額、但自今年夏季起只有所得水準符合標準的消費者才能取得補助。

    Thomson Reuters 7月16日引述法國週報《Le Journal du Dimanche(JDD)》報導,法國總統馬克宏(Emmanuel Macron)在受訪時表示,他對於美國總統川普(Donald Trump)改變退出巴黎氣候協議的決定抱持希望。馬克宏說,川普在兩人碰面時表示他會試著在未來數個月內找到解決方案。

    Cobalt 27 Capital Corp(TSX-V:KBLT)6月23日在加拿大創業板初次公開發行(IPO)、一舉募得2.0億加幣(相當於1.507億美元)。瑞銀(UBS)表示,鋰電池供應鏈中的原物料將受電動車滲透率快速增長的衝擊、但當中僅有鈷面臨儲備有限的問題。

    通用汽車(General Motors)Chevrolet Bolt電動車續航力達238英里(383公里)、建議零售價37,495美元起(註:最多可取得7,500美元的聯邦折抵稅額、扣除後入手價相當於29,995美元)。

    美聯社報導,IHS Markit汽車業分析師Stephanie Brinley指出,Bolt續航力遠高於美國平均來回通勤距離(40英里),但有時人們回家後可能忘了或沒有足夠時間進行充電,這是電動車主得多加費心的地方。

    電動車大廠特斯拉(Tesla)14日上漲1.35%、收327.78美元;週線上漲4.65%、3週以來首度收高。

    通用汽車14日上漲1.37%、收36.35美元,創3月16日以來收盤新高。

    Cobalt 27 IPO價格為9.0加幣;7月14日大漲7.00%、以10.70加幣坐收,創IPO以來收盤新高。

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

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

    【其他文章推薦】

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

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

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

  • 電動車增長 高盛估全球石油需求最快2024年觸頂

    電動車增長 高盛估全球石油需求最快2024年觸頂

    路透社7月25日報導,高盛(Goldman Sachs)最新報告表示,受到汽車燃油效率提高、電動車產業快速發展、經濟增長低迷以及高油價等因素的影響,全球石油需求最快可能在2024年就會到達高峰。報告預估,全球的電動車市場將從2016年的200萬輛,爆發增長至2030年的8,300萬輛。全球石油需求的年均增長率將從2011到2016年期間的1.6%,降至2017到2022年的1.2%,至2025年降至0.7%,2030年降至0.4%。

    高盛表示,從現在起到2030年,運輸部門對石油需求增長的貢獻將會逐步下滑,石化產業的需求將取而代之並躍居主流。報告也預估未來五年油品的供應將會出現過剩,因煉油產能持續增加但需求增長放緩的影響,這會使得全球煉油廠的產能利用率下滑,並壓縮煉油廠的毛利。此外,由於越來越多的石化原料來自煉油體系之外(如天然氣凝析油等),煉油廠石油需求的佔比也將會下滑。

    亞洲三大石油消費國中國大陸、印度以及日本的需求增長疲弱,將令油市重新恢復平衡的時間拉長。大陸、印度以及日本合計佔全球石油需求的20%比重,但各自面臨不同的困難,使得石油需求的增長疲弱。其中,日本受困於人口老化以及汽車燃油效率的持續提高,印度因去年底去貨幣化的政策衝擊需求,而中國大陸正積極去化過剩煉油產能,將會影響到原油的需求。

    英國石油公司(BP PLC)發布的《世界能源統計年鑑》表示,2016年,全球能源需求增長1%,連續第三年呈現疲弱的增長態勢(2014與2015年分別年增1%與0.9%),相比過去十年的平均增長率為1.8%。主要的增長來自於中國大陸與印度,其中印度2016年能源需求年增5.4%,增速與過去幾年相符。大陸去年能源需求年增1.3%,與2015年的1.2%增幅相近,但只有過去十年平均增速的四分之一,並寫下1997-98年亞洲金融風暴以來的連續兩年最低增速。

    BP年鑑指出,2016年,石油消費佔全球能源消費的三分之一比重,全球石油需求年增1.6%或每日160萬桶,此高於過去十年的平均增速(1.2%)。其中,大陸石油需求年增每日40萬桶,印度以及歐洲的石油需求均年增每日30萬桶。2016年,全球石油日產量僅年增40萬桶,則是創下2013年以來的最低增長;其中,中東的石油日產量年增170萬桶,但中東以外的石油日產量則是年減130萬桶。

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

     

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

    【其他文章推薦】

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

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

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

  • 上海新能源汽車展8月23舉行 新能源物流車運營模式受關注

    資料顯示,目前中國物流車的保有量在2000萬量左右,而新能源物流車在整個物流市場僅占2%。隨著建設“美麗中國”的宏願及“十三五規劃”的國策推進,物流車輛的新能源化勢在必行。目前,中國物流行業發展迅速,除了中國郵政、順豐及四通一達等快遞企業;天貓、京東等電商自建物流之外,供應鏈企業、飲料食品、餐飲配送、食蔬鮮農、工廠貨運等行業無不存在物流。

     

    千億市場規模放量

     

    市場普遍認為,僅考慮替代市場,城市物流車市場空間可達250萬輛。2017年以來,物流車的訂單也是不斷。3月,欣旺達與陝西通家簽訂了20000輛電動物流車的動力電池系統訂單。5月,九龍汽車與廣通汽車簽訂《採購合同》,採購金額達26.55億元。6月,中植汽車與浙江軍盛控股有限公司、城市合夥人創客(南京)股份有限公司簽訂5年10萬輛純電動物流車的超大生產協議。7月,萬馬股份發佈最新投資者關係活動記錄表披露,公司圍繞“運力”重點佈局電動物流車、充電樁、貨源和快充網四個方面。上市公司頻獲訂單和加速佈局的背後無一不在說明,新能源物流車行業的發展將呈井噴態勢。

     

    市場放量巨大但運營卻遇瓶頸

     

    雖然市場放量巨大,政府剛需迫切,但是新能源物流車與傳統物流車同樣存在著購車成本高、運營模式單一、盈利難等問題。同時新能源物流車基本為純電動,充電難也成為了新能源物流車的推廣中亟待解決的問題。

    目前,新能源物流車運營的模式大致分為四種:1.新能源物流車輛中長期租賃模式;2.新能源物流車輛分時租賃模式;廠商自有物流車定向租賃模式;4.新能源專用車輛定制模式。但是無論哪種模式來看,新能源物流車目前普遍面臨著充維保障體系建設週期長、投資大、需要土地、電網等需政府部門協調的諸多問題。

     

    現有困難如何突破?

     

    業內人士認為,大規模、體系化的運營,才能真正取得客戶的深度信任,讓客戶切實感受到物流車電動化時代的到來。如果純電動物流車運營仍然沿襲傳統物流車的老模式,必將會面臨諸多的發展瓶頸。只有改革新能源物流車的商業模式與運營模式,才能從根本上解決充電難、購車成本高、維保成本高、運營效率低等一系列問題。

    據瞭解,由充電設施線上網、廣東省充電設施協會、廣東省新能源汽車產業協會和振威展覽股份共同主辦,中國土木工程學會城市公共交通學會協辦的2017上海國際新能源汽車產業博覽會將於8月23-25日在上海新國際博覽中心舉行。同期還將舉辦純電動物流車運營商大會、新能源汽車充換電技術高峰論壇及共用汽車大會。

    其中,純電動物流車運營商大會邀請了八匹馬、一微新能源、駒馬物流、雲杉智慧、地上鐵、傳化智聯、士繼達新能源、綠道新能源等國內知名新能源物流車運營商,以及來自政府主管部門、專家學者、科研院校、資本方等重量級嘉賓參會交流,多維度深層次探討新能源物流車運營模式,旨在推動新能源物流車產業的快速發展。

    此外,比亞迪、申龍客車、珠海銀隆、上汽集團、上饒客車、中植新能源、中通、江淮、吉利、眾泰、知豆、南京金龍、成功汽車、新吉奧集團、瑞馳新能源、福汽新龍馬等新能源汽車企業,以及精進電動、英威騰、東風電機、力神、沃特瑪、國軒高科、地上鐵、特來電、科陸、巴斯巴、萬馬專纜、奧美格、瑞可達等核心三電及零部件知名企業將亮相本次展會,展出最新款產品和前沿技術。

     

    參觀預登記,請點擊:

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

    【其他文章推薦】

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

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

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