Some tweaks to Lua docs
[lsnes.git] / src / library / opus-ogg.cpp
blob1b35a5a6d02a84c6d7f0802a040e9bd622ebd61d
1 #include "opus-ogg.hpp"
2 #include <cstring>
3 #include "serialization.hpp"
4 #include "minmax.hpp"
6 namespace opus
8 void ogg_header::parse(struct ogg::packet& packet) throw(std::runtime_error)
10 struct ogg_header h;
11 if(!packet.get_atomic())
12 throw std::runtime_error("OggOpus header page must have one complete packet");
13 if(packet.get_granulepos() != 0)
14 throw std::runtime_error("OggOpus header page must have granulepos 0");
15 if(!packet.get_on_bos_page() || packet.get_on_eos_page())
16 throw std::runtime_error("OggOpus header page must be first but not last page");
17 const std::vector<uint8_t>& p = packet.get_vector();
18 if(p.size() < 9 || memcmp(&p[0], "OpusHead", 8))
19 throw std::runtime_error("Bad OggOpus header magic");
20 if(p[8] & 0xF0)
21 throw std::runtime_error("Unsupported OggOpus version");
22 if(p.size() < 19 || (p[18] && p.size() < 21U + p[9]))
23 throw std::runtime_error("OggOpus header packet truncated");
24 if(!p[9])
25 throw std::runtime_error("Zero channels not allowed");
26 h.version = p[8];
27 h.channels = p[9];
28 h.preskip = serialization::u16l(&p[10]);
29 h.rate = serialization::u32l(&p[12]);
30 h.gain = serialization::s16l(&p[16]);
31 h.map_family = p[18];
32 memset(h.chanmap, 255, sizeof(h.chanmap));
33 if(h.map_family) {
34 h.streams = p[19];
35 h.coupled = p[20];
36 if(h.coupled > h.streams)
37 throw std::runtime_error("More coupled streams than total streams.");
38 if(static_cast<int>(h.streams) > 255 - h.coupled)
39 throw std::runtime_error("Maximum of 255 physical channels exceeded");
40 memcpy(h.chanmap, &p[21], h.channels);
41 for(unsigned i = 0; i < h.channels; i++)
42 if(h.chanmap[i] != 255 && h.chanmap[i] > h.streams + h.coupled)
43 throw std::runtime_error("Logical channel mapped to invalid physical channel");
44 } else {
45 h.streams = 1;
46 if(h.channels > 2)
47 throw std::runtime_error("Only 1 or 2 channels allowed with mapping family 0");
48 h.coupled = (h.channels == 2) ? 1 : 0;
49 h.chanmap[0] = 0;
50 if(h.channels == 2) h.chanmap[1] = 1;
52 *this = h;
55 void ogg_tags::parse(struct ogg::packet& packet) throw(std::bad_alloc, std::runtime_error)
57 struct ogg_tags h;
58 if(!packet.get_first_page() || !packet.get_last_page())
59 throw std::runtime_error("OggOpus tags packet must be alone on its pages");
60 if(packet.get_granulepos() != 0)
61 throw std::runtime_error("OggOpus header page must have granulepos 0");
62 if(packet.get_on_bos_page())
63 throw std::runtime_error("OggOpus tags page must not be first page");
64 const std::vector<uint8_t>& p = packet.get_vector();
65 if(p.size() < 8 || memcmp(&p[0], "OpusTags", 8))
66 throw std::runtime_error("Bad OggOpus tags magic");
67 if(p.size() < 12)
68 throw std::runtime_error("OggOpus header packet truncated");
69 //Scan the thing.
70 size_t itr = 8;
71 size_t oitr = 8;
72 itr = itr + 4 + serialization::u32l(&p[itr]);
73 if(itr + 4 > p.size())
74 throw std::runtime_error("OggOpus header packet truncated");
75 h.vendor = std::string(&p[oitr + 4], &p[itr]);
76 if(itr + 4 > p.size())
77 throw std::runtime_error("OggOpus header packet truncated");
78 uint32_t headers = serialization::u32l(&p[itr]);
79 itr += 4;
80 for(uint32_t i = 0; i < headers; i++) {
81 if(itr + 4 > p.size())
82 throw std::runtime_error("OggOpus header packet truncated");
83 itr = itr + 4 + serialization::u32l(&p[itr]);
84 if(itr > p.size())
85 throw std::runtime_error("OggOpus header packet truncated");
86 h.comments.push_back(std::string(&p[oitr + 4], &p[itr]));
87 oitr = itr;
89 *this = h;
92 ogg::page ogg_header::serialize() throw(std::runtime_error)
94 struct ogg::page page;
95 unsigned char buffer[276];
96 size_t bsize = 19;
97 if(version != 1)
98 throw std::runtime_error("Don't how to serialize this oggopus version");
99 if(!channels || (channels > 2 && !map_family))
100 throw std::runtime_error("Illegal channel count");
101 if(map_family && static_cast<int>(streams) > 255 - coupled)
102 throw std::runtime_error("Maximum of 255 physical channels exceeded");
103 if(map_family)
104 for(unsigned i = 0; i < channels; i++)
105 if(chanmap[i] != 255 && chanmap[i] > streams + coupled)
106 throw std::runtime_error("Logical channel mapped to invalid physical channel");
107 serialization::u64b(buffer, 0x4F70757348656164ULL);
108 buffer[8] = version;
109 buffer[9] = channels;
110 serialization::u16l(buffer + 10, preskip);
111 serialization::u32l(buffer + 12, rate);
112 serialization::s16l(buffer + 16, gain);
113 buffer[18] = map_family;
114 if(map_family) {
115 buffer[19] = streams;
116 buffer[20] = coupled;
117 memcpy(buffer + 21, chanmap, channels);
118 bsize = 21 + channels;
119 } else
120 bsize = 19;
121 if(!page.append_packet(buffer, bsize))
122 throw std::runtime_error("Header packet too large");
123 page.set_granulepos(0);
124 page.set_sequence(0);
125 page.set_bos(true);
126 return page;
129 uint32_t ogg_tags::serialize(std::function<void(const ogg::page& p)> output,
130 uint32_t strmid) throw(std::bad_alloc, std::runtime_error)
132 size_t needed = 8;
133 needed += vendor.length();
134 needed += 4;
135 for(auto i : comments)
136 needed += (i.length() + 4);
138 //TODO: Do without this buffer.
139 std::vector<uint8_t> contents;
140 contents.resize(needed);
141 size_t itr = 0;
142 serialization::u64b(&contents[0], 0x4F70757354616773ULL);
143 serialization::u32l(&contents[8], vendor.length());
144 std::copy(vendor.begin(), vendor.end(), reinterpret_cast<char*>(&contents[12]));
145 itr = 12 + vendor.length();
146 serialization::u32l(&contents[itr], comments.size());
147 itr += 4;
148 for(auto i : comments) {
149 serialization::u32l(&contents[itr], i.length());
150 std::copy(i.begin(), i.end(), reinterpret_cast<char*>(&contents[itr + 4]));
151 itr += (i.length() + 4);
154 uint32_t next_page = 1;
155 size_t written = 0;
156 while(true) {
157 ogg::page q;
158 q.set_continue(next_page != 1);
159 q.set_bos(false);
160 q.set_eos(false);
161 q.set_granulepos(0);
162 q.set_stream(strmid);
163 q.set_sequence(next_page++);
164 const uint8_t* ptr = &contents[written];
165 size_t szr = needed - written;
166 bool complete = q.append_packet_incomplete(ptr, szr);
167 output(q);
168 if(complete)
169 break;
170 written = ptr - &contents[0];