2013-11-18 23:39:51 +01:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
|
2014-02-21 05:26:53 +01:00
|
|
|
#include "third_party/nestegg/include/nestegg/nestegg.h"
|
2013-11-18 23:39:51 +01:00
|
|
|
|
|
|
|
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;
|
2013-12-14 03:53:31 +01:00
|
|
|
if (nestegg_init(&webm_ctx->nestegg_ctx, io, NULL, -1))
|
2013-11-18 23:39:51 +01:00
|
|
|
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) {
|
2014-01-15 23:01:38 +01:00
|
|
|
vpx_ctx->fourcc = VP8_FOURCC;
|
2013-11-18 23:39:51 +01:00
|
|
|
} else if (codec_id == NESTEGG_CODEC_VP9) {
|
2014-01-15 23:01:38 +01:00
|
|
|
vpx_ctx->fourcc = VP9_FOURCC;
|
2013-11-18 23:39:51 +01:00
|
|
|
} else {
|
2014-04-14 23:21:47 +02:00
|
|
|
fprintf(stderr, "Not VPx video, quitting.\n");
|
|
|
|
goto fail;
|
2013-11-18 23:39:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2014-04-15 00:21:22 +02:00
|
|
|
int status;
|
2013-11-18 23:39:51 +01:00
|
|
|
|
|
|
|
do {
|
|
|
|
/* End of this packet, get another. */
|
2013-12-14 04:03:08 +01:00
|
|
|
if (webm_ctx->pkt) {
|
2013-11-18 23:39:51 +01:00
|
|
|
nestegg_free_packet(webm_ctx->pkt);
|
2013-12-14 04:03:08 +01:00
|
|
|
webm_ctx->pkt = NULL;
|
|
|
|
}
|
2013-11-18 23:39:51 +01:00
|
|
|
|
2014-04-15 00:21:22 +02:00
|
|
|
status = nestegg_read_packet(webm_ctx->nestegg_ctx, &webm_ctx->pkt);
|
|
|
|
if (status <= 0)
|
|
|
|
return status ? status : 1;
|
|
|
|
|
|
|
|
if (nestegg_packet_track(webm_ctx->pkt, &track))
|
|
|
|
return -1;
|
2013-11-18 23:39:51 +01:00
|
|
|
} while (track != webm_ctx->video_track);
|
|
|
|
|
|
|
|
if (nestegg_packet_count(webm_ctx->pkt, &webm_ctx->chunks))
|
2014-04-15 00:21:22 +02:00
|
|
|
return -1;
|
2013-11-18 23:39:51 +01:00
|
|
|
|
|
|
|
webm_ctx->chunk = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nestegg_packet_data(webm_ctx->pkt, webm_ctx->chunk,
|
|
|
|
buffer, bytes_in_buffer)) {
|
2014-04-15 00:21:22 +02:00
|
|
|
return -1;
|
2013-11-18 23:39:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
2014-04-18 08:41:24 +02:00
|
|
|
fprintf(stderr, "Failed to guess framerate (no Cues), set to 30fps.\n");
|
2013-11-18 23:39:51 +01:00
|
|
|
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) {
|
2013-12-14 04:03:08 +01:00
|
|
|
if (webm_ctx && webm_ctx->nestegg_ctx) {
|
|
|
|
if (webm_ctx->pkt)
|
|
|
|
nestegg_free_packet(webm_ctx->pkt);
|
2013-11-18 23:39:51 +01:00
|
|
|
nestegg_destroy(webm_ctx->nestegg_ctx);
|
2013-12-14 04:03:08 +01:00
|
|
|
}
|
2013-11-18 23:39:51 +01:00
|
|
|
}
|