00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "race.h"
00019 #include "mmfile.h"
00020 #include "int_types.h"
00021 #include "macros.h"
00022 #include "utils.h"
00023 #include "logging.h"
00024 #include <math.h>
00025 #include <assert.h>
00026 #include <stdio.h>
00027 #include <memory.h>
00028 #include <stdlib.h>
00029 #include <limits.h>
00030 #include <ogg/ogg.h>
00031 #include <vorbis/codec.h>
00032 #include <theora/theora.h>
00033 #include <SDL.h>
00034
00035 LOG_DEFAULT_CATEGORY(multimedia);
00036
00037
00038
00039
00040
00041
00042
00043 static int
00044 get_page(mm_file * mf, ogg_page * pg)
00045 {
00046 const int bufsize = 8192;
00047 char *p = NULL;
00048 int n = 0;
00049 int res = 0;
00050
00051 assert(mf);
00052
00053 while (0 == (res = ogg_sync_pageout(&mf->sync, pg)))
00054 {
00055 p = ogg_sync_buffer(&mf->sync, bufsize);
00056 if (!p)
00057 {
00058 ERROR1("ogg buffer synchronization failed");
00059 return -1;
00060 }
00061
00062 if (0 == (n = fread(p, 1, bufsize, mf->file)))
00063 return (feof(mf->file)) ? 0 : -1;
00064
00065 if (ogg_sync_wrote(&mf->sync, n))
00066 {
00067 ERROR1("buffer overflow in ogg_sync_wrote");
00068 return -1;
00069 }
00070 }
00071
00072 if (res < 0 || ogg_page_version(pg) != 0)
00073 return -1;
00074 return 1;
00075 }
00076
00077 static int
00078 get_packet(mm_file * mf, ogg_packet * pkt, enum stream_type type)
00079 {
00080 ogg_stream_state *stream, *other;
00081 ogg_page pg;
00082 enum stream_type other_type;
00083 int rv = 0;
00084
00085 assert(mf);
00086 assert(pkt);
00087 switch (type)
00088 {
00089 case MEDIA_VIDEO:
00090 assert(mf->video);
00091 stream = mf->video;
00092 other = mf->audio;
00093 other_type = MEDIA_AUDIO;
00094 break;
00095 case MEDIA_AUDIO:
00096 assert(mf->audio);
00097 stream = mf->audio;
00098 other = mf->video;
00099 other_type = MEDIA_VIDEO;
00100 break;
00101 default:
00102 WARNING2("bad stream type: %d", type);
00103 return -1;
00104 }
00105 if (mf->end_of_stream & type)
00106 return 0;
00107 while (0 == ogg_stream_packetout(stream, pkt))
00108 {
00109 rv = get_page(mf, &pg);
00110 if (rv <= 0)
00111 return rv;
00112 if (ogg_stream_pagein(stream, &pg) < 0)
00113 {
00114 if (other && ogg_stream_pagein(other, &pg) == 0)
00115 {
00116
00117
00118
00119
00120
00121 if (mf->drop_packets & other_type)
00122 {
00123 ogg_packet packet;
00124
00125 while (ogg_stream_packetout(other, &packet))
00126 ;
00127 }
00128 }
00129 else
00130 {
00131 INFO2("got page not associated with any stream, "
00132 "serial 0x%x", ogg_page_serialno(&pg));
00133
00134
00135
00136
00137
00138 }
00139 }
00140 }
00141 mf->end_of_stream |= (!!pkt->e_o_s) * type;
00142 return 1;
00143 }
00144
00145 static int
00146 init_theora(mm_file * mf, ogg_page * pg)
00147 {
00148 int pkts = 0;
00149 int res = 0;
00150 int rval = 0;
00151 theora_info *th_info = NULL;
00152 theora_comment th_comm;
00153 ogg_packet pkt;
00154 ogg_stream_state stream;
00155
00156 assert(mf);
00157 th_info = xmalloc(sizeof(*mf->video_info));
00158 theora_info_init(th_info);
00159 theora_comment_init(&th_comm);
00160 ogg_stream_init(&stream, ogg_page_serialno(pg));
00161
00162 if (ogg_page_packets(pg) != 1 || ogg_page_granulepos(pg) != 0)
00163 goto end;
00164
00165 if (ogg_stream_pagein(&stream, pg))
00166
00167 goto end;
00168
00169
00170 for (pkts = 0; pkts < 3; ++pkts)
00171 {
00172 while ((res = ogg_stream_packetpeek(&stream, &pkt)) != 1)
00173 {
00174 if (res < 0
00175 || get_page(mf, pg) <= 0
00176 || ogg_stream_pagein(&stream, pg) < 0)
00177 {
00178 rval = -1;
00179 goto end;
00180 }
00181 }
00182 switch (theora_decode_header(th_info, &th_comm, &pkt))
00183 {
00184 case 0:
00185 break;
00186 case OC_VERSION:
00187 case OC_NEWPACKET:
00188 INFO1("incompatible theora file");
00189
00190 case OC_BADHEADER:
00191 default:
00192 goto end;
00193 }
00194
00195
00196 ogg_stream_packetout(&stream, &pkt);
00197 }
00198
00199 mf->video_ctx = xmalloc(sizeof(*mf->video_ctx));
00200 mf->video = xmalloc(sizeof(*mf->video));
00201 memcpy(mf->video, &stream, sizeof(stream));
00202 theora_decode_init(mf->video_ctx, th_info);
00203 mf->video_info = th_info;
00204 rval = 1;
00205 end:
00206 theora_comment_clear(&th_comm);
00207 if (rval <= 0)
00208 {
00209 ogg_stream_clear(&stream);
00210 theora_info_clear(th_info);
00211 free(th_info);
00212 mf->video_info = NULL;
00213 }
00214 return rval;
00215 }
00216
00217 static int
00218 init_vorbis(mm_file * mf, ogg_page * pg)
00219 {
00220 int pkts = 0;
00221 int res = 0;
00222 int rval = 0;
00223 vorbis_block *vo_blk = NULL;
00224 vorbis_info *vo_info = NULL;
00225 vorbis_comment vo_comm;
00226 ogg_packet pkt;
00227 ogg_stream_state stream;
00228
00229 assert(mf);
00230 vo_info = xmalloc(sizeof(*vo_info));
00231 vorbis_info_init(vo_info);
00232 vorbis_comment_init(&vo_comm);
00233 ogg_stream_init(&stream, ogg_page_serialno(pg));
00234
00235 if (ogg_page_packets(pg) != 1 || ogg_page_granulepos(pg) != 0)
00236 goto end;
00237
00238 if (ogg_stream_pagein(&stream, pg) < 0)
00239
00240 goto end;
00241
00242
00243
00244
00245 for (pkts = 0; pkts < 3; ++pkts)
00246 {
00247 while ((res = ogg_stream_packetpeek(&stream, &pkt)) != 1)
00248 {
00249 if (res < 0
00250 || get_page(mf, pg) <= 0
00251 || ogg_stream_pagein(&stream, pg) < 0)
00252 {
00253 rval = -1;
00254 goto end;
00255 }
00256 }
00257
00258 switch (vorbis_synthesis_headerin(vo_info, &vo_comm, &pkt))
00259 {
00260 case 0:
00261 break;
00262 case OV_EBADHEADER:
00263 INFO1("bad vorbis header");
00264 case OV_ENOTVORBIS:
00265 default:
00266 goto end;
00267 }
00268
00269
00270 ogg_stream_packetout(&stream, &pkt);
00271 }
00272
00273
00274 mf->audio_ctx = xmalloc(sizeof(*mf->audio_ctx));
00275 mf->audio = xmalloc(sizeof(*mf->audio));
00276 vo_blk = xmalloc(sizeof(*vo_blk));
00277 memcpy(mf->audio, &stream, sizeof(stream));
00278 vorbis_synthesis_init(mf->audio_ctx, vo_info);
00279 vorbis_block_init(mf->audio_ctx, vo_blk);
00280 mf->audio_info = vo_info;
00281 mf->audio_blk = vo_blk;
00282 rval = 1;
00283 end:
00284 vorbis_comment_clear(&vo_comm);
00285 if (rval <= 0)
00286 {
00287 ogg_stream_clear(&stream);
00288 vorbis_info_clear(vo_info);
00289 free(vo_info);
00290 }
00291 return rval;
00292 }
00293
00294 static int
00295 yuv_to_overlay(const mm_file * mf, const yuv_buffer * yuv, SDL_Overlay * ovl)
00296 {
00297 unsigned i, h, w, xoff, yoff;
00298 uint8_t *yp, *up, *vp;
00299
00300 assert(mf);
00301 assert(yuv);
00302 assert(ovl);
00303
00304 h = min(mf->video_info->frame_height, (unsigned) ovl->h);
00305 w = min(mf->video_info->frame_width, (unsigned) ovl->w);
00306 xoff = mf->video_info->offset_x;
00307 yoff = mf->video_info->offset_y;
00308
00309 switch (ovl->format)
00310 {
00311 case SDL_IYUV_OVERLAY:
00312 up = yuv->u;
00313 vp = yuv->v;
00314 break;
00315 case SDL_YV12_OVERLAY:
00316 up = yuv->v;
00317 vp = yuv->u;
00318 break;
00319 default:
00320 WARNING1("only IYUV and YV12 SDL overlay formats supported");
00321 return -1;
00322 }
00323 yp = yuv->y;
00324
00325 switch (mf->video_info->pixelformat)
00326 {
00327 case OC_PF_420:
00328 break;
00329 case OC_PF_422:
00330 case OC_PF_444:
00331 default:
00332 WARNING1("unknown/unsupported theora pixel format");
00333 return -1;
00334 }
00335
00336 if (SDL_LockYUVOverlay(ovl) < 0)
00337 {
00338 WARNING1("unable to lock overlay");
00339 return -1;
00340 }
00341
00342 for (i = 0; i < h; ++i)
00343 {
00344 memcpy(ovl->pixels[0] + i * ovl->pitches[0],
00345 yp + (i + yoff) * yuv->y_stride + xoff, w);
00346 }
00347 xoff /= 2;
00348 yoff /= 2;
00349
00350 w = w / 2 + w % 2;
00351 h = h / 2 + h % 2;
00352
00353 for (i = 0; i < h; ++i)
00354 {
00355 memcpy(ovl->pixels[1] + i * ovl->pitches[1],
00356 up + (i + yoff) * yuv->uv_stride + xoff, w);
00357 memcpy(ovl->pixels[2] + i * ovl->pitches[2],
00358 vp + (i + yoff) * yuv->uv_stride + xoff, w);
00359 }
00360 SDL_UnlockYUVOverlay(ovl);
00361 return 0;
00362 }
00363
00364
00365 int
00366 mm_open_fp(mm_file * mf, FILE * file)
00367 {
00368 int retval = -1;
00369 int res = 0;
00370 int have_vorbis = 0;
00371 int have_theora = 0;
00372 ogg_page pg;
00373
00374 assert(mf);
00375 memset(mf, 0, sizeof(*mf));
00376
00377 mf->file = file;
00378 if (!mf->file)
00379 return retval;
00380 ogg_sync_init(&mf->sync);
00381
00382
00383 if (get_page(mf, &pg) <= 0)
00384 goto err;
00385
00386 DEBUG1("trying theora decoder...");
00387 res = init_theora(mf, &pg);
00388 if (res < 0)
00389 goto err;
00390 else
00391 have_theora = !!res * MEDIA_VIDEO;
00392
00393 DEBUG1("trying vorbis decoder...");
00394 res = init_vorbis(mf, &pg);
00395 if (res < 0)
00396 goto err;
00397 else
00398 have_vorbis = !!res * MEDIA_AUDIO;
00399
00400 if (have_vorbis)
00401 {
00402 unsigned c, r;
00403 mm_audio_info(mf, &c, &r);
00404 INFO3("audio %u channel(s) at %u Hz", c, r);
00405 }
00406 if (have_theora)
00407 {
00408 unsigned w, h; float fps;
00409 mm_video_info(mf, &w, &h, &fps);
00410 INFO4("video %ux%u pixels at %g fps", w, h, fps);
00411 }
00412 return have_vorbis | have_theora;
00413 err:
00414 WARNING1("unable to decode stream");
00415 mm_close(mf);
00416 return retval;
00417 }
00418
00419 int
00420 mm_open(mm_file * mf, const char *fname)
00421 {
00422 assert(mf);
00423 assert(fname);
00424 INFO2("opening file `%s'", fname);
00425 return mm_open_fp(mf, fopen(fname, "rb"));
00426 }
00427
00428 unsigned
00429 mm_ignore(mm_file * mf, unsigned mask)
00430 {
00431 unsigned old = mf->drop_packets;
00432
00433 mf->drop_packets = mask;
00434 return old;
00435 }
00436
00437 int
00438 mm_close(mm_file * mf)
00439 {
00440 assert(mf);
00441 if (mf->file)
00442 {
00443 fclose(mf->file);
00444 mf->file = NULL;
00445 }
00446 if (mf->audio)
00447 {
00448 ogg_stream_destroy(mf->audio);
00449 mf->audio = NULL;
00450 }
00451 if (mf->video)
00452 {
00453 ogg_stream_destroy(mf->video);
00454 mf->video = NULL;
00455 }
00456 if (mf->video_ctx)
00457 {
00458 theora_clear(mf->video_ctx);
00459 free(mf->video_ctx);
00460 mf->video_ctx = NULL;
00461 }
00462 if (mf->video_info)
00463 {
00464 theora_info_clear(mf->video_info);
00465 free(mf->video_info);
00466 mf->video_info = NULL;
00467 }
00468 if (mf->audio_blk)
00469 {
00470 vorbis_block_clear(mf->audio_blk);
00471 free(mf->audio_blk);
00472 mf->audio_blk = NULL;
00473 }
00474 if (mf->audio_ctx)
00475 {
00476 vorbis_dsp_clear(mf->audio_ctx);
00477 free(mf->audio_ctx);
00478 mf->audio_ctx = NULL;
00479 }
00480 if (mf->audio_info)
00481 {
00482 vorbis_info_clear(mf->audio_info);
00483 free(mf->audio_info);
00484 mf->audio_info = NULL;
00485 }
00486 ogg_sync_clear(&mf->sync);
00487 return 0;
00488 }
00489
00490
00491
00492
00493 int
00494 mm_video_info(const mm_file * mf, unsigned *width, unsigned *height,
00495 float *fps)
00496 {
00497 assert(mf);
00498 if (!mf->video)
00499 return -1;
00500 if (width)
00501 *width = mf->video_info->frame_width;
00502 if (height)
00503 *height = mf->video_info->frame_height;
00504 if (fps)
00505 *fps = mf->video_info->fps_numerator
00506 / mf->video_info->fps_denominator;
00507 return 1;
00508 }
00509
00510
00511
00512
00513 int
00514 mm_audio_info(const mm_file * mf, unsigned *channels, unsigned *rate)
00515 {
00516 assert(mf);
00517 if (!mf->audio)
00518 return -1;
00519 if (channels)
00520 *channels = mf->audio_info->channels;
00521 if (rate)
00522 *rate = mf->audio_info->rate;
00523 return 1;
00524 }
00525
00526 int
00527 mm_decode_video(mm_file * mf, SDL_Overlay * ovl)
00528 {
00529 int rv = 0;
00530 ogg_packet pkt;
00531 yuv_buffer yuv;
00532
00533 assert(mf);
00534 if (!mf->video)
00535 return -1;
00536 if (mf->drop_packets & MEDIA_VIDEO)
00537 {
00538 WARNING1("requested decode but MEDIA_VIDEO is set to ignore");
00539 return -1;
00540 }
00541 for (;;)
00542 {
00543 rv = get_packet(mf, &pkt, MEDIA_VIDEO);
00544 if (rv <= 0)
00545 return rv;
00546
00547 if (theora_decode_packetin(mf->video_ctx, &pkt) == 0)
00548 break;
00549 else
00550 {
00551 WARNING1("packet does not contain theora frame");
00552
00553 }
00554 }
00555 theora_decode_YUVout(mf->video_ctx, &yuv);
00556 if (yuv_to_overlay(mf, &yuv, ovl) < 0)
00557 return -1;
00558 return 1;
00559 }
00560
00561
00562
00563 int
00564 mm_decode_audio(mm_file * mf, void *buf, int buflen)
00565 {
00566 const int max_val = UCHAR_MAX;
00567 const int min_val = 0;
00568 const int bytes_per_sample = 1;
00569
00570 int rv = 0, samples = 0, left = 0, total = 0;
00571 unsigned channels = 0;
00572
00573 assert(mf);
00574 if (-1 == mm_audio_info(mf, &channels, NULL))
00575 return -1;
00576 if (mf->drop_packets & MEDIA_AUDIO)
00577 {
00578 WARNING1("requested decode but MEDIA_AUDIO is set to ignore");
00579 return -1;
00580 }
00581
00582
00583 left = buflen;
00584 left = left / channels / bytes_per_sample;
00585
00586 while (left > 0)
00587 {
00588 float **pcm;
00589 ogg_packet pkt;
00590
00591
00592 while (left > 0
00593 && (samples = vorbis_synthesis_pcmout(mf->audio_ctx, &pcm)) > 0)
00594 {
00595 int i = 0;
00596 unsigned ch = 0;
00597
00598 samples = min(samples, left);
00599
00600 for (i = 0; i < samples; ++i)
00601 {
00602 for (ch = 0; ch < channels; ++ch)
00603 {
00604
00605 int val = lrint((pcm[ch][i] + 1.0) / 2.0 * max_val);
00606
00607 if (val > max_val)
00608 val = max_val;
00609 if (val < min_val)
00610 val = min_val;
00611 *((uint8_t *) buf + (total + i) * channels + ch) = val;
00612 }
00613 }
00614
00615 total += samples;
00616 left -= samples;
00617 vorbis_synthesis_read(mf->audio_ctx, samples);
00618
00619 }
00620
00621 for (;;)
00622 {
00623 rv = get_packet(mf, &pkt, MEDIA_AUDIO);
00624 if (rv < 0)
00625 return rv;
00626 else if (rv == 0)
00627 return total * channels * bytes_per_sample;
00628
00629
00630 if (vorbis_synthesis(mf->audio_blk, &pkt) == 0)
00631 {
00632 vorbis_synthesis_blockin(mf->audio_ctx, mf->audio_blk);
00633 break;
00634 }
00635 else
00636 {
00637 WARNING1("packet does not contain a valid vorbis frame");
00638
00639 }
00640 }
00641 }
00642
00643 return total * channels * bytes_per_sample;
00644 }
00645
00646 #if 0
00647 int
00648 mm_convert_audio(mm_file * mf, void *buf, int buflen, SDL_AudioSpec * spec)
00649 {
00650 SDL_AudioCVT cvt;
00651 unsigned channels, rate;
00652 uint8_t *tmp_buf = NULL;
00653 int allocated = 0;
00654 int to_decode = 0;
00655 int decoded = 0;
00656
00657 assert(mf);
00658 assert(spec);
00659
00660 if (-1 == mm_audio_info(mf, &channels, &rate))
00661 return -1;
00662
00663 if (-1 == SDL_BuildAudioCVT(&cvt,
00664 MM_AUDIO_FORMAT, channels, rate,
00665 spec->format, spec->channels, spec->freq))
00666 return -1;
00667
00668
00669 to_decode = buflen / cvt.len_ratio;
00670 if (to_decode > buflen)
00671 {
00672 allocated = 1;
00673 tmp_buf = xmalloc(to_decode);
00674 }
00675 else
00676 tmp_buf = buf;
00677
00678 decoded = mm_decode_audio(mf, tmp_buf, to_decode);
00679 if (decoded <= 0)
00680 return decoded;
00681
00682 cvt.buf = tmp_buf;
00683 cvt.len = decoded;
00684
00685 if (-1 == SDL_ConvertAudio(&cvt))
00686 return -1;
00687
00688 if (allocated)
00689 {
00690 memcpy(buf, tmp_buf, cvt.len * cvt.len_ratio);
00691 free(tmp_buf);
00692 }
00693
00694 return cvt.len * cvt.len_ratio;
00695 }
00696 #endif
00697
00698