webmdec.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. #include "./webmdec.h"
  11. #include <cstring>
  12. #include <cstdio>
  13. #include "third_party/libwebm/mkvparser/mkvparser.h"
  14. #include "third_party/libwebm/mkvparser/mkvreader.h"
  15. namespace {
  16. void reset(struct WebmInputContext *const webm_ctx) {
  17. if (webm_ctx->reader != NULL) {
  18. mkvparser::MkvReader *const reader =
  19. reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
  20. delete reader;
  21. }
  22. if (webm_ctx->segment != NULL) {
  23. mkvparser::Segment *const segment =
  24. reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
  25. delete segment;
  26. }
  27. if (webm_ctx->buffer != NULL) {
  28. delete[] webm_ctx->buffer;
  29. }
  30. webm_ctx->reader = NULL;
  31. webm_ctx->segment = NULL;
  32. webm_ctx->buffer = NULL;
  33. webm_ctx->cluster = NULL;
  34. webm_ctx->block_entry = NULL;
  35. webm_ctx->block = NULL;
  36. webm_ctx->block_frame_index = 0;
  37. webm_ctx->video_track_index = 0;
  38. webm_ctx->timestamp_ns = 0;
  39. webm_ctx->is_key_frame = false;
  40. }
  41. void get_first_cluster(struct WebmInputContext *const webm_ctx) {
  42. mkvparser::Segment *const segment =
  43. reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
  44. const mkvparser::Cluster *const cluster = segment->GetFirst();
  45. webm_ctx->cluster = cluster;
  46. }
  47. void rewind_and_reset(struct WebmInputContext *const webm_ctx,
  48. struct VpxInputContext *const vpx_ctx) {
  49. rewind(vpx_ctx->file);
  50. reset(webm_ctx);
  51. }
  52. } // namespace
  53. int file_is_webm(struct WebmInputContext *webm_ctx,
  54. struct VpxInputContext *vpx_ctx) {
  55. mkvparser::MkvReader *const reader = new mkvparser::MkvReader(vpx_ctx->file);
  56. webm_ctx->reader = reader;
  57. webm_ctx->reached_eos = 0;
  58. mkvparser::EBMLHeader header;
  59. long long pos = 0;
  60. if (header.Parse(reader, pos) < 0) {
  61. rewind_and_reset(webm_ctx, vpx_ctx);
  62. return 0;
  63. }
  64. mkvparser::Segment *segment;
  65. if (mkvparser::Segment::CreateInstance(reader, pos, segment)) {
  66. rewind_and_reset(webm_ctx, vpx_ctx);
  67. return 0;
  68. }
  69. webm_ctx->segment = segment;
  70. if (segment->Load() < 0) {
  71. rewind_and_reset(webm_ctx, vpx_ctx);
  72. return 0;
  73. }
  74. const mkvparser::Tracks *const tracks = segment->GetTracks();
  75. const mkvparser::VideoTrack *video_track = NULL;
  76. for (unsigned long i = 0; i < tracks->GetTracksCount(); ++i) {
  77. const mkvparser::Track *const track = tracks->GetTrackByIndex(i);
  78. if (track->GetType() == mkvparser::Track::kVideo) {
  79. video_track = static_cast<const mkvparser::VideoTrack *>(track);
  80. webm_ctx->video_track_index = static_cast<int>(track->GetNumber());
  81. break;
  82. }
  83. }
  84. if (video_track == NULL || video_track->GetCodecId() == NULL) {
  85. rewind_and_reset(webm_ctx, vpx_ctx);
  86. return 0;
  87. }
  88. if (!strncmp(video_track->GetCodecId(), "V_VP8", 5)) {
  89. vpx_ctx->fourcc = VP8_FOURCC;
  90. } else if (!strncmp(video_track->GetCodecId(), "V_VP9", 5)) {
  91. vpx_ctx->fourcc = VP9_FOURCC;
  92. } else {
  93. rewind_and_reset(webm_ctx, vpx_ctx);
  94. return 0;
  95. }
  96. vpx_ctx->framerate.denominator = 0;
  97. vpx_ctx->framerate.numerator = 0;
  98. vpx_ctx->width = static_cast<uint32_t>(video_track->GetWidth());
  99. vpx_ctx->height = static_cast<uint32_t>(video_track->GetHeight());
  100. get_first_cluster(webm_ctx);
  101. return 1;
  102. }
  103. int webm_read_frame(struct WebmInputContext *webm_ctx, uint8_t **buffer,
  104. size_t *buffer_size) {
  105. // This check is needed for frame parallel decoding, in which case this
  106. // function could be called even after it has reached end of input stream.
  107. if (webm_ctx->reached_eos) {
  108. return 1;
  109. }
  110. mkvparser::Segment *const segment =
  111. reinterpret_cast<mkvparser::Segment *>(webm_ctx->segment);
  112. const mkvparser::Cluster *cluster =
  113. reinterpret_cast<const mkvparser::Cluster *>(webm_ctx->cluster);
  114. const mkvparser::Block *block =
  115. reinterpret_cast<const mkvparser::Block *>(webm_ctx->block);
  116. const mkvparser::BlockEntry *block_entry =
  117. reinterpret_cast<const mkvparser::BlockEntry *>(webm_ctx->block_entry);
  118. bool block_entry_eos = false;
  119. do {
  120. long status = 0;
  121. bool get_new_block = false;
  122. if (block_entry == NULL && !block_entry_eos) {
  123. status = cluster->GetFirst(block_entry);
  124. get_new_block = true;
  125. } else if (block_entry_eos || block_entry->EOS()) {
  126. cluster = segment->GetNext(cluster);
  127. if (cluster == NULL || cluster->EOS()) {
  128. *buffer_size = 0;
  129. webm_ctx->reached_eos = 1;
  130. return 1;
  131. }
  132. status = cluster->GetFirst(block_entry);
  133. block_entry_eos = false;
  134. get_new_block = true;
  135. } else if (block == NULL ||
  136. webm_ctx->block_frame_index == block->GetFrameCount() ||
  137. block->GetTrackNumber() != webm_ctx->video_track_index) {
  138. status = cluster->GetNext(block_entry, block_entry);
  139. if (block_entry == NULL || block_entry->EOS()) {
  140. block_entry_eos = true;
  141. continue;
  142. }
  143. get_new_block = true;
  144. }
  145. if (status || block_entry == NULL) {
  146. return -1;
  147. }
  148. if (get_new_block) {
  149. block = block_entry->GetBlock();
  150. if (block == NULL) return -1;
  151. webm_ctx->block_frame_index = 0;
  152. }
  153. } while (block_entry_eos ||
  154. block->GetTrackNumber() != webm_ctx->video_track_index);
  155. webm_ctx->cluster = cluster;
  156. webm_ctx->block_entry = block_entry;
  157. webm_ctx->block = block;
  158. const mkvparser::Block::Frame &frame =
  159. block->GetFrame(webm_ctx->block_frame_index);
  160. ++webm_ctx->block_frame_index;
  161. if (frame.len > static_cast<long>(*buffer_size)) {
  162. delete[] * buffer;
  163. *buffer = new uint8_t[frame.len];
  164. if (*buffer == NULL) {
  165. return -1;
  166. }
  167. webm_ctx->buffer = *buffer;
  168. }
  169. *buffer_size = frame.len;
  170. webm_ctx->timestamp_ns = block->GetTime(cluster);
  171. webm_ctx->is_key_frame = block->IsKey();
  172. mkvparser::MkvReader *const reader =
  173. reinterpret_cast<mkvparser::MkvReader *>(webm_ctx->reader);
  174. return frame.Read(reader, *buffer) ? -1 : 0;
  175. }
  176. int webm_guess_framerate(struct WebmInputContext *webm_ctx,
  177. struct VpxInputContext *vpx_ctx) {
  178. uint32_t i = 0;
  179. uint8_t *buffer = NULL;
  180. size_t buffer_size = 0;
  181. while (webm_ctx->timestamp_ns < 1000000000 && i < 50) {
  182. if (webm_read_frame(webm_ctx, &buffer, &buffer_size)) {
  183. break;
  184. }
  185. ++i;
  186. }
  187. vpx_ctx->framerate.numerator = (i - 1) * 1000000;
  188. vpx_ctx->framerate.denominator =
  189. static_cast<int>(webm_ctx->timestamp_ns / 1000);
  190. delete[] buffer;
  191. get_first_cluster(webm_ctx);
  192. webm_ctx->block = NULL;
  193. webm_ctx->block_entry = NULL;
  194. webm_ctx->block_frame_index = 0;
  195. webm_ctx->timestamp_ns = 0;
  196. webm_ctx->reached_eos = 0;
  197. return 0;
  198. }
  199. void webm_free(struct WebmInputContext *webm_ctx) { reset(webm_ctx); }