標籤: 網站設計

  • Spring Boot2 系列教程(二十六)Spring Boot 整合 Redis

    Spring Boot2 系列教程(二十六)Spring Boot 整合 Redis

    在 Redis 出現之前,我們的緩存框架各種各樣,有了 Redis ,緩存方案基本上都統一了,關於 Redis,松哥之前有一個系列教程,尚不了解 Redis 的小夥伴可以參考這個教程:

    使用 Java 操作 Redis 的方案很多,Jedis 是目前較為流行的一種方案,除了 Jedis ,還有很多其他解決方案,如下:

    除了這些方案之外,還有一個使用也相當多的方案,就是 Spring Data Redis。

    在傳統的 SSM 中,需要開發者自己來配置 Spring Data Redis ,這個配置比較繁瑣,主要配置 3 個東西:連接池、連接器信息以及 key 和 value 的序列化方案。

    在 Spring Boot 中,默認集成的 Redis 就是 Spring Data Redis,默認底層的連接池使用了 lettuce ,開發者可以自行修改為自己的熟悉的,例如 Jedis。

    Spring Data Redis 針對 Redis 提供了非常方便的操作模板 RedisTemplate 。這是 Spring Data 擅長的事情,那麼接下來我們就來看看 Spring Boot 中 Spring Data Redis 的具體用法。

    方案一:Spring Data Redis

    創建工程

    創建工程,引入 Redis 依賴:

    創建成功后,還需要手動引入 commos-pool2 的依賴,因此最終完整的 pom.xml 依賴如下:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
    </dependencies>

    這裏主要就是引入了 Spring Data Redis + 連接池。

    配置 Redis 信息

    接下來配置 Redis 的信息,信息包含兩方面,一方面是 Redis 的基本信息,另一方面則是連接池信息:

    spring.redis.database=0
    spring.redis.password=123
    spring.redis.port=6379
    spring.redis.host=192.168.66.128
    spring.redis.lettuce.pool.min-idle=5
    spring.redis.lettuce.pool.max-idle=10
    spring.redis.lettuce.pool.max-active=8
    spring.redis.lettuce.pool.max-wait=1ms
    spring.redis.lettuce.shutdown-timeout=100ms

    自動配置

    當開發者在項目中引入了 Spring Data Redis ,並且配置了 Redis 的基本信息,此時,自動化配置就會生效。

    我們從 Spring Boot 中 Redis 的自動化配置類中就可以看出端倪:

    @Configuration
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(
                        RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
                RedisTemplate<Object, Object> template = new RedisTemplate<>();
                template.setConnectionFactory(redisConnectionFactory);
                return template;
        }
        @Bean
        @ConditionalOnMissingBean
        public StringRedisTemplate stringRedisTemplate(
                        RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
                StringRedisTemplate template = new StringRedisTemplate();
                template.setConnectionFactory(redisConnectionFactory);
                return template;
        }
    }

    這個自動化配置類很好理解:

    1. 首先標記這個是一個配置類,同時該配置在 RedisOperations 存在的情況下才會生效(即項目中引入了 Spring Data Redis)
    2. 然後導入在 application.properties 中配置的屬性
    3. 然後再導入連接池信息(如果存在的話)
    4. 最後,提供了兩個 Bean ,RedisTemplate 和 StringRedisTemplate ,其中 StringRedisTemplate 是 RedisTemplate 的子類,兩個的方法基本一致,不同之處主要體現在操作的數據類型不同,RedisTemplate 中的兩個泛型都是 Object ,意味者存儲的 key 和 value 都可以是一個對象,而 StringRedisTemplate 的 兩個泛型都是 String ,意味者 StringRedisTemplate 的 key 和 value 都只能是字符串。如果開發者沒有提供相關的 Bean ,這兩個配置就會生效,否則不會生效。

    使用

    接下來,可以直接在 Service 中注入 StringRedisTemplate 或者 RedisTemplate 來使用:

    @Service
    public class HelloService {
        @Autowired
        RedisTemplate redisTemplate;
        public void hello() {
            ValueOperations ops = redisTemplate.opsForValue();
            ops.set("k1", "v1");
            Object k1 = ops.get("k1");
            System.out.println(k1);
        }
    }

    Redis 中的數據操作,大體上來說,可以分為兩種:

    1. 針對 key 的操作,相關的方法就在 RedisTemplate 中
    2. 針對具體數據類型的操作,相關的方法需要首先獲取對應的數據類型,獲取相應數據類型的操作方法是 opsForXXX

    調用該方法就可以將數據存儲到 Redis 中去了,如下:

    k1 前面的字符是由於使用了 RedisTemplate 導致的,RedisTemplate 對 key 進行序列化之後的結果。

    RedisTemplate 中,key 默認的序列化方案是 JdkSerializationRedisSerializer 。

    而在 StringRedisTemplate 中,key 默認的序列化方案是 StringRedisSerializer ,因此,如果使用 StringRedisTemplate ,默認情況下 key 前面不會有前綴。

    不過開發者也可以自行修改 RedisTemplate 中的序列化方案,如下:

    @Service
    public class HelloService {
        @Autowired
        RedisTemplate redisTemplate;
        public void hello() {
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            ValueOperations ops = redisTemplate.opsForValue();
            ops.set("k1", "v1");
            Object k1 = ops.get("k1");
            System.out.println(k1);
        }
    }

    當然也可以直接使用 StringRedisTemplate:

    @Service
    public class HelloService {
        @Autowired
        StringRedisTemplate stringRedisTemplate;
        public void hello2() {
            ValueOperations ops = stringRedisTemplate.opsForValue();
            ops.set("k2", "v2");
            Object k1 = ops.get("k2");
            System.out.println(k1);
        }
    }

    另外需要注意 ,Spring Boot 的自動化配置,只能配置單機的 Redis ,如果是 Redis 集群,則所有的東西都需要自己手動配置,關於如何操作 Redis 集群,松哥以後再來和大家分享。

    方案二:Spring Cache

    通過 Spring Cache 的形式來操作 Redis,Spring Cache 統一了緩存江湖的門面,這種方案,松哥之前有過一篇專門的文章介紹,小夥伴可以移步這裏:。

    方案三:回歸原始時代

    第三種方案,就是直接使用 Jedis 或者 其他的客戶端工具來操作 Redis ,這種方案在 Spring Boot 中也是支持的,雖然操作麻煩,但是支持,這種操作松哥之前也有介紹的文章,因此這裏就不再贅述了,可以參考 。

    總結

    Spring Boot 中,Redis 的操作,這裏松哥給大家總結了三種方案,實際上前兩個使用廣泛一些,直接使用 Jedis 還是比較少,基本上 Spring Boot 中沒見過有人直接這麼搞。

    好了,本文就說到這裏,有問題歡迎留言討論。

    相關案例已經上傳到 GitHub,歡迎小夥伴們們下載:

    掃碼關注松哥,公眾號後台回復 2TB,獲取松哥獨家 超2TB 免費 Java 學習乾貨

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

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

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

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

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

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

  • PL真有意思(一):引言

    前言

    斷斷續續學編譯原理到之前發過寫一個編譯器和正則表達式引擎系列文章也有一段時間了,然後最近看完PLP這本書,這本書應該算是入門書,但是對我這種半吊子收穫很大。所以為了彌補最近學操作系統和接外包摸的魚,就想寫寫看完這本書的收穫。(為拙劣的標題道歉

    程序設計語言的譜系

    現在的新語言都是一撮一撮的出來,但是基本都可以用他們的計算模型來分成兩類,一類是更關心計算機做什麼的說明式,一類是更關心計算機怎麼做的命令式

    一般認為像函數式邏輯式語言都算是說明式,而馮諾依曼式和面向對象的都被認為是命令式

    函數式

    函數式是基於函數的遞歸定義的計算模型,一般從本質上來看,函數式把程序認為是一種從輸入到輸出的函數,使用更簡單的函數通過逐步細化的過程來定義

    邏輯式

    邏輯式里經典的應該就是Prolog了,邏輯式一般將計算看作是一種找出滿足某些特定關係的值的嘗試過程

    馮諾依曼式

    馮諾依曼式最重要的就是通過副作用也就是修改寄存器里的值來對後續計算產生影響,像C和Fortran都屬於馮諾依曼式

    幾個例子

    如果C語言來實現求最大公約數,可以發現C語言偏向通過迭代和反覆修改變量的值來實現

    int gcd(int a, intb) {
        while (a != b) {
          if (a > b) {
            a = a - b;
          } else {
            b = b - a;
          }
        }
    }

    從lisp來看,則更加關注輸入和輸出的數學關係,要算出最大公約數,需要對函數的不斷的擴充和精簡

    (define gcd
      (lambda (a b)
        (cond ((= a b) a)
              ((> a b) (gcd (- a b) b))
              (else (gcd (- b a) a)))))

    對於像C或者Java入門的人,看到Prolog可能頭都大了,因為Prolog和命令式的思考邏輯完全不同,邏輯式更傾向給出一組公理和檢測規則,期望系統能給出相應合理的值,我也僅限於能看懂這些小程序

    gcd(A,B,G) :- A = B, G = A.
    gcd(A,B,G) :- A > B, C is A - B, gcd(C,B,G)
    gcd(A,B,G) :- B > A, C is B-A
    gcd(C,A,G)

    編譯和解釋

    下面再看兩個概念,編譯和解釋。

    編譯一般是指從一個語言到另一個語言的翻譯,無論是高級語言到彙編還是高級語言到高級語言都算是編譯。而解釋就是直接執行代碼,但是現代的解釋器,一般還有虛擬機一層,即翻譯到虛擬機指令再由虛擬機進行執行

    自舉

    很多語言的編譯器都是由自己編譯而成的,所以問題就是,最開始的編譯器是怎麼編譯的?

    假設現在要為Java編寫一個編譯器,我們可以先用C語言編寫一個Java小子集的編譯器,然後再手動將C語言翻譯到這個Java子集,就可以由這個子集編譯運行自己,然後就可以不斷擴充這個編譯器

    編譯概覽

    其實這個在之前那個寫編譯器的系列是說得最詳細的,這個系列是想要寫寫筆記對實踐和語言設計結合的說。

    • 詞法分析

    有關詞法分析其實就是將字符流化成單詞流,記錄每個單詞的信息,然後通常還會有其它更多的信息讓編譯器更好的生成錯誤信息

    • 語法分析

    語法分析通常會用到一個概念:上下文無關文法,就是用來定義語法形式的,比如while語句就可以這樣表示

    while-statment := WHILE ( expr ) statment

    一般語法分析過程最後的輸出都是樹狀結構

    • 語義分析和中間代碼生成

    語法分析只保證源代碼語法格式的正確,但是卻不能保證其它的正確性,比如對於靜態類型的語言,可能就會在編譯時直接檢測出類型錯誤

    而在語義分析過程一般還需要藉助一個叫做符號表的數據結構,這個符號表將每個標識符都映射到關於它的已知信息

    中間代碼的生成通常是會將樹形結構翻譯成更接近彙編的中間線性格式,但是中間格式不是必須的,比如之前那個系列里還寫了C的解釋器,雖然很殘缺,它是沒有中間代碼的,連虛擬機都沒有,是直接進行遍歷語法樹進行執行解釋的

    • 代碼優化

    有些代碼改進是機器無關的,即可以在中間格式上就進行優化,但是有的代碼優化是平台相關的,所以就需要在最後對目標語言優化

    • 目標代碼生成

    代碼生成階段就是根據之前生成的線性結構和符號表信息對目標代碼的生成

    小結

    這一篇主要說了幾個範式的語言和編譯過程的概括,對摸魚進行懺悔

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

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

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

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

  • 電動車環島不是夢,特斯拉7月將於台灣完成250座充電站

    電動車環島不是夢,特斯拉7月將於台灣完成250座充電站

    一台特斯拉隨隨便便就要兩、三百萬元台幣的價碼,讓不少喜愛電動車有效率、環保特性的人無法輕易擁有。特斯拉宣布他們跟台灣本地銀行提供的優惠方案,最低每月負擔36,000 元台幣,就可以將特斯拉車子開回家。另外特斯拉的超級充電站也將遍布全台,開著特斯拉環島將不是夢想。

    在上週五(6/16)特斯拉Model X 交車派對上,特斯拉香港、澳門及台灣地區區域總監范菁怡表示,在台灣由星展銀行提供的優惠方案,每月最少花36,000 元,就可以開著特斯拉回家。或車主開了3 年之後,除了繼續繳每月的費用,也可以選擇回購專案,將車子賣回給特斯拉。特斯拉也有與車行合作模式,可採租賃模式駕駛特斯拉車。以上幾項不同的方案,供車主依自己的需求選擇最適合的方案。

    特斯拉表示,到了7 月,開特斯拉車環島也不會是問題。台灣中部、南部的台南、高雄都將在高速公路交流道一帶設置超級充電站,而東部也會有超級充電站。未來開著特斯拉享受環島行程不再是夢想。Tesla 於今年第二季末正式在台完成超過250 座目的地充電站,分布於全台80 個地點。

    ▲ 在派對上交車的車輛,5~7 人座都有。

    另外不少車主期待的自動輔助駕駛2.0 版與導航功能也將在台啟用,為全世界第一批啟用國家之一。不論是自動輔助轉向、跟車、防撞預警、自動換導、自動停車或是召換功能,都能在台灣使用。未來Model S 或Model X 開在台灣的道路上,透過OTA 機制更新,能夠累積台灣路況,提供最好的駕駛體驗。

    ▲ 特斯拉與車行合作,提供駕駛特斯拉車的不同方式。

    儘管在交車派對上車主是主角,但特斯拉宣布了不少優惠專案。對車主來說,可在一個月內免費申請安裝家用充電設備,另外還有機會到SpaceX 參觀!

     

    ▲ 特斯拉在台灣與星展銀行合作,推出特斯拉優惠方案。

    (合作媒體:。圖片出處:科技新報)

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

    【其他文章推薦】

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

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

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

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

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

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

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

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

    【其他文章推薦】

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

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

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

  • Json模塊和Pickle模塊的使用

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

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

    Json 模塊

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

    dumps()函數

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

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

    dump()函數

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

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

    loads()函數

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

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

    load()函數

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

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

    Pickle 模塊

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

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

    dumps()函數

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

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

    dump()函數

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

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

    loads()函數

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

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

    load()函數

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    【其他文章推薦】

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

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

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

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

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

  • 上海新能源汽車展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/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

  • 加州拚減排、擬給予電動車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/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

  • Gogoro 雲嘉地區電池交換站正式啟用,暢騎基隆到屏東

    Gogoro 雲嘉地區電池交換站正式啟用,暢騎基隆到屏東

    Gogoro 車主們期待已久的雲嘉地區電池交換站21 日正式開通提供服務。未來,Gogoro 的車主將可以騎乘Smartscooter 智慧雙輪縱貫台灣西半部,一路從基隆暢騎到屏東。

    Gogoro 於21 日宣布,雲林嘉義地區首批5 座GoStation 電池交換站正式上線,其中兩站位於雲林斗六、虎尾;另三站則分布於嘉義市區。自從Gogoro 於2015 年在台北市設立首座電池交換站以來,短短兩年的時間,已經建置將近400 座電池交換站,廣布於基隆到屏東的各個縣市,在六都甚至不到一公里就設有一站,累積提供將近500 萬次的電池交換服務,是全世界最穩定的電池交換能源網路系統。

    Gogoro 台灣區行銷總監陳彥揚表示: 「雲嘉地區GoStation 電池交換站的開通有3 個劃時代的意義。首先,我們顛覆消費者對於電動機車續航力不足的刻板印象;兩年前,沒有人會相信,我們可以騎乘電動機車橫跨北高。其次,對Gogoro 車主而言,Gogoro 將不再僅是都會的通勤工具,而是更進一步深入他們的生活,成為跨縣市旅遊的另一種交通選擇;最後,我們則提供雲嘉地區民眾一個能夠改善當地空污,讓生活環境更環保、更健康的解決方案。」

    日前宣布Gogoro 2 系列預購超過13,000 台,震撼市場的Gogoro 在雲林嘉義地區提供電池交換站的服務後,搭配雲嘉地區相對較高的電動機車補助,將加速當地居民的購車意願。目前,雲林縣汰換二行程機車購買電動機車的總補助金額達34,000 元,嘉義市為31,000 元,嘉義縣則為21,000 元。Gogoro 也正規劃在不久的將來於雲嘉地區增設銷售及維修據點,希望很快可以讓雲嘉的居民享有Gogoro 完整的銷售、維修、電池交換等全方位服務。

      (合作媒體:。圖片出處:科技新報)

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

    【其他文章推薦】

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

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

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

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

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