1/*
2 * Copyright (c) 2022-2023 Shenzhen Kaihong DID Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * 		http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "codec_packet_reader.h"
16#include <arpa/inet.h>
17#include <hdf_log.h>
18namespace {
19constexpr int32_t START_CODE_OFFSET_ONE = -1;
20constexpr int32_t START_CODE_OFFSET_SEC = -2;
21constexpr int32_t START_CODE_OFFSET_THIRD = -3;
22constexpr int32_t START_CODE_SIZE_FRAME = 4;
23constexpr int32_t START_CODE_SIZE_SLICE = 3;
24constexpr char START_CODE = 0x1;
25constexpr char VOP_START = 0xB6;
26}  // namespace
27
28CodecPacketReader::Ptr CodecPacketReader::GetPacketReader(const CodecMime &mime)
29{
30    CodecPacketReader::Ptr reader = nullptr;
31    switch (mime) {
32        case CodecMime::AVC:
33        case CodecMime::HEVC:
34            reader = std::make_shared<CodecH264Reader>();
35            break;
36        case CodecMime::MPEG4:
37            reader = std::make_shared<CodecMpeg4Reader>();
38            break;
39        case CodecMime::VP9:
40            reader = std::make_shared<CodecVp9Reader>();
41            break;
42        default:
43            break;
44    }
45    return reader;
46}
47
48CodecH264Reader::CodecH264Reader() : CodecPacketReader()
49{}
50
51bool CodecH264Reader::ReadOnePacket(std::ifstream &ioIn, char *buf, uint32_t &filledCount)
52{
53    // read start code first
54    ioIn.read(buf, START_CODE_SIZE_FRAME);
55    if (ioIn.eof()) {
56        return true;
57    }
58
59    char *temp = buf;
60    temp += START_CODE_SIZE_FRAME;
61    bool ret = true;
62    while (!ioIn.eof()) {
63        ioIn.read(temp, 1);
64        if (*temp == START_CODE) {
65            // check start code
66            if ((temp[START_CODE_OFFSET_ONE] == 0) && (temp[START_CODE_OFFSET_SEC] == 0) &&
67                (temp[START_CODE_OFFSET_THIRD] == 0)) {
68                ioIn.seekg(-START_CODE_SIZE_FRAME, std::ios_base::cur);
69                temp -= (START_CODE_SIZE_FRAME - 1);
70                ret = false;
71                break;
72            } else if ((temp[START_CODE_OFFSET_ONE] == 0) && (temp[START_CODE_OFFSET_SEC] == 0)) {
73                ioIn.seekg(-START_CODE_SIZE_SLICE, std::ios_base::cur);
74                temp -= (START_CODE_SIZE_SLICE - 1);
75                ret = false;
76                break;
77            }
78        }
79        temp++;
80    }
81    filledCount = (temp - buf);
82    return ret;
83}
84
85CodecMpeg4Reader::CodecMpeg4Reader() : CodecPacketReader()
86{}
87
88bool CodecMpeg4Reader::ReadOnePacket(std::ifstream &ioIn, char *buf, uint32_t &filledCount)
89{
90    ioIn.read(buf, START_CODE_SIZE_SLICE);
91    if (ioIn.eof()) {
92        return true;
93    }
94
95    char *temp = buf;
96    temp += START_CODE_SIZE_SLICE;
97    bool ret = true;
98    bool findVop = false;
99    while (!ioIn.eof()) {
100        ioIn.read(temp, 1);
101        // check start code
102        if ((*temp == VOP_START) && (temp[START_CODE_OFFSET_ONE] == START_CODE) && (temp[START_CODE_OFFSET_SEC] == 0) &&
103            (temp[START_CODE_OFFSET_THIRD] == 0)) {
104            findVop = true;
105        }
106        if (findVop && (*temp == START_CODE) && (temp[START_CODE_OFFSET_ONE] == 0) &&
107            (temp[START_CODE_OFFSET_SEC] == 0)) {
108            temp -= START_CODE_SIZE_SLICE - 1;
109            ioIn.seekg(START_CODE_OFFSET_THIRD, std::ios_base::cur);
110            ret = false;
111            break;
112        }
113        temp++;
114    }
115    filledCount = (temp - buf);
116    return ret;
117}
118
119CodecVp9Reader::CodecVp9Reader() : CodecPacketReader()
120{}
121
122bool CodecVp9Reader::ReadOnePacket(std::ifstream &ioIn, char *buf, uint32_t &filledCount)
123{
124    // vp9 saved in trunk, use ffmpeg to save vp9 to .vp9 file, the format like this:
125    // len(4 bytes, little-end, length of vp9 data) + vp9 data
126    filledCount = 0;
127    ioIn.read(reinterpret_cast<char *>(&filledCount), sizeof(filledCount));
128    if (ioIn.eof()) {
129        return true;
130    }
131    filledCount = ntohl(filledCount);
132    ioIn.read(buf, filledCount);
133    if (ioIn.eof()) {
134        return true;
135    }
136    return false;
137}