1/*
2 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "./webmdec.h"
12
13#include <stdarg.h>
14
15#include "third_party/nestegg/include/nestegg/nestegg.h"
16
17static int nestegg_read_cb(void *buffer, size_t length, void *userdata) {
18  FILE *f = userdata;
19
20  if (fread(buffer, 1, length, f) < length) {
21    if (ferror(f))
22      return -1;
23    if (feof(f))
24      return 0;
25  }
26  return 1;
27}
28
29static int nestegg_seek_cb(int64_t offset, int whence, void *userdata) {
30  switch (whence) {
31    case NESTEGG_SEEK_SET:
32      whence = SEEK_SET;
33      break;
34    case NESTEGG_SEEK_CUR:
35      whence = SEEK_CUR;
36      break;
37    case NESTEGG_SEEK_END:
38      whence = SEEK_END;
39      break;
40  };
41  return fseek(userdata, (int32_t)offset, whence) ? -1 : 0;
42}
43
44static int64_t nestegg_tell_cb(void *userdata) {
45  return ftell(userdata);
46}
47
48static void nestegg_log_cb(nestegg *context,
49                           unsigned int severity,
50                           char const *format, ...) {
51  va_list ap;
52  va_start(ap, format);
53  vfprintf(stderr, format, ap);
54  fprintf(stderr, "\n");
55  va_end(ap);
56}
57
58int file_is_webm(struct WebmInputContext *webm_ctx,
59                 struct VpxInputContext *vpx_ctx) {
60  uint32_t i, n;
61  int track_type = -1;
62  int codec_id;
63
64  nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb, 0};
65  nestegg_video_params params;
66
67  io.userdata = vpx_ctx->file;
68  if (nestegg_init(&webm_ctx->nestegg_ctx, io, NULL, -1))
69    goto fail;
70
71  if (nestegg_track_count(webm_ctx->nestegg_ctx, &n))
72    goto fail;
73
74  for (i = 0; i < n; i++) {
75    track_type = nestegg_track_type(webm_ctx->nestegg_ctx, i);
76
77    if (track_type == NESTEGG_TRACK_VIDEO)
78      break;
79    else if (track_type < 0)
80      goto fail;
81  }
82
83  codec_id = nestegg_track_codec_id(webm_ctx->nestegg_ctx, i);
84  if (codec_id == NESTEGG_CODEC_VP8) {
85    vpx_ctx->fourcc = VP8_FOURCC;
86  } else if (codec_id == NESTEGG_CODEC_VP9) {
87    vpx_ctx->fourcc = VP9_FOURCC;
88  } else {
89    fatal("Not VPx video, quitting.\n");
90  }
91
92  webm_ctx->video_track = i;
93
94  if (nestegg_track_video_params(webm_ctx->nestegg_ctx, i, &params))
95    goto fail;
96
97  vpx_ctx->framerate.denominator = 0;
98  vpx_ctx->framerate.numerator = 0;
99  vpx_ctx->width = params.width;
100  vpx_ctx->height = params.height;
101
102  return 1;
103
104 fail:
105  webm_ctx->nestegg_ctx = NULL;
106  rewind(vpx_ctx->file);
107
108  return 0;
109}
110
111int webm_read_frame(struct WebmInputContext *webm_ctx,
112                    uint8_t **buffer,
113                    size_t *bytes_in_buffer,
114                    size_t *buffer_size) {
115  if (webm_ctx->chunk >= webm_ctx->chunks) {
116    uint32_t track;
117
118    do {
119      /* End of this packet, get another. */
120      if (webm_ctx->pkt) {
121        nestegg_free_packet(webm_ctx->pkt);
122        webm_ctx->pkt = NULL;
123      }
124
125      if (nestegg_read_packet(webm_ctx->nestegg_ctx, &webm_ctx->pkt) <= 0 ||
126          nestegg_packet_track(webm_ctx->pkt, &track)) {
127        return 1;
128      }
129    } while (track != webm_ctx->video_track);
130
131    if (nestegg_packet_count(webm_ctx->pkt, &webm_ctx->chunks))
132      return 1;
133
134    webm_ctx->chunk = 0;
135  }
136
137  if (nestegg_packet_data(webm_ctx->pkt, webm_ctx->chunk,
138                          buffer, bytes_in_buffer)) {
139    return 1;
140  }
141
142  webm_ctx->chunk++;
143  return 0;
144}
145
146int webm_guess_framerate(struct WebmInputContext *webm_ctx,
147                         struct VpxInputContext *vpx_ctx) {
148  uint32_t i;
149  uint64_t tstamp = 0;
150
151  /* Check to see if we can seek before we parse any data. */
152  if (nestegg_track_seek(webm_ctx->nestegg_ctx, webm_ctx->video_track, 0)) {
153    warn("Failed to guess framerate (no Cues), set to 30fps.\n");
154    vpx_ctx->framerate.numerator = 30;
155    vpx_ctx->framerate.denominator  = 1;
156    return 0;
157  }
158
159  /* Guess the framerate. Read up to 1 second, or 50 video packets,
160   * whichever comes first.
161   */
162  for (i = 0; tstamp < 1000000000 && i < 50;) {
163    nestegg_packet *pkt;
164    uint32_t track;
165
166    if (nestegg_read_packet(webm_ctx->nestegg_ctx, &pkt) <= 0)
167      break;
168
169    nestegg_packet_track(pkt, &track);
170    if (track == webm_ctx->video_track) {
171      nestegg_packet_tstamp(pkt, &tstamp);
172      ++i;
173    }
174
175    nestegg_free_packet(pkt);
176  }
177
178  if (nestegg_track_seek(webm_ctx->nestegg_ctx, webm_ctx->video_track, 0))
179    goto fail;
180
181  vpx_ctx->framerate.numerator = (i - 1) * 1000000;
182  vpx_ctx->framerate.denominator = (int)(tstamp / 1000);
183  return 0;
184
185 fail:
186  nestegg_destroy(webm_ctx->nestegg_ctx);
187  webm_ctx->nestegg_ctx = NULL;
188  rewind(vpx_ctx->file);
189  return 1;
190}
191
192void webm_free(struct WebmInputContext *webm_ctx) {
193  if (webm_ctx && webm_ctx->nestegg_ctx) {
194    if (webm_ctx->pkt)
195      nestegg_free_packet(webm_ctx->pkt);
196    nestegg_destroy(webm_ctx->nestegg_ctx);
197  }
198}
199