Mercurial > libavformat.hg
comparison matroskaenc.c @ 2447:54f0053730cf libavformat
Write the Seek Head element
author | conrad |
---|---|
date | Wed, 05 Sep 2007 00:23:19 +0000 |
parents | b2f9523ee424 |
children | bc52328116ac |
comparison
equal
deleted
inserted
replaced
2446:b2f9523ee424 | 2447:54f0053730cf |
---|---|
22 #include "avformat.h" | 22 #include "avformat.h" |
23 #include "riff.h" | 23 #include "riff.h" |
24 #include "xiph.h" | 24 #include "xiph.h" |
25 #include "matroska.h" | 25 #include "matroska.h" |
26 | 26 |
27 typedef struct mkv_seekhead_entry { | |
28 unsigned int elementid; | |
29 uint64_t segmentpos; | |
30 } mkv_seekhead_entry; | |
31 | |
32 typedef struct mkv_seekhead { | |
33 offset_t filepos; | |
34 offset_t segment_offset; // the file offset to the beginning of the segment | |
35 int reserved_size; // -1 if appending to file | |
36 int max_entries; | |
37 mkv_seekhead_entry *entries; | |
38 int num_entries; | |
39 } mkv_seekhead; | |
40 | |
27 typedef struct MatroskaMuxContext { | 41 typedef struct MatroskaMuxContext { |
28 offset_t segment; | 42 offset_t segment; |
43 offset_t segment_offset; | |
29 offset_t cluster; | 44 offset_t cluster; |
30 uint64_t cluster_pts; | 45 uint64_t cluster_pts; |
31 offset_t duration_offset; | 46 offset_t duration_offset; |
32 uint64_t duration; | 47 uint64_t duration; |
48 mkv_seekhead *main_seekhead; | |
49 mkv_seekhead *cluster_seekhead; | |
33 } MatroskaMuxContext; | 50 } MatroskaMuxContext; |
34 | 51 |
35 static void put_ebml_id(ByteIOContext *pb, unsigned int id) | 52 static void put_ebml_id(ByteIOContext *pb, unsigned int id) |
36 { | 53 { |
37 if (id >= 0x3fffff) | 54 if (id >= 0x3fffff) |
39 if (id >= 0x7fff) | 56 if (id >= 0x7fff) |
40 put_byte(pb, id >> 16); | 57 put_byte(pb, id >> 16); |
41 if (id >= 0xff) | 58 if (id >= 0xff) |
42 put_byte(pb, id >> 8); | 59 put_byte(pb, id >> 8); |
43 put_byte(pb, id); | 60 put_byte(pb, id); |
61 } | |
62 | |
63 static int ebml_id_size(unsigned int id) | |
64 { | |
65 if (id >= 0x3fffff) | |
66 return 4; | |
67 if (id >= 0x7fff) | |
68 return 3; | |
69 if (id >= 0xff) | |
70 return 2; | |
71 return 1; | |
44 } | 72 } |
45 | 73 |
46 // XXX: test this thoroughly and get rid of minbytes hack (currently needed to | 74 // XXX: test this thoroughly and get rid of minbytes hack (currently needed to |
47 // use up all of the space reserved in start_ebml_master) | 75 // use up all of the space reserved in start_ebml_master) |
48 static void put_ebml_size(ByteIOContext *pb, uint64_t size, int minbytes) | 76 static void put_ebml_size(ByteIOContext *pb, uint64_t size, int minbytes) |
126 url_fseek(pb, start - 8, SEEK_SET); | 154 url_fseek(pb, start - 8, SEEK_SET); |
127 put_ebml_size(pb, pos - start, 7); | 155 put_ebml_size(pb, pos - start, 7); |
128 url_fseek(pb, pos, SEEK_SET); | 156 url_fseek(pb, pos, SEEK_SET); |
129 } | 157 } |
130 | 158 |
159 // initializes a mkv_seekhead element to be ready to index level 1 matroska elements | |
160 // if numelements is greater than 0, it reserves enough space for that many elements | |
161 // at the current file position and writes the seekhead there, otherwise the seekhead | |
162 // will be appended to the file when end_mkv_seekhead() is called | |
163 static mkv_seekhead * mkv_start_seekhead(ByteIOContext *pb, offset_t segment_offset, int numelements) | |
164 { | |
165 mkv_seekhead *new_seekhead = av_mallocz(sizeof(mkv_seekhead)); | |
166 if (new_seekhead == NULL) | |
167 return NULL; | |
168 | |
169 new_seekhead->segment_offset = segment_offset; | |
170 | |
171 if (numelements > 0) { | |
172 new_seekhead->filepos = url_ftell(pb); | |
173 // 21 bytes max for a seek entry, 10 bytes max for the SeekHead ID and size, | |
174 // and 3 bytes to guarantee that an EBML void element will fit afterwards | |
175 // XXX: 28 bytes right now because begin_ebml_master() reserves more than necessary | |
176 new_seekhead->reserved_size = numelements * 28 + 13; | |
177 new_seekhead->max_entries = numelements; | |
178 put_ebml_void(pb, new_seekhead->reserved_size); | |
179 } | |
180 return new_seekhead; | |
181 } | |
182 | |
183 static int mkv_add_seekhead_entry(mkv_seekhead *seekhead, unsigned int elementid, uint64_t filepos) | |
184 { | |
185 mkv_seekhead_entry *entries = seekhead->entries; | |
186 int new_entry = seekhead->num_entries; | |
187 | |
188 // don't store more elements than we reserved space for | |
189 if (seekhead->max_entries > 0 && seekhead->max_entries <= seekhead->num_entries) | |
190 return -1; | |
191 | |
192 entries = av_realloc(entries, (seekhead->num_entries + 1) * sizeof(mkv_seekhead_entry)); | |
193 if (entries == NULL) | |
194 return -1; | |
195 | |
196 entries[new_entry].elementid = elementid; | |
197 entries[new_entry].segmentpos = filepos - seekhead->segment_offset; | |
198 | |
199 seekhead->entries = entries; | |
200 seekhead->num_entries++; | |
201 | |
202 return 0; | |
203 } | |
204 | |
205 // returns the file offset where the seekhead was written and frees the seekhead | |
206 static offset_t mkv_write_seekhead(ByteIOContext *pb, mkv_seekhead *seekhead) | |
207 { | |
208 offset_t metaseek, seekentry, currentpos; | |
209 int i; | |
210 | |
211 currentpos = url_ftell(pb); | |
212 | |
213 if (seekhead->reserved_size > 0) | |
214 url_fseek(pb, seekhead->filepos, SEEK_SET); | |
215 | |
216 metaseek = start_ebml_master(pb, MATROSKA_ID_SEEKHEAD); | |
217 for (i = 0; i < seekhead->num_entries; i++) { | |
218 mkv_seekhead_entry *entry = &seekhead->entries[i]; | |
219 | |
220 seekentry = start_ebml_master(pb, MATROSKA_ID_SEEKENTRY); | |
221 | |
222 put_ebml_id(pb, MATROSKA_ID_SEEKID); | |
223 put_ebml_size(pb, ebml_id_size(entry->elementid), 0); | |
224 put_ebml_id(pb, entry->elementid); | |
225 | |
226 put_ebml_uint(pb, MATROSKA_ID_SEEKPOSITION, entry->segmentpos); | |
227 end_ebml_master(pb, seekentry); | |
228 } | |
229 end_ebml_master(pb, metaseek); | |
230 | |
231 if (seekhead->reserved_size > 0) { | |
232 uint64_t remaining = seekhead->filepos + seekhead->reserved_size - url_ftell(pb); | |
233 put_ebml_void(pb, remaining); | |
234 url_fseek(pb, currentpos, SEEK_SET); | |
235 | |
236 currentpos = seekhead->filepos; | |
237 } | |
238 av_free(seekhead->entries); | |
239 av_free(seekhead); | |
240 | |
241 return currentpos; | |
242 } | |
243 | |
131 static int put_xiph_codecpriv(ByteIOContext *pb, AVCodecContext *codec) | 244 static int put_xiph_codecpriv(ByteIOContext *pb, AVCodecContext *codec) |
132 { | 245 { |
133 offset_t codecprivate; | 246 offset_t codecprivate; |
134 uint8_t *header_start[3]; | 247 uint8_t *header_start[3]; |
135 int header_len[3]; | 248 int header_len[3]; |
165 { | 278 { |
166 MatroskaMuxContext *mkv = s->priv_data; | 279 MatroskaMuxContext *mkv = s->priv_data; |
167 ByteIOContext *pb = &s->pb; | 280 ByteIOContext *pb = &s->pb; |
168 offset_t tracks; | 281 offset_t tracks; |
169 int i, j; | 282 int i, j; |
283 | |
284 if (mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_TRACKS, url_ftell(pb)) < 0) | |
285 return -1; | |
170 | 286 |
171 tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS); | 287 tracks = start_ebml_master(pb, MATROSKA_ID_TRACKS); |
172 for (i = 0; i < s->nb_streams; i++) { | 288 for (i = 0; i < s->nb_streams; i++) { |
173 AVStream *st = s->streams[i]; | 289 AVStream *st = s->streams[i]; |
174 AVCodecContext *codec = st->codec; | 290 AVCodecContext *codec = st->codec; |
265 put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 2); | 381 put_ebml_uint (pb, EBML_ID_DOCTYPEVERSION , 2); |
266 put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2); | 382 put_ebml_uint (pb, EBML_ID_DOCTYPEREADVERSION , 2); |
267 end_ebml_master(pb, ebml_header); | 383 end_ebml_master(pb, ebml_header); |
268 | 384 |
269 mkv->segment = start_ebml_master(pb, MATROSKA_ID_SEGMENT); | 385 mkv->segment = start_ebml_master(pb, MATROSKA_ID_SEGMENT); |
386 mkv->segment_offset = url_ftell(pb); | |
387 | |
388 // we write 2 seek heads - one at the end of the file to point to each cluster, and | |
389 // one at the beginning to point to all other level one elements (including the seek | |
390 // head at the end of the file), which isn't more than 10 elements if we only write one | |
391 // of each other currently defined level 1 element | |
392 mkv->main_seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 10); | |
393 mkv->cluster_seekhead = mkv_start_seekhead(pb, mkv->segment_offset, 0); | |
394 | |
395 if (mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_INFO, url_ftell(pb)) < 0) | |
396 return -1; | |
270 | 397 |
271 segment_info = start_ebml_master(pb, MATROSKA_ID_INFO); | 398 segment_info = start_ebml_master(pb, MATROSKA_ID_INFO); |
272 put_ebml_uint(pb, MATROSKA_ID_TIMECODESCALE, 1000000); | 399 put_ebml_uint(pb, MATROSKA_ID_TIMECODESCALE, 1000000); |
273 if (strlen(s->title)) | 400 if (strlen(s->title)) |
274 put_ebml_string(pb, MATROSKA_ID_TITLE, s->title); | 401 put_ebml_string(pb, MATROSKA_ID_TITLE, s->title); |
285 end_ebml_master(pb, segment_info); | 412 end_ebml_master(pb, segment_info); |
286 | 413 |
287 if (mkv_write_tracks(s) < 0) | 414 if (mkv_write_tracks(s) < 0) |
288 return -1; | 415 return -1; |
289 | 416 |
417 if (mkv_add_seekhead_entry(mkv->cluster_seekhead, MATROSKA_ID_CLUSTER, url_ftell(pb)) < 0) | |
418 return -1; | |
419 | |
290 mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER); | 420 mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER); |
291 put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, 0); | 421 put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, 0); |
292 mkv->cluster_pts = 0; | 422 mkv->cluster_pts = 0; |
293 | 423 |
294 return 0; | 424 return 0; |
301 offset_t block; | 431 offset_t block; |
302 | 432 |
303 // start a new cluster every 5 MB or 5 sec | 433 // start a new cluster every 5 MB or 5 sec |
304 if (url_ftell(pb) > mkv->cluster + 5*1024*1024 || pkt->pts > mkv->cluster_pts + 5000) { | 434 if (url_ftell(pb) > mkv->cluster + 5*1024*1024 || pkt->pts > mkv->cluster_pts + 5000) { |
305 end_ebml_master(pb, mkv->cluster); | 435 end_ebml_master(pb, mkv->cluster); |
436 | |
437 if (mkv_add_seekhead_entry(mkv->cluster_seekhead, MATROSKA_ID_CLUSTER, url_ftell(pb)) < 0) | |
438 return -1; | |
439 | |
306 mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER); | 440 mkv->cluster = start_ebml_master(pb, MATROSKA_ID_CLUSTER); |
307 put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, pkt->pts); | 441 put_ebml_uint(pb, MATROSKA_ID_CLUSTERTIMECODE, pkt->pts); |
308 mkv->cluster_pts = pkt->pts; | 442 mkv->cluster_pts = pkt->pts; |
309 } | 443 } |
310 | 444 |
321 | 455 |
322 static int mkv_write_trailer(AVFormatContext *s) | 456 static int mkv_write_trailer(AVFormatContext *s) |
323 { | 457 { |
324 MatroskaMuxContext *mkv = s->priv_data; | 458 MatroskaMuxContext *mkv = s->priv_data; |
325 ByteIOContext *pb = &s->pb; | 459 ByteIOContext *pb = &s->pb; |
326 offset_t currentpos; | 460 offset_t currentpos, second_seekhead; |
327 | 461 |
328 end_ebml_master(pb, mkv->cluster); | 462 end_ebml_master(pb, mkv->cluster); |
463 | |
464 second_seekhead = mkv_write_seekhead(pb, mkv->cluster_seekhead); | |
465 mkv_add_seekhead_entry(mkv->main_seekhead, MATROSKA_ID_SEEKHEAD, second_seekhead); | |
466 mkv_write_seekhead(pb, mkv->main_seekhead); | |
329 | 467 |
330 // update the duration | 468 // update the duration |
331 currentpos = url_ftell(pb); | 469 currentpos = url_ftell(pb); |
332 url_fseek(pb, mkv->duration_offset, SEEK_SET); | 470 url_fseek(pb, mkv->duration_offset, SEEK_SET); |
333 put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration); | 471 put_ebml_float(pb, MATROSKA_ID_DURATION, mkv->duration); |