Libav
|
00001 /* 00002 * Ogg bitstream support 00003 * Luca Barbato <lu_zero@gentoo.org> 00004 * Based on tcvp implementation 00005 * 00006 */ 00007 00033 #include <stdio.h> 00034 #include "oggdec.h" 00035 #include "avformat.h" 00036 #include "vorbiscomment.h" 00037 00038 #define MAX_PAGE_SIZE 65307 00039 #define DECODER_BUFFER_SIZE MAX_PAGE_SIZE 00040 00041 static const struct ogg_codec * const ogg_codecs[] = { 00042 &ff_skeleton_codec, 00043 &ff_dirac_codec, 00044 &ff_speex_codec, 00045 &ff_vorbis_codec, 00046 &ff_theora_codec, 00047 &ff_flac_codec, 00048 &ff_old_dirac_codec, 00049 &ff_old_flac_codec, 00050 &ff_ogm_video_codec, 00051 &ff_ogm_audio_codec, 00052 &ff_ogm_text_codec, 00053 &ff_ogm_old_codec, 00054 NULL 00055 }; 00056 00057 //FIXME We could avoid some structure duplication 00058 static int 00059 ogg_save (AVFormatContext * s) 00060 { 00061 struct ogg *ogg = s->priv_data; 00062 struct ogg_state *ost = 00063 av_malloc(sizeof (*ost) + (ogg->nstreams-1) * sizeof (*ogg->streams)); 00064 int i; 00065 ost->pos = url_ftell (s->pb); 00066 ost->curidx = ogg->curidx; 00067 ost->next = ogg->state; 00068 ost->nstreams = ogg->nstreams; 00069 memcpy(ost->streams, ogg->streams, ogg->nstreams * sizeof(*ogg->streams)); 00070 00071 for (i = 0; i < ogg->nstreams; i++){ 00072 struct ogg_stream *os = ogg->streams + i; 00073 os->buf = av_malloc (os->bufsize); 00074 memset (os->buf, 0, os->bufsize); 00075 memcpy (os->buf, ost->streams[i].buf, os->bufpos); 00076 } 00077 00078 ogg->state = ost; 00079 00080 return 0; 00081 } 00082 00083 static int 00084 ogg_restore (AVFormatContext * s, int discard) 00085 { 00086 struct ogg *ogg = s->priv_data; 00087 ByteIOContext *bc = s->pb; 00088 struct ogg_state *ost = ogg->state; 00089 int i; 00090 00091 if (!ost) 00092 return 0; 00093 00094 ogg->state = ost->next; 00095 00096 if (!discard){ 00097 struct ogg_stream *old_streams = ogg->streams; 00098 00099 for (i = 0; i < ogg->nstreams; i++) 00100 av_free (ogg->streams[i].buf); 00101 00102 url_fseek (bc, ost->pos, SEEK_SET); 00103 ogg->curidx = ost->curidx; 00104 ogg->nstreams = ost->nstreams; 00105 ogg->streams = av_realloc (ogg->streams, 00106 ogg->nstreams * sizeof (*ogg->streams)); 00107 00108 if (ogg->streams) { 00109 memcpy(ogg->streams, ost->streams, 00110 ost->nstreams * sizeof(*ogg->streams)); 00111 } else { 00112 av_free(old_streams); 00113 ogg->nstreams = 0; 00114 } 00115 } 00116 00117 av_free (ost); 00118 00119 return 0; 00120 } 00121 00122 static int 00123 ogg_reset (struct ogg * ogg) 00124 { 00125 int i; 00126 00127 for (i = 0; i < ogg->nstreams; i++){ 00128 struct ogg_stream *os = ogg->streams + i; 00129 os->bufpos = 0; 00130 os->pstart = 0; 00131 os->psize = 0; 00132 os->granule = -1; 00133 os->lastpts = AV_NOPTS_VALUE; 00134 os->lastdts = AV_NOPTS_VALUE; 00135 os->sync_pos = -1; 00136 os->page_pos = 0; 00137 os->nsegs = 0; 00138 os->segp = 0; 00139 os->incomplete = 0; 00140 } 00141 00142 ogg->curidx = -1; 00143 00144 return 0; 00145 } 00146 00147 static const struct ogg_codec * 00148 ogg_find_codec (uint8_t * buf, int size) 00149 { 00150 int i; 00151 00152 for (i = 0; ogg_codecs[i]; i++) 00153 if (size >= ogg_codecs[i]->magicsize && 00154 !memcmp (buf, ogg_codecs[i]->magic, ogg_codecs[i]->magicsize)) 00155 return ogg_codecs[i]; 00156 00157 return NULL; 00158 } 00159 00160 static int 00161 ogg_new_stream (AVFormatContext * s, uint32_t serial) 00162 { 00163 00164 struct ogg *ogg = s->priv_data; 00165 int idx = ogg->nstreams++; 00166 AVStream *st; 00167 struct ogg_stream *os; 00168 00169 ogg->streams = av_realloc (ogg->streams, 00170 ogg->nstreams * sizeof (*ogg->streams)); 00171 memset (ogg->streams + idx, 0, sizeof (*ogg->streams)); 00172 os = ogg->streams + idx; 00173 os->serial = serial; 00174 os->bufsize = DECODER_BUFFER_SIZE; 00175 os->buf = av_malloc(os->bufsize); 00176 os->header = -1; 00177 00178 st = av_new_stream (s, idx); 00179 if (!st) 00180 return AVERROR(ENOMEM); 00181 00182 av_set_pts_info(st, 64, 1, 1000000); 00183 00184 return idx; 00185 } 00186 00187 static int 00188 ogg_new_buf(struct ogg *ogg, int idx) 00189 { 00190 struct ogg_stream *os = ogg->streams + idx; 00191 uint8_t *nb = av_malloc(os->bufsize); 00192 int size = os->bufpos - os->pstart; 00193 if(os->buf){ 00194 memcpy(nb, os->buf + os->pstart, size); 00195 av_free(os->buf); 00196 } 00197 os->buf = nb; 00198 os->bufpos = size; 00199 os->pstart = 0; 00200 00201 return 0; 00202 } 00203 00204 static int 00205 ogg_read_page (AVFormatContext * s, int *str) 00206 { 00207 ByteIOContext *bc = s->pb; 00208 struct ogg *ogg = s->priv_data; 00209 struct ogg_stream *os; 00210 int i = 0; 00211 int flags, nsegs; 00212 uint64_t gp; 00213 uint32_t serial; 00214 uint32_t seq; 00215 uint32_t crc; 00216 int size, idx; 00217 uint8_t sync[4]; 00218 int sp = 0; 00219 00220 if (get_buffer (bc, sync, 4) < 4) 00221 return -1; 00222 00223 do{ 00224 int c; 00225 00226 if (sync[sp & 3] == 'O' && 00227 sync[(sp + 1) & 3] == 'g' && 00228 sync[(sp + 2) & 3] == 'g' && sync[(sp + 3) & 3] == 'S') 00229 break; 00230 00231 c = url_fgetc (bc); 00232 if (c < 0) 00233 return -1; 00234 sync[sp++ & 3] = c; 00235 }while (i++ < MAX_PAGE_SIZE); 00236 00237 if (i >= MAX_PAGE_SIZE){ 00238 av_log (s, AV_LOG_INFO, "ogg, can't find sync word\n"); 00239 return -1; 00240 } 00241 00242 if (url_fgetc (bc) != 0) /* version */ 00243 return -1; 00244 00245 flags = url_fgetc (bc); 00246 gp = get_le64 (bc); 00247 serial = get_le32 (bc); 00248 seq = get_le32 (bc); 00249 crc = get_le32 (bc); 00250 nsegs = url_fgetc (bc); 00251 00252 idx = ogg_find_stream (ogg, serial); 00253 if (idx < 0){ 00254 idx = ogg_new_stream (s, serial); 00255 if (idx < 0) 00256 return -1; 00257 } 00258 00259 os = ogg->streams + idx; 00260 os->page_pos = url_ftell(bc) - 27; 00261 00262 if(os->psize > 0) 00263 ogg_new_buf(ogg, idx); 00264 00265 if (get_buffer (bc, os->segments, nsegs) < nsegs) 00266 return -1; 00267 00268 os->nsegs = nsegs; 00269 os->segp = 0; 00270 00271 size = 0; 00272 for (i = 0; i < nsegs; i++) 00273 size += os->segments[i]; 00274 00275 if (flags & OGG_FLAG_CONT || os->incomplete){ 00276 if (!os->psize){ 00277 while (os->segp < os->nsegs){ 00278 int seg = os->segments[os->segp++]; 00279 os->pstart += seg; 00280 if (seg < 255) 00281 break; 00282 } 00283 os->sync_pos = os->page_pos; 00284 } 00285 }else{ 00286 os->psize = 0; 00287 os->sync_pos = os->page_pos; 00288 } 00289 00290 if (os->bufsize - os->bufpos < size){ 00291 uint8_t *nb = av_malloc (os->bufsize *= 2); 00292 memcpy (nb, os->buf, os->bufpos); 00293 av_free (os->buf); 00294 os->buf = nb; 00295 } 00296 00297 if (get_buffer (bc, os->buf + os->bufpos, size) < size) 00298 return -1; 00299 00300 os->bufpos += size; 00301 os->granule = gp; 00302 os->flags = flags; 00303 00304 if (str) 00305 *str = idx; 00306 00307 return 0; 00308 } 00309 00310 static int 00311 ogg_packet (AVFormatContext * s, int *str, int *dstart, int *dsize, int64_t *fpos) 00312 { 00313 struct ogg *ogg = s->priv_data; 00314 int idx, i; 00315 struct ogg_stream *os; 00316 int complete = 0; 00317 int segp = 0, psize = 0; 00318 00319 #if 0 00320 av_log (s, AV_LOG_DEBUG, "ogg_packet: curidx=%i\n", ogg->curidx); 00321 #endif 00322 00323 do{ 00324 idx = ogg->curidx; 00325 00326 while (idx < 0){ 00327 if (ogg_read_page (s, &idx) < 0) 00328 return -1; 00329 } 00330 00331 os = ogg->streams + idx; 00332 00333 #if 0 00334 av_log (s, AV_LOG_DEBUG, 00335 "ogg_packet: idx=%d pstart=%d psize=%d segp=%d nsegs=%d\n", 00336 idx, os->pstart, os->psize, os->segp, os->nsegs); 00337 #endif 00338 00339 if (!os->codec){ 00340 if (os->header < 0){ 00341 os->codec = ogg_find_codec (os->buf, os->bufpos); 00342 if (!os->codec){ 00343 os->header = 0; 00344 return 0; 00345 } 00346 }else{ 00347 return 0; 00348 } 00349 } 00350 00351 segp = os->segp; 00352 psize = os->psize; 00353 00354 while (os->segp < os->nsegs){ 00355 int ss = os->segments[os->segp++]; 00356 os->psize += ss; 00357 if (ss < 255){ 00358 complete = 1; 00359 break; 00360 } 00361 } 00362 00363 if (!complete && os->segp == os->nsegs){ 00364 ogg->curidx = -1; 00365 os->incomplete = 1; 00366 } 00367 }while (!complete); 00368 00369 #if 0 00370 av_log (s, AV_LOG_DEBUG, 00371 "ogg_packet: idx %i, frame size %i, start %i\n", 00372 idx, os->psize, os->pstart); 00373 #endif 00374 00375 if (os->granule == -1) 00376 av_log(s, AV_LOG_WARNING, "Page at %lld is missing granule\n", os->page_pos); 00377 00378 ogg->curidx = idx; 00379 os->incomplete = 0; 00380 00381 if (os->header) { 00382 os->header = os->codec->header (s, idx); 00383 if (!os->header){ 00384 os->segp = segp; 00385 os->psize = psize; 00386 if (!ogg->headers) 00387 s->data_offset = os->sync_pos; 00388 ogg->headers = 1; 00389 }else{ 00390 os->pstart += os->psize; 00391 os->psize = 0; 00392 } 00393 } else { 00394 os->pflags = 0; 00395 os->pduration = 0; 00396 if (os->codec && os->codec->packet) 00397 os->codec->packet (s, idx); 00398 if (str) 00399 *str = idx; 00400 if (dstart) 00401 *dstart = os->pstart; 00402 if (dsize) 00403 *dsize = os->psize; 00404 if (fpos) 00405 *fpos = os->sync_pos; 00406 os->pstart += os->psize; 00407 os->psize = 0; 00408 os->sync_pos = os->page_pos; 00409 } 00410 00411 // determine whether there are more complete packets in this page 00412 // if not, the page's granule will apply to this packet 00413 os->page_end = 1; 00414 for (i = os->segp; i < os->nsegs; i++) 00415 if (os->segments[i] < 255) { 00416 os->page_end = 0; 00417 break; 00418 } 00419 00420 if (os->segp == os->nsegs) 00421 ogg->curidx = -1; 00422 00423 return 0; 00424 } 00425 00426 static int 00427 ogg_get_headers (AVFormatContext * s) 00428 { 00429 struct ogg *ogg = s->priv_data; 00430 00431 do{ 00432 if (ogg_packet (s, NULL, NULL, NULL, NULL) < 0) 00433 return -1; 00434 }while (!ogg->headers); 00435 00436 #if 0 00437 av_log (s, AV_LOG_DEBUG, "found headers\n"); 00438 #endif 00439 00440 return 0; 00441 } 00442 00443 static int 00444 ogg_get_length (AVFormatContext * s) 00445 { 00446 struct ogg *ogg = s->priv_data; 00447 int i; 00448 int64_t size, end; 00449 00450 if(url_is_streamed(s->pb)) 00451 return 0; 00452 00453 // already set 00454 if (s->duration != AV_NOPTS_VALUE) 00455 return 0; 00456 00457 size = url_fsize(s->pb); 00458 if(size < 0) 00459 return 0; 00460 end = size > MAX_PAGE_SIZE? size - MAX_PAGE_SIZE: 0; 00461 00462 ogg_save (s); 00463 url_fseek (s->pb, end, SEEK_SET); 00464 00465 while (!ogg_read_page (s, &i)){ 00466 if (ogg->streams[i].granule != -1 && ogg->streams[i].granule != 0 && 00467 ogg->streams[i].codec) { 00468 s->streams[i]->duration = 00469 ogg_gptopts (s, i, ogg->streams[i].granule, NULL); 00470 if (s->streams[i]->start_time != AV_NOPTS_VALUE) 00471 s->streams[i]->duration -= s->streams[i]->start_time; 00472 } 00473 } 00474 00475 ogg_restore (s, 0); 00476 00477 return 0; 00478 } 00479 00480 00481 static int 00482 ogg_read_header (AVFormatContext * s, AVFormatParameters * ap) 00483 { 00484 struct ogg *ogg = s->priv_data; 00485 int i; 00486 ogg->curidx = -1; 00487 //linear headers seek from start 00488 if (ogg_get_headers (s) < 0){ 00489 return -1; 00490 } 00491 00492 for (i = 0; i < ogg->nstreams; i++) 00493 if (ogg->streams[i].header < 0) 00494 ogg->streams[i].codec = NULL; 00495 00496 //linear granulepos seek from end 00497 ogg_get_length (s); 00498 00499 //fill the extradata in the per codec callbacks 00500 return 0; 00501 } 00502 00503 static int64_t ogg_calc_pts(AVFormatContext *s, int idx, int64_t *dts) 00504 { 00505 struct ogg *ogg = s->priv_data; 00506 struct ogg_stream *os = ogg->streams + idx; 00507 int64_t pts = AV_NOPTS_VALUE; 00508 00509 if (dts) 00510 *dts = AV_NOPTS_VALUE; 00511 00512 if (os->lastpts != AV_NOPTS_VALUE) { 00513 pts = os->lastpts; 00514 os->lastpts = AV_NOPTS_VALUE; 00515 } 00516 if (os->lastdts != AV_NOPTS_VALUE) { 00517 if (dts) 00518 *dts = os->lastdts; 00519 os->lastdts = AV_NOPTS_VALUE; 00520 } 00521 if (os->page_end) { 00522 if (os->granule != -1LL) { 00523 if (os->codec && os->codec->granule_is_start) 00524 pts = ogg_gptopts(s, idx, os->granule, dts); 00525 else 00526 os->lastpts = ogg_gptopts(s, idx, os->granule, &os->lastdts); 00527 os->granule = -1LL; 00528 } 00529 } 00530 return pts; 00531 } 00532 00533 static int 00534 ogg_read_packet (AVFormatContext * s, AVPacket * pkt) 00535 { 00536 struct ogg *ogg; 00537 struct ogg_stream *os; 00538 int idx = -1; 00539 int pstart, psize; 00540 int64_t fpos, pts, dts; 00541 00542 //Get an ogg packet 00543 retry: 00544 do{ 00545 if (ogg_packet (s, &idx, &pstart, &psize, &fpos) < 0) 00546 return AVERROR(EIO); 00547 }while (idx < 0 || !s->streams[idx]); 00548 00549 ogg = s->priv_data; 00550 os = ogg->streams + idx; 00551 00552 // pflags might not be set until after this 00553 pts = ogg_calc_pts(s, idx, &dts); 00554 00555 if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) 00556 goto retry; 00557 os->keyframe_seek = 0; 00558 00559 //Alloc a pkt 00560 if (av_new_packet (pkt, psize) < 0) 00561 return AVERROR(EIO); 00562 pkt->stream_index = idx; 00563 memcpy (pkt->data, os->buf + pstart, psize); 00564 00565 pkt->pts = pts; 00566 pkt->dts = dts; 00567 pkt->flags = os->pflags; 00568 pkt->duration = os->pduration; 00569 pkt->pos = fpos; 00570 00571 return psize; 00572 } 00573 00574 00575 static int 00576 ogg_read_close (AVFormatContext * s) 00577 { 00578 struct ogg *ogg = s->priv_data; 00579 int i; 00580 00581 for (i = 0; i < ogg->nstreams; i++){ 00582 av_free (ogg->streams[i].buf); 00583 av_free (ogg->streams[i].private); 00584 } 00585 av_free (ogg->streams); 00586 return 0; 00587 } 00588 00589 00590 static int64_t ogg_read_timestamp (AVFormatContext * s, int stream_index, 00591 int64_t * pos_arg, int64_t pos_limit) 00592 { 00593 struct ogg *ogg = s->priv_data; 00594 ByteIOContext *bc = s->pb; 00595 int64_t pts = AV_NOPTS_VALUE; 00596 int i = -1; 00597 url_fseek(bc, *pos_arg, SEEK_SET); 00598 ogg_reset(ogg); 00599 00600 while (url_ftell(bc) < pos_limit && !ogg_packet(s, &i, NULL, NULL, pos_arg)) { 00601 if (i == stream_index) { 00602 struct ogg_stream *os = ogg->streams + stream_index; 00603 pts = ogg_calc_pts(s, i, NULL); 00604 if (os->keyframe_seek && !(os->pflags & AV_PKT_FLAG_KEY)) 00605 pts = AV_NOPTS_VALUE; 00606 } 00607 if (pts != AV_NOPTS_VALUE) 00608 break; 00609 } 00610 ogg_reset(ogg); 00611 return pts; 00612 } 00613 00614 static int ogg_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) 00615 { 00616 struct ogg *ogg = s->priv_data; 00617 struct ogg_stream *os = ogg->streams + stream_index; 00618 int ret; 00619 00620 // Try seeking to a keyframe first. If this fails (very possible), 00621 // av_seek_frame will fall back to ignoring keyframes 00622 if (s->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO 00623 && !(flags & AVSEEK_FLAG_ANY)) 00624 os->keyframe_seek = 1; 00625 00626 ret = av_seek_frame_binary(s, stream_index, timestamp, flags); 00627 os = ogg->streams + stream_index; 00628 if (ret < 0) 00629 os->keyframe_seek = 0; 00630 return ret; 00631 } 00632 00633 static int ogg_probe(AVProbeData *p) 00634 { 00635 if (p->buf[0] == 'O' && p->buf[1] == 'g' && 00636 p->buf[2] == 'g' && p->buf[3] == 'S' && 00637 p->buf[4] == 0x0 && p->buf[5] <= 0x7 ) 00638 return AVPROBE_SCORE_MAX; 00639 else 00640 return 0; 00641 } 00642 00643 AVInputFormat ogg_demuxer = { 00644 "ogg", 00645 NULL_IF_CONFIG_SMALL("Ogg"), 00646 sizeof (struct ogg), 00647 ogg_probe, 00648 ogg_read_header, 00649 ogg_read_packet, 00650 ogg_read_close, 00651 ogg_read_seek, 00652 ogg_read_timestamp, 00653 .extensions = "ogg", 00654 .metadata_conv = ff_vorbiscomment_metadata_conv, 00655 .flags = AVFMT_GENERIC_INDEX, 00656 };