• YUVVIEWER之YUV解析器


    YUV是视频原始数据存储格式,如何将文件中的YUV解析出来呢?

    一、YUV概要

            YUV中的Y表示图像的亮度,即灰度值;U和V表示图像的色度,即图像的颜色。一帧YUV数据只提取Y分量,仍然可以完整的显示这一帧图像,但是黑白色。

            YUV存储格式为两种:

    1. planar : 平面格式,即先存储Y分量,再U分量,最后V分量
    2. packed :打包格式,即Y,U,V分量交叉存储

            按照UV分量比例不同,YUV存储方式分为:

    1. YUV444 :Y,U,V分量存储比例相同
    2. YUV422 :Y分量存储比例是U,V分量的1倍;U,V分量比例相同
    3. YUV420 :Y分量存储比例是U,V分量和的1倍;U,V分量比例相同

      YUV不同存储方式数据大小计算(图像分辨率:width * height):

    1. YUV444 :width * height + width * height + width * height = width * height * 3
    2. YUV422 :width * height + width * height  / 2 + widht * height / 2 = widht * height * 2
    3. YUV420 :widht * height + width * height / 4 + width * height / 4 = width * height * 3 / 2

      在实际编程中,需要根据YUV事件存储格式和存储方式动态提取或存储数据。

    二、YUV解析器

      YUV数据结构

     1 namespace yuv{
     2     
     3     struct YUV
     4     {
     5         std::unique_ptr<uint8_t[]> Y;      // Y分量
     6         size_t YSize;
     7         std::unique_ptr<uint8_t[]> U;     // U分量
     8         size_t USize;
     9         std::unique_ptr<uint8_t[]> V;     // V分量
    10         size_t VSize;
    11     };
    12 }

      YUV帧数据结构

    1 namespace yuv{
    2     struct YUVFrame
    3     {
    4         Resolution resolution;     // 分辨率
    5         YUV        yuv;            // YUV分量数据 
    6         size_t     size;           // 帧数据大小
    7     };
    8 }

      YUV解析器

      1 namespace yuv{
      2     template <typename ParserType>
      3     class YUVParser
      4     {
      5     public:
      6         using Frames = std::vector<YUVFrame>;
      7         constexpr YUVParser(const std::string& file, const Resolution& resolution) noexcept
      8             : _file(file)
      9             , _iFileStream(file, std::ios::binary)
     10             , _ext(Extension(file))
     11             , _resolution(resolution)
     12         {
     13         }
     14 
     15         virtual ~YUVParser() {}
     16 
     17         virtual bool Parse()
     18         {
     19             if (!_iFileStream.is_open()) {
     20                 return false;
     21             }
     22 
     23             const auto& resolution = GetResolution();
     24             const auto kFarmeSize = GetPerFrameSize();
     25             const auto frameCounts = CountFrames(_iFileStream, kFarmeSize);
     26             if (frameCounts == 0) {
     27                 return false;
     28             }
     29 
     30             _frames.reserve(frameCounts);
     31             auto buffer = std::make_unique<uint8_t[]>(kFarmeSize);
     32             while (true) {
     33                 std::memset(buffer.get(), 0x00, kFarmeSize);
     34                 _iFileStream.read(reinterpret_cast<char*>(buffer.get()), kFarmeSize);
     35                 auto readSize = _iFileStream.gcount();
     36                 if (readSize == 0) {
     37                     break;
     38                 }
     39                 if (readSize != kFarmeSize) {
     40                     continue;
     41                 }
     42 
     43                 auto frame = BuildYUVFrame(buffer.get());
     44                 _frames.push_back(std::move(frame));
     45             }
     46 
     47             return _frames.size();
     48         }
     49 
     50         bool DuplicateToFile(const std::string& duplicateFile) const
     51         {
     52             const auto& parser = static_cast<const ParserType&>(*this);
     53             return parser.DuplicateToFileImpl(duplicateFile);
     54         }
     55 
     56         bool DumpYToFile(const std::string& yFile) const
     57         {
     58             return DumpYToFileImpl(yFile);
     59         }
     60 
     61         inline const std::string& GetFilePath() const { return _file; }
     62         inline const std::string& GetExtension() const { return _ext; }
     63         inline const Resolution& GetResolution() const { return _resolution; }
     64         inline const Frames& GetFrames() const { return _frames; }
     65         inline const size_t GetFrameCounts() const { return _frames.size(); }
     66     protected:
     67         static constexpr auto Extension(const std::string& file)
     68         {
     69             using namespace std::filesystem;
     70             using namespace std::string_literals;
     71 
     72             if (u8path(file).has_extension()) {
     73                 return u8path(file).extension().u8string();
     74             }
     75             else {
     76                 return ".yuv"s;
     77             }
     78         }
     79         
     80         static size_t CountFrames(std::ifstream& iFileStream, size_t frameSize)
     81         {
     82             SeekBeg(iFileStream);
     83             auto begPos = iFileStream.tellg();
     84             iFileStream.seekg(0, std::ios_base::end);
     85             auto endPos = iFileStream.tellg();
     86             SeekBeg(iFileStream);
     87 
     88             return static_cast<size_t>(endPos - begPos) / frameSize;
     89         }
     90 
     91         static void SeekBeg(std::ifstream& iFileStream)
     92         {
     93             iFileStream.seekg(0, std::ios_base::beg);
     94         }
     95         
     96         virtual YUVFrame BuildYUVFrame(const uint8_t* buf) const = 0;
     97         virtual size_t GetPerFrameSize() const = 0;
     98 
     99         virtual bool DumpYToFileImpl(const std::string& yFile) const
    100         {
    101             std::ofstream oYFileStream(yFile, std::ios::binary);
    102             if (oYFileStream.is_open() && GetFrameCounts()) {
    103                 for (const auto& frame : GetFrames()) {
    104                     oYFileStream.write(reinterpret_cast<char*>(frame.yuv.Y.get()), frame.yuv.YSize);
    105                 }
    106                 return true;
    107             }
    108             else {
    109                 return false;
    110             }
    111         }
    112 
    113         virtual YUVFrame CreateYUVFrame() const
    114         {
    115             YUVFrame yuvFrame;
    116             yuvFrame.resolution = GetResolution();
    117             yuvFrame.size = GetPerFrameSize();
    118             yuvFrame.yuv.YSize = GetYSize();
    119             yuvFrame.yuv.Y = std::make_unique<uint8_t[]>(yuvFrame.yuv.YSize);
    120             yuvFrame.yuv.USize = GetUSize();
    121             yuvFrame.yuv.U = std::make_unique<uint8_t[]>(yuvFrame.yuv.USize);
    122             yuvFrame.yuv.VSize = GetVSize();
    123             yuvFrame.yuv.V = std::make_unique<uint8_t[]>(yuvFrame.yuv.VSize);
    124 
    125             return yuvFrame;
    126         }
    127 
    128         virtual void FillYUVFrame(const uint8_t* buf, YUVFrame& frame) const = 0;
    129         virtual size_t GetYSize() const
    130         {
    131             return _resolution.width * _resolution.height;
    132         }
    133         virtual size_t GetUSize() const = 0;
    134         virtual size_t GetVSize() const
    135         {
    136             return GetUSize();
    137         }
    138     protected:
    139         const std::string   _file;
    140         const std::string   _ext;
    141         const Resolution    _resolution;
    142         std::ifstream       _iFileStream;
    143         Frames              _frames;
    144     };  
    145 }

      YUV444P解析器(planar)

     1 namesapce yuv{
     2     // YUV444P Foramt
     3     // example : 4 * 4
     4     //  -------------------------------
     5     // |  Y00  |  Y01  |  Y02  |  Y03  |
     6     //  -------------------------------
     7     // |  Y10  |  Y11  |  Y12  |  Y13  |
     8     //  -------------------------------
     9     // |  Y20  |  Y21  |  Y22  |  Y23  |
    10     //  -------------------------------
    11     // |  Y30  |  Y31  |  Y32  |  Y33  |
    12     //  -------------------------------
    13     // |  U40  |  U41  |  U42  |  U43  |
    14     //  -------------------------------
    15     // |  U50  |  U51  |  U52  |  U53  |
    16     //  -------------------------------
    17     // |  U60  |  U61  |  U62  |  U63  |
    18     //  -------------------------------
    19     // |  U70  |  U71  |  U72  |  U73  |
    20     //  -------------------------------
    21     // |  V80  |  V81  |  V82  |  V83  |
    22     //  -------------------------------
    23     // |  V90  |  V91  |  V92  |  V93  |
    24     //  -------------------------------
    25     // |  V100 |  V101 |  V102 |  V103 |
    26     //  -------------------------------
    27     // |  V110 |  V111 |  V112 |  V113 |
    28     //  -------------------------------  
    29 
    30     class YUVParser444P
    31         : public YUVParser420P 
    32     {
    33     public:
    34         YUVParser444P(const std::string& file, const Resolution& resolution)
    35             : YUVParser420P(file, resolution)
    36         {}
    37 
    38     private:
    39         size_t GetPerFrameSize() const override
    40         {
    41             return _resolution.width * _resolution.height * 3;
    42         }
    43 
    44         size_t GetUSize() const override
    45         {
    46             return GetYSize();
    47         }
    48     };
    49 }    

      YUV422P解析器(planar)

     1 namespace yuv{
     2     // YUV422P Foramt
     3     // example : 4 * 4
     4     //  -------------------------------
     5     // |  Y00  |  Y01  |  Y02  |  Y03  |
     6     //  -------------------------------
     7     // |  Y10  |  Y11  |  Y12  |  Y13  |
     8     //  -------------------------------
     9     // |  Y20  |  Y21  |  Y22  |  Y23  |
    10     //  -------------------------------
    11     // |  Y30  |  Y31  |  Y32  |  Y33  |
    12     //  -------------------------------
    13     // |  U40  |  U41  |  U42  |  U43  |
    14     //  -------------------------------
    15     // |  U50  |  U51  |  U52  |  U53  |
    16     //  -------------------------------
    17     // |  V60  |  V61  |  V62  |  V63  |
    18     //  -------------------------------
    19     // |  V70  |  V71  |  V72  |  V73  |
    20     //  -------------------------------
    21     class YUVParser422P
    22         : public YUVParser420P
    23     {
    24     public:
    25         YUVParser422P(const std::string& file, const Resolution& resolution)
    26             : YUVParser420P(file, resolution)
    27         {}
    28     private:
    29         size_t GetPerFrameSize() const override
    30         {
    31             return _resolution.width * _resolution.height * 2;
    32         }
    33 
    34         size_t GetUSize() const override
    35         {
    36             return GetYSize() / 2;
    37         }
    38     };
    39 }

      YUV420P解析器(planar)

     1 namespace yuv{
     2     // YUV420P Format
     3     // Example : 4 * 4
     4     //  -------------------------------
     5     // |  Y00  |  Y01  |  Y02  |  Y03  |
     6     //  -------------------------------
     7     // |  Y10  |  Y11  |  Y12  |  Y13  |
     8     //  -------------------------------
     9     // |  Y20  |  Y21  |  Y22  |  Y23  |
    10     //  -------------------------------
    11     // |  Y30  |  Y31  |  Y32  |  Y33  |
    12     //  -------------------------------
    13     // |  U40  |  U41  |  U42  |  U43  |
    14     //  -------------------------------
    15     // |  V50  |  V51  |  V52  |  V53  |
    16     //  -------------------------------
    17     template <typename ParserType> class YUVParser;
    18     class YUVParser420P
    19         : public YUVParser<YUVParser420P>
    20     {
    21         friend class YUVParser<YUVParser420P>;
    22     public:
    23         YUVParser420P(const std::string& file, const Resolution& resolution)
    24             : YUVParser(file, resolution)
    25         {}
    26     private:
    27         bool DuplicateToFileImpl(const std::string& duplicateFile) const
    28         {
    29             std::ofstream oDuplicateFileStream(duplicateFile, std::ios::binary);
    30             if (oDuplicateFileStream.is_open() && GetFrameCounts()) {
    31                 for (const auto& frame : GetFrames()) {
    32                     oDuplicateFileStream.write(reinterpret_cast<char*>(frame.yuv.Y.get()), frame.yuv.YSize);
    33                     oDuplicateFileStream.write(reinterpret_cast<char*>(frame.yuv.U.get()), frame.yuv.USize);
    34                     oDuplicateFileStream.write(reinterpret_cast<char*>(frame.yuv.V.get()), frame.yuv.VSize);
    35                 }
    36                 return true;
    37             }
    38             else {
    39                 return false;
    40             }
    41         }
    42 
    43         YUVFrame BuildYUVFrame(const uint8_t* buf) const override
    44         {
    45             auto frame = CreateYUVFrame();
    46             FillYUVFrame(buf, frame);
    47             return frame;
    48         }
    49         
    50         size_t GetPerFrameSize() const override
    51         {
    52             return _resolution.width * _resolution.height * 3 / 2;
    53         }
    54 
    55         void FillYUVFrame(const uint8_t* buf, YUVFrame& frame) const override
    56         {
    57             std::memcpy(frame.yuv.Y.get(),
    58                 buf,
    59                 frame.yuv.YSize);
    60             std::memcpy(frame.yuv.U.get(),
    61                 buf
    62                 + frame.yuv.YSize,
    63                 frame.yuv.USize);
    64             std::memcpy(frame.yuv.V.get(),
    65                 buf
    66                 + frame.yuv.YSize
    67                 + frame.yuv.USize,
    68                 frame.yuv.VSize);
    69         }
    70 
    71         size_t GetUSize() const override
    72         {
    73             return GetYSize() / 4;
    74         }
    75     };
    76 }

      YUV420SP解析器(packed)

     1 namespace yuv{
     2     // YUV420SP Foramt
     3     //  -------------------------------
     4     // |  Y00  |  Y01  |  Y02  |  Y03  |
     5     //  -------------------------------
     6     // |  Y10  |  Y11  |  Y12  |  Y13  |
     7     //  -------------------------------
     8     // |  Y20  |  Y21  |  Y22  |  Y23  |
     9     //  -------------------------------
    10     // |  Y30  |  Y31  |  Y32  |  Y33  |
    11     //  -------------------------------
    12     // |  U40  |  V41  |  U42  |  V43  |
    13     //  -------------------------------
    14     // |  U50  |  V51  |  U52  |  V53  |
    15     //  -------------------------------1
    16     class YUVParser420SP
    17         : public YUVParser420P 
    18     {
    19     public:
    20         YUVParser420SP(const std::string& file, const Resolution& resolution)
    21             : YUVParser420P(file, resolution)
    22         {}
    23 
    24     private:
    25         bool DuplicateToFileImpl(const std::string& duplicateFile) const
    26         {
    27             std::ofstream oDuplicateFileStream(duplicateFile, std::ios::binary);
    28             if (oDuplicateFileStream.is_open() && GetFrameCounts()) {
    29                 for (const auto& frame : GetFrames()) {
    30                     oDuplicateFileStream.write(reinterpret_cast<char*>(frame.yuv.Y.get()), frame.yuv.YSize);
    31                     for (size_t index = 0; index < frame.yuv.USize; ++index) {
    32                         oDuplicateFileStream.write(reinterpret_cast<char*>(frame.yuv.U.get()), 1);
    33                         oDuplicateFileStream.write(reinterpret_cast<char*>(frame.yuv.V.get()), 1);
    34                     }
    35                 }
    36                 return true;
    37             }
    38             else {
    39                 return false;
    40             }
    41         }
    42 
    43         void FillYUVFrame(const uint8_t* buf, YUVFrame& frame) const override
    44         {
    45             std::memcpy(frame.yuv.Y.get(),
    46                 buf,
    47                 frame.yuv.YSize);
    48             const auto& uvBuf = buf + frame.yuv.YSize;
    49             const auto& uvSize = frame.yuv.USize + frame.yuv.VSize;
    50             for (size_t index = frame.yuv.YSize, uIndex = 0, vIndex = 0; index < uvSize;) {
    51                 std::memcpy(frame.yuv.U.get() + uIndex++,
    52                     buf + index++,
    53                     1);
    54 
    55                 std::memcpy(frame.yuv.V.get() + vIndex++,
    56                     buf + index++,
    57                     1);
    58             }
    59         }
    60     };
    61 }

      ... ...

    三、测试样例

     1 int main()
     2 {
     3     using namespace yuv;
     4     constexpr auto kYUVFile = u8R"(.\res\file.xxx)";
     5     const Resolution resolution{ 1920,1080 };
     6     // 444P
     7     YUVParser444P parser(kYUVFile, resolution);
     8     // 422P
     9     YUVParser422P parser(kYUVFile, resolution);
    10     // 420P
    11     YUVParser420P parser(kYUVFile, resolution);
    12     // 420SP
    13     YUVParser420SP parser(kYUVFile, resolution);
    14 
    15     parser.Parse();
    16     std::cerr << "frames = > " << parser.GetFrameCounts() << "\n";
    17     
    18     const auto& ext = parser.GetExtension();
    19     const auto filename = "y_file" + ext;
    20     const auto duplicateFilename = "duplicate" + ext;
    21     parser.DuplicateToFile(".//" + duplicateFilename);
    22     parser.DumpYToFile(".//" + filename);
    23 
    24     return 0;
    25 }

    参考文献:

    • http://www.chiark.greenend.org.uk/doc/linux-doc-3.16/html/media_api/yuv-formats.html
  • 相关阅读:
    (十)条件判断
    (九)字符处理命令
    (八)awk命令
    (六)环境变量配置文件
    (七)grep命令行提取符号
    Ⅶ 类模板与STL编程 ②
    Ⅵ 虚函数与多态性
    Ⅴ 运算符重载
    Ⅳ 继承与派生②
    Ⅳ 继承与派生①
  • 原文地址:https://www.cnblogs.com/smartNeo/p/15673518.html
Copyright © 2020-2023  润新知