Upload UI
[lsnes.git] / src / library / oggopus.cpp
blobda8e93284095d49ba092ad0931c23a84fb614e9c
1 #include "oggopus.hpp"
2 #include <cstring>
3 #include "serialization.hpp"
4 #include "minmax.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");
18 if(p[8] & 0xF0)
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");
22 if(!p[9])
23 throw std::runtime_error("Zero channels not allowed");
24 h.version = p[8];
25 h.channels = p[9];
26 h.preskip = read16ule(&p[10]);
27 h.rate = read32ule(&p[12]);
28 h.gain = read16sle(&p[16]);
29 h.map_family = p[18];
30 memset(h.chanmap, 255, sizeof(h.chanmap));
31 if(h.map_family) {
32 h.streams = p[19];
33 h.coupled = p[20];
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");
42 } else {
43 h.streams = 1;
44 if(h.channels > 2)
45 throw std::runtime_error("Only 1 or 2 channels allowed with mapping family 0");
46 h.coupled = (h.channels == 2) ? 1 : 0;
47 h.chanmap[0] = 0;
48 if(h.channels == 2) h.chanmap[1] = 1;
50 return h;
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");
65 if(p.size() < 12)
66 throw std::runtime_error("OggOpus header packet truncated");
67 //Scan the thing.
68 size_t itr = 8;
69 size_t oitr = 8;
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]);
77 itr += 4;
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]);
82 if(itr > p.size())
83 throw std::runtime_error("OggOpus header packet truncated");
84 h.comments.push_back(std::string(&p[oitr + 4], &p[itr]));
85 oitr = itr;
87 return h;
90 struct ogg_page serialize_oggopus_header(struct oggopus_header& header) throw(std::runtime_error)
92 struct ogg_page page;
93 unsigned char buffer[276];
94 size_t bsize = 19;
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;
117 } else
118 bsize = 19;
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);
123 page.set_bos(true);
124 return page;
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)
130 size_t needed = 8;
131 needed += tags.vendor.length();
132 needed += 4;
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);
139 size_t itr = 0;
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());
145 itr += 4;
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;
153 size_t written = 0;
154 while(true) {
155 ogg_page q;
156 q.set_continue(next_page != 1);
157 q.set_bos(false);
158 q.set_eos(false);
159 q.set_granulepos(0);
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);
165 output(q);
166 if(complete)
167 break;
168 written = ptr - &contents[0];
172 uint8_t opus_packet_tick_count(const uint8_t* packet, size_t packetsize)
174 if(packetsize < 1)
175 return 0;
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) {
181 case 0: return x;
182 case 1: return x << 1;
183 case 2: return x << 1;
184 case 3: return (z <= 48) ? z : 0;