| /* | 
 |  *  Copyright (c) 2013 The WebM project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "./webmdec.h" | 
 |  | 
 | #include <stdarg.h> | 
 |  | 
 | #include "third_party/nestegg/include/nestegg/nestegg.h" | 
 |  | 
 | static int nestegg_read_cb(void *buffer, size_t length, void *userdata) { | 
 |   FILE *f = userdata; | 
 |  | 
 |   if (fread(buffer, 1, length, f) < length) { | 
 |     if (ferror(f)) | 
 |       return -1; | 
 |     if (feof(f)) | 
 |       return 0; | 
 |   } | 
 |   return 1; | 
 | } | 
 |  | 
 | static int nestegg_seek_cb(int64_t offset, int whence, void *userdata) { | 
 |   switch (whence) { | 
 |     case NESTEGG_SEEK_SET: | 
 |       whence = SEEK_SET; | 
 |       break; | 
 |     case NESTEGG_SEEK_CUR: | 
 |       whence = SEEK_CUR; | 
 |       break; | 
 |     case NESTEGG_SEEK_END: | 
 |       whence = SEEK_END; | 
 |       break; | 
 |   }; | 
 |   return fseek(userdata, (int32_t)offset, whence) ? -1 : 0; | 
 | } | 
 |  | 
 | static int64_t nestegg_tell_cb(void *userdata) { | 
 |   return ftell(userdata); | 
 | } | 
 |  | 
 | static void nestegg_log_cb(nestegg *context, | 
 |                            unsigned int severity, | 
 |                            char const *format, ...) { | 
 |   va_list ap; | 
 |   va_start(ap, format); | 
 |   vfprintf(stderr, format, ap); | 
 |   fprintf(stderr, "\n"); | 
 |   va_end(ap); | 
 | } | 
 |  | 
 | int file_is_webm(struct WebmInputContext *webm_ctx, | 
 |                  struct VpxInputContext *vpx_ctx) { | 
 |   uint32_t i, n; | 
 |   int track_type = -1; | 
 |   int codec_id; | 
 |  | 
 |   nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0}; | 
 |   nestegg_video_params params; | 
 |  | 
 |   io.userdata = vpx_ctx->file; | 
 |   if (nestegg_init(&webm_ctx->nestegg_ctx, io, NULL, -1)) | 
 |     goto fail; | 
 |  | 
 |   if (nestegg_track_count(webm_ctx->nestegg_ctx, &n)) | 
 |     goto fail; | 
 |  | 
 |   for (i = 0; i < n; i++) { | 
 |     track_type = nestegg_track_type(webm_ctx->nestegg_ctx, i); | 
 |  | 
 |     if (track_type == NESTEGG_TRACK_VIDEO) | 
 |       break; | 
 |     else if (track_type < 0) | 
 |       goto fail; | 
 |   } | 
 |  | 
 |   codec_id = nestegg_track_codec_id(webm_ctx->nestegg_ctx, i); | 
 |   if (codec_id == NESTEGG_CODEC_VP8) { | 
 |     vpx_ctx->fourcc = VP8_FOURCC; | 
 |   } else if (codec_id == NESTEGG_CODEC_VP9) { | 
 |     vpx_ctx->fourcc = VP9_FOURCC; | 
 |   } else { | 
 |     fatal("Not VPx video, quitting.\n"); | 
 |   } | 
 |  | 
 |   webm_ctx->video_track = i; | 
 |  | 
 |   if (nestegg_track_video_params(webm_ctx->nestegg_ctx, i, ¶ms)) | 
 |     goto fail; | 
 |  | 
 |   vpx_ctx->framerate.denominator = 0; | 
 |   vpx_ctx->framerate.numerator = 0; | 
 |   vpx_ctx->width = params.width; | 
 |   vpx_ctx->height = params.height; | 
 |  | 
 |   return 1; | 
 |  | 
 |  fail: | 
 |   webm_ctx->nestegg_ctx = NULL; | 
 |   rewind(vpx_ctx->file); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | int webm_read_frame(struct WebmInputContext *webm_ctx, | 
 |                     uint8_t **buffer, | 
 |                     size_t *bytes_in_buffer, | 
 |                     size_t *buffer_size) { | 
 |   if (webm_ctx->chunk >= webm_ctx->chunks) { | 
 |     uint32_t track; | 
 |  | 
 |     do { | 
 |       /* End of this packet, get another. */ | 
 |       if (webm_ctx->pkt) { | 
 |         nestegg_free_packet(webm_ctx->pkt); | 
 |         webm_ctx->pkt = NULL; | 
 |       } | 
 |  | 
 |       if (nestegg_read_packet(webm_ctx->nestegg_ctx, &webm_ctx->pkt) <= 0 || | 
 |           nestegg_packet_track(webm_ctx->pkt, &track)) { | 
 |         return 1; | 
 |       } | 
 |     } while (track != webm_ctx->video_track); | 
 |  | 
 |     if (nestegg_packet_count(webm_ctx->pkt, &webm_ctx->chunks)) | 
 |       return 1; | 
 |  | 
 |     webm_ctx->chunk = 0; | 
 |   } | 
 |  | 
 |   if (nestegg_packet_data(webm_ctx->pkt, webm_ctx->chunk, | 
 |                           buffer, bytes_in_buffer)) { | 
 |     return 1; | 
 |   } | 
 |  | 
 |   webm_ctx->chunk++; | 
 |   return 0; | 
 | } | 
 |  | 
 | int webm_guess_framerate(struct WebmInputContext *webm_ctx, | 
 |                          struct VpxInputContext *vpx_ctx) { | 
 |   uint32_t i; | 
 |   uint64_t tstamp = 0; | 
 |  | 
 |   /* Check to see if we can seek before we parse any data. */ | 
 |   if (nestegg_track_seek(webm_ctx->nestegg_ctx, webm_ctx->video_track, 0)) { | 
 |     warn("Failed to guess framerate (no Cues), set to 30fps.\n"); | 
 |     vpx_ctx->framerate.numerator = 30; | 
 |     vpx_ctx->framerate.denominator  = 1; | 
 |     return 0; | 
 |   } | 
 |  | 
 |   /* Guess the framerate. Read up to 1 second, or 50 video packets, | 
 |    * whichever comes first. | 
 |    */ | 
 |   for (i = 0; tstamp < 1000000000 && i < 50;) { | 
 |     nestegg_packet *pkt; | 
 |     uint32_t track; | 
 |  | 
 |     if (nestegg_read_packet(webm_ctx->nestegg_ctx, &pkt) <= 0) | 
 |       break; | 
 |  | 
 |     nestegg_packet_track(pkt, &track); | 
 |     if (track == webm_ctx->video_track) { | 
 |       nestegg_packet_tstamp(pkt, &tstamp); | 
 |       ++i; | 
 |     } | 
 |  | 
 |     nestegg_free_packet(pkt); | 
 |   } | 
 |  | 
 |   if (nestegg_track_seek(webm_ctx->nestegg_ctx, webm_ctx->video_track, 0)) | 
 |     goto fail; | 
 |  | 
 |   vpx_ctx->framerate.numerator = (i - 1) * 1000000; | 
 |   vpx_ctx->framerate.denominator = (int)(tstamp / 1000); | 
 |   return 0; | 
 |  | 
 |  fail: | 
 |   nestegg_destroy(webm_ctx->nestegg_ctx); | 
 |   webm_ctx->nestegg_ctx = NULL; | 
 |   rewind(vpx_ctx->file); | 
 |   return 1; | 
 | } | 
 |  | 
 | void webm_free(struct WebmInputContext *webm_ctx) { | 
 |   if (webm_ctx && webm_ctx->nestegg_ctx) { | 
 |     if (webm_ctx->pkt) | 
 |       nestegg_free_packet(webm_ctx->pkt); | 
 |     nestegg_destroy(webm_ctx->nestegg_ctx); | 
 |   } | 
 | } |