9 void write32(char* x
, uint64_t v
)
18 void jmd_dumper::video(uint64_t ts
, uint32_t* memory
, uint32_t width
, uint32_t height
)
23 //We'll compress the frame here.
24 f
.data
= compress_frame(memory
, width
, height
);
29 void jmd_dumper::audio(uint64_t ts
, short l
, short r
)
39 jmd_dumper::jmd_dumper(const std::string
& filename
, unsigned level
)
42 jmd
.open(filename
.c_str(), std::ios::out
| std::ios::binary
);
44 throw std::runtime_error("Can't open output JMD file.");
46 //Write the segment tables.
48 //Stream #1 is PCM audio.
49 //Stream #2 is Gameinfo.
53 -1, -1, 0x4A, 0x50, 0x43, 0x52, 0x52, 0x4D, 0x55, 0x4C, 0x54, 0x49, 0x44, 0x55, 0x4D, 0x50,
56 /* Video channel header. */
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 'v', 'i',
58 /* Audio channel header. */
59 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 'a', 'u',
60 /* Gameinfo channel header. */
61 0x00, 0x02, 0x00, 0x05, 0x00, 0x02, 'g', 'i',
62 /* Dummy channel header. */
63 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 'd', 'u'
65 jmd
.write(header
, sizeof(header
));
67 throw std::runtime_error("Can't write JMD header and segment table");
70 jmd_dumper::~jmd_dumper()
78 void jmd_dumper::end(uint64_t ts
)
81 if(last_written_ts
> ts
) {
85 char dummypacket
[8] = {0x00, 0x03};
86 write32(dummypacket
+ 2, ts
- last_written_ts
);
88 jmd
.write(dummypacket
, sizeof(dummypacket
));
90 throw std::runtime_error("Can't write JMD ending dummy packet");
94 void jmd_dumper::gameinfo(const std::string
& gamename
, const std::string
& authors
, uint64_t gametime
,
97 //FIXME: Implement this.
100 void jmd_dumper::flush_buffers(bool force
)
102 while(!frames
.empty() || !samples
.empty()) {
103 if(frames
.empty() || samples
.empty()) {
106 else if(!frames
.empty()) {
107 frame_buffer
& f
= frames
.front();
110 } else if(!samples
.empty()) {
111 sample_buffer
& s
= samples
.front();
117 frame_buffer
& f
= frames
.front();
118 sample_buffer
& s
= samples
.front();
129 void jmd_dumper::flush_frame(frame_buffer
& f
)
131 char videopacketh
[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
132 write32(videopacketh
+ 2, f
.ts
- last_written_ts
);
133 last_written_ts
= f
.ts
;
135 if(f
.data
.size() >= (1ULL << 63))
136 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 63) & 0x7F);
137 if(f
.data
.size() >= (1ULL << 56))
138 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 56) & 0x7F);
139 if(f
.data
.size() >= (1ULL << 49))
140 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 49) & 0x7F);
141 if(f
.data
.size() >= (1ULL << 42))
142 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 42) & 0x7F);
143 if(f
.data
.size() >= (1ULL << 35))
144 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 35) & 0x7F);
145 if(f
.data
.size() >= (1ULL << 28))
146 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 28) & 0x7F);
147 if(f
.data
.size() >= (1ULL << 21))
148 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 21) & 0x7F);
149 if(f
.data
.size() >= (1ULL << 14))
150 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 14) & 0x7F);
151 if(f
.data
.size() >= (1ULL << 7))
152 videopacketh
[7 + lneed
++] = 0x80 | ((f
.data
.size() >> 7) & 0x7F);
153 videopacketh
[7 + lneed
++] = (f
.data
.size() & 0x7F);
155 jmd
.write(videopacketh
, 7 + lneed
);
157 throw std::runtime_error("Can't write JMD video packet header");
158 if(f
.data
.size() > 0)
159 jmd
.write(&f
.data
[0], f
.data
.size());
161 throw std::runtime_error("Can't write JMD video packet body");
164 void jmd_dumper::flush_sample(sample_buffer
& s
)
166 char soundpacket
[12] = {0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04};
167 write32(soundpacket
+ 2, s
.ts
- last_written_ts
);
168 last_written_ts
= s
.ts
;
169 soundpacket
[8] = (s
.l
>> 8) & 0xFF;
170 soundpacket
[9] = s
.l
& 0xFF;
171 soundpacket
[10] = (s
.r
>> 8) & 0xFF;
172 soundpacket
[11] = s
.r
& 0xFF;
173 jmd
.write(soundpacket
, sizeof(soundpacket
));
175 throw std::runtime_error("Can't write JMD sound packet");
178 #define INBUF_PIXELS 4096
179 #define OUTBUF_ADVANCE 4096
181 std::vector
<char> jmd_dumper::compress_frame(uint32_t* memory
, uint32_t width
, uint32_t height
)
183 std::vector
<char> ret
;
185 memset(&stream
, 0, sizeof(stream
));
186 if(deflateInit(&stream
, clevel
) != Z_OK
)
187 throw std::runtime_error("Can't initialize zlib stream");
191 ret
[0] = (width
>> 8);
193 ret
[2] = (height
>> 8);
195 uint8_t input_buffer
[4 * INBUF_PIXELS
];
197 size_t pixels
= static_cast<size_t>(width
) * height
;
198 bool input_clear
= true;
199 bool flushed
= false;
201 uint32_t _magic
= 0x18100800;
202 const uint8_t* magic
= reinterpret_cast<const uint8_t*>(&magic
);
206 for(unsigned i
= 0; i
< INBUF_PIXELS
&& pixel
< pixels
; i
++, pixel
++) {
207 input_buffer
[4 * i
+ 0] = memory
[pixel
];
208 input_buffer
[4 * i
+ 1] = memory
[pixel
] >> 8;
209 input_buffer
[4 * i
+ 2] = memory
[pixel
] >> 16;
210 input_buffer
[4 * i
+ 3] = 0;
215 //Now the input data to compress is in input_buffer, bsize elements.
216 stream
.next_in
= reinterpret_cast<uint8_t*>(input_buffer
);
217 stream
.avail_in
= 4 * bsize
;
219 if(!stream
.avail_out
) {
221 usize
+= (OUTBUF_ADVANCE
- stream
.avail_out
);
223 ret
.resize(usize
+ OUTBUF_ADVANCE
);
224 stream
.next_out
= reinterpret_cast<uint8_t*>(&ret
[usize
]);
225 stream
.avail_out
= OUTBUF_ADVANCE
;
227 int r
= deflate(&stream
, (ptr
== pixels
) ? Z_FINISH
: 0);
228 if(r
== Z_STREAM_END
)
231 throw std::runtime_error("Can't deflate data");
235 usize
+= (OUTBUF_ADVANCE
- stream
.avail_out
);