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