3 #include "serialization.hpp"
6 struct oggopus_header
parse_oggopus_header(struct ogg_packet
& packet
) throw(std::runtime_error
)
8 struct oggopus_header h
;
9 if(!packet
.get_atomic())
10 throw std::runtime_error("OggOpus header page must have one complete packet");
11 if(packet
.get_granulepos() != 0)
12 throw std::runtime_error("OggOpus header page must have granulepos 0");
13 if(!packet
.get_on_bos_page() || packet
.get_on_eos_page())
14 throw std::runtime_error("OggOpus header page must be first but not last page");
15 const std::vector
<uint8_t>& p
= packet
.get_vector();
16 if(p
.size() < 9 || memcmp(&p
[0], "OpusHead", 8))
17 throw std::runtime_error("Bad OggOpus header magic");
19 throw std::runtime_error("Unsupported OggOpus version");
20 if(p
.size() < 19 || (p
[18] && p
.size() < 21U + p
[9]))
21 throw std::runtime_error("OggOpus header packet truncated");
23 throw std::runtime_error("Zero channels not allowed");
26 h
.preskip
= read16ule(&p
[10]);
27 h
.rate
= read32ule(&p
[12]);
28 h
.gain
= read16sle(&p
[16]);
30 memset(h
.chanmap
, 255, sizeof(h
.chanmap
));
34 if(h
.coupled
> h
.streams
)
35 throw std::runtime_error("More coupled streams than total streams.");
36 if(static_cast<int>(h
.streams
) > 255 - h
.coupled
)
37 throw std::runtime_error("Maximum of 255 physical channels exceeded");
38 memcpy(h
.chanmap
, &p
[21], h
.channels
);
39 for(unsigned i
= 0; i
< h
.channels
; i
++)
40 if(h
.chanmap
[i
] != 255 && h
.chanmap
[i
] > h
.streams
+ h
.coupled
)
41 throw std::runtime_error("Logical channel mapped to invalid physical channel");
45 throw std::runtime_error("Only 1 or 2 channels allowed with mapping family 0");
46 h
.coupled
= (h
.channels
== 2) ? 1 : 0;
48 if(h
.channels
== 2) h
.chanmap
[1] = 1;
53 struct oggopus_tags
parse_oggopus_tags(struct ogg_packet
& packet
) throw(std::bad_alloc
, std::runtime_error
)
55 struct oggopus_tags h
;
56 if(!packet
.get_first_page() || !packet
.get_last_page())
57 throw std::runtime_error("OggOpus tags packet must be alone on its pages");
58 if(packet
.get_granulepos() != 0)
59 throw std::runtime_error("OggOpus header page must have granulepos 0");
60 if(packet
.get_on_bos_page())
61 throw std::runtime_error("OggOpus tags page must not be first page");
62 const std::vector
<uint8_t>& p
= packet
.get_vector();
63 if(p
.size() < 8 || memcmp(&p
[0], "OpusTags", 8))
64 throw std::runtime_error("Bad OggOpus tags magic");
66 throw std::runtime_error("OggOpus header packet truncated");
70 itr
= itr
+ 4 + read32ule(&p
[itr
]);
71 if(itr
+ 4 > p
.size())
72 throw std::runtime_error("OggOpus header packet truncated");
73 h
.vendor
= std::string(&p
[oitr
+ 4], &p
[itr
]);
74 if(itr
+ 4 > p
.size())
75 throw std::runtime_error("OggOpus header packet truncated");
76 uint32_t headers
= read32ule(&p
[itr
]);
78 for(uint32_t i
= 0; i
< headers
; i
++) {
79 if(itr
+ 4 > p
.size())
80 throw std::runtime_error("OggOpus header packet truncated");
81 itr
= itr
+ 4 + read32ule(&p
[itr
]);
83 throw std::runtime_error("OggOpus header packet truncated");
84 h
.comments
.push_back(std::string(&p
[oitr
+ 4], &p
[itr
]));
90 struct ogg_page
serialize_oggopus_header(struct oggopus_header
& header
) throw(std::runtime_error
)
93 unsigned char buffer
[276];
95 if(header
.version
!= 1)
96 throw std::runtime_error("Don't how to serialize this oggopus version");
97 if(!header
.channels
|| (header
.channels
> 2 && !header
.map_family
))
98 throw std::runtime_error("Illegal channel count");
99 if(header
.map_family
&& static_cast<int>(header
.streams
) > 255 - header
.coupled
)
100 throw std::runtime_error("Maximum of 255 physical channels exceeded");
101 if(header
.map_family
)
102 for(unsigned i
= 0; i
< header
.channels
; i
++)
103 if(header
.chanmap
[i
] != 255 && header
.chanmap
[i
] > header
.streams
+ header
.coupled
)
104 throw std::runtime_error("Logical channel mapped to invalid physical channel");
105 write64ube(buffer
, 0x4F70757348656164ULL
);
106 buffer
[8] = header
.version
;
107 buffer
[9] = header
.channels
;
108 write16ule(buffer
+ 10, header
.preskip
);
109 write32ule(buffer
+ 12, header
.rate
);
110 write16sle(buffer
+ 16, header
.gain
);
111 buffer
[18] = header
.map_family
;
112 if(header
.map_family
) {
113 buffer
[19] = header
.streams
;
114 buffer
[20] = header
.coupled
;
115 memcpy(buffer
+ 21, header
.chanmap
, header
.channels
);
116 bsize
= 21 + header
.channels
;
119 if(!page
.append_packet(buffer
, bsize
))
120 throw std::runtime_error("Header packet too large");
121 page
.set_granulepos(0);
122 page
.set_sequence(0);
127 uint32_t serialize_oggopus_tags(struct oggopus_tags
& tags
, std::function
<void(const ogg_page
& p
)> output
,
128 uint32_t strmid
) throw(std::bad_alloc
, std::runtime_error
)
131 needed
+= tags
.vendor
.length();
133 for(auto i
: tags
.comments
)
134 needed
+= (i
.length() + 4);
136 //TODO: Do without this buffer.
137 std::vector
<uint8_t> contents
;
138 contents
.resize(needed
);
140 write64ube(&contents
[0], 0x4F70757354616773ULL
);
141 write32ule(&contents
[8], tags
.vendor
.length());
142 std::copy(tags
.vendor
.begin(), tags
.vendor
.end(), reinterpret_cast<char*>(&contents
[12]));
143 itr
= 12 + tags
.vendor
.length();
144 write32ule(&contents
[itr
], tags
.comments
.size());
146 for(auto i
: tags
.comments
) {
147 write32ule(&contents
[itr
], i
.length());
148 std::copy(i
.begin(), i
.end(), reinterpret_cast<char*>(&contents
[itr
+ 4]));
149 itr
+= (i
.length() + 4);
152 uint32_t next_page
= 1;
156 q
.set_continue(next_page
!= 1);
160 q
.set_stream(strmid
);
161 q
.set_sequence(next_page
++);
162 const uint8_t* ptr
= &contents
[written
];
163 size_t szr
= needed
- written
;
164 bool complete
= q
.append_packet_incomplete(ptr
, szr
);
168 written
= ptr
- &contents
[0];
172 uint8_t opus_packet_tick_count(const uint8_t* packet
, size_t packetsize
)
176 uint8_t x
= ((packet
[0] >= 0x70) ? 1 : 4) << ((packet
[0] >> 3) & 3);
177 x
= min(x
, (uint8_t)24);
178 uint8_t y
= (packetsize
< 2) ? 255 : (packet
[1] & 0x3F);
179 uint16_t z
= (uint16_t)x
* y
;
180 switch(packet
[0] & 3) {
182 case 1: return x
<< 1;
183 case 2: return x
<< 1;
184 case 3: return (z
<= 48) ? z
: 0;