| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/tools/omx_test/file_reader_util.h" | |
| 6 | |
| 7 #include <stdio.h> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/string_util.h" | |
| 13 #include "media/ffmpeg/ffmpeg_common.h" | |
| 14 #include "media/ffmpeg/file_protocol.h" | |
| 15 #include "media/filters/bitstream_converter.h" | |
| 16 #include "media/tools/omx_test/color_space_util.h" | |
| 17 | |
| 18 namespace media { | |
| 19 | |
| 20 ////////////////////////////////////////////////////////////////////////////// | |
| 21 // BasicFileReader | |
| 22 BasicFileReader::BasicFileReader(const FilePath& path) | |
| 23 : path_(path), | |
| 24 file_(NULL) { | |
| 25 } | |
| 26 | |
| 27 bool BasicFileReader::Initialize() { | |
| 28 file_.Set(file_util::OpenFile(path_, "rb")); | |
| 29 if (!file_.get()) { | |
| 30 LOG(ERROR) << "unable to open " << path_.value(); | |
| 31 } | |
| 32 return file_.get() != NULL; | |
| 33 } | |
| 34 | |
| 35 ////////////////////////////////////////////////////////////////////////////// | |
| 36 // YuvFileReader | |
| 37 YuvFileReader::YuvFileReader(const FilePath& path, | |
| 38 int width, | |
| 39 int height, | |
| 40 int loop_count, | |
| 41 bool enable_csc) | |
| 42 : BasicFileReader(path), | |
| 43 width_(width), | |
| 44 height_(height), | |
| 45 loop_count_(loop_count), | |
| 46 output_nv21_(enable_csc) { | |
| 47 } | |
| 48 | |
| 49 YuvFileReader::~YuvFileReader() {} | |
| 50 | |
| 51 void YuvFileReader::Read(uint8** output, int* size) { | |
| 52 if (!file()) { | |
| 53 *size = 0; | |
| 54 *output = NULL; | |
| 55 return; | |
| 56 } | |
| 57 | |
| 58 while (true) { | |
| 59 scoped_array<uint8> data; | |
| 60 int bytes_read = 0; | |
| 61 | |
| 62 // OMX require encoder input are delivered in frames (or planes). | |
| 63 // Assume the input file is I420 YUV file. | |
| 64 const int kFrameSize = width_ * height_ * 3 / 2; | |
| 65 data.reset(new uint8[kFrameSize]); | |
| 66 | |
| 67 if (output_nv21_) { | |
| 68 if (!csc_buf_.get()) | |
| 69 csc_buf_.reset(new uint8[kFrameSize]); | |
| 70 bytes_read = fread(csc_buf_.get(), 1, kFrameSize, file()); | |
| 71 | |
| 72 // We do not convert partial frames. | |
| 73 if (bytes_read == kFrameSize) | |
| 74 IYUVtoNV21(csc_buf_.get(), data.get(), width_, height_); | |
| 75 else | |
| 76 bytes_read = 0; // force cleanup or loop around. | |
| 77 } else { | |
| 78 bytes_read = fread(data.get(), 1, kFrameSize, file()); | |
| 79 } | |
| 80 | |
| 81 if (bytes_read) { | |
| 82 *size = bytes_read; | |
| 83 *output = data.release(); | |
| 84 break; | |
| 85 } | |
| 86 | |
| 87 // Encounter the end of file. | |
| 88 if (loop_count_ == 1) { | |
| 89 // Signal end of stream. | |
| 90 *size = 0; | |
| 91 *output = data.release(); | |
| 92 } | |
| 93 | |
| 94 --loop_count_; | |
| 95 fseek(file(), 0, SEEK_SET); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 ////////////////////////////////////////////////////////////////////////////// | |
| 100 // BlockFileReader | |
| 101 BlockFileReader::BlockFileReader(const FilePath& path, | |
| 102 int block_size) | |
| 103 : BasicFileReader(path), | |
| 104 block_size_(block_size) { | |
| 105 } | |
| 106 | |
| 107 void BlockFileReader::Read(uint8** output, int* size) { | |
| 108 CHECK(file()); | |
| 109 *output = new uint8[block_size_]; | |
| 110 *size = fread(*output, 1, block_size_, file()); | |
| 111 } | |
| 112 | |
| 113 ////////////////////////////////////////////////////////////////////////////// | |
| 114 // FFmpegFileReader | |
| 115 FFmpegFileReader::FFmpegFileReader(const FilePath& path) | |
| 116 : path_(path), | |
| 117 format_context_(NULL), | |
| 118 codec_context_(NULL), | |
| 119 target_stream_(-1), | |
| 120 converter_(NULL) { | |
| 121 } | |
| 122 | |
| 123 FFmpegFileReader::~FFmpegFileReader() { | |
| 124 if (format_context_) | |
| 125 av_close_input_file(format_context_); | |
| 126 } | |
| 127 | |
| 128 bool FFmpegFileReader::Initialize() { | |
| 129 // av_open_input_file wants a char*, which can't work with wide paths. | |
| 130 // So we assume ASCII on Windows. On other platforms we can pass the | |
| 131 // path bytes through verbatim. | |
| 132 #if defined(OS_WIN) | |
| 133 std::string string_path = WideToASCII(path_.value()); | |
| 134 #else | |
| 135 const std::string& string_path = path_.value(); | |
| 136 #endif | |
| 137 int result = av_open_input_file(&format_context_, string_path.c_str(), | |
| 138 NULL, 0, NULL); | |
| 139 if (result < 0) { | |
| 140 switch (result) { | |
| 141 case AVERROR_NOFMT: | |
| 142 LOG(ERROR) << "Error: File format not supported " | |
| 143 << path_.value() << std::endl; | |
| 144 break; | |
| 145 default: | |
| 146 LOG(ERROR) << "Error: Could not open input for " | |
| 147 << path_.value() << std::endl; | |
| 148 break; | |
| 149 } | |
| 150 return false; | |
| 151 } | |
| 152 if (av_find_stream_info(format_context_) < 0) { | |
| 153 LOG(ERROR) << "can't use FFmpeg to parse stream info"; | |
| 154 return false; | |
| 155 } | |
| 156 | |
| 157 for (size_t i = 0; i < format_context_->nb_streams; ++i) { | |
| 158 codec_context_ = format_context_->streams[i]->codec; | |
| 159 | |
| 160 // Find the video stream. | |
| 161 if (codec_context_->codec_type == CODEC_TYPE_VIDEO) { | |
| 162 target_stream_ = i; | |
| 163 break; | |
| 164 } | |
| 165 } | |
| 166 if (target_stream_ == -1) { | |
| 167 LOG(ERROR) << "no video in the stream"; | |
| 168 return false; | |
| 169 } | |
| 170 | |
| 171 // Initialize the bitstream filter if needed. | |
| 172 // TODO(hclam): find a better way to identify mp4 container. | |
| 173 if (codec_context_->codec_id == CODEC_ID_H264) { | |
| 174 converter_.reset(new media::FFmpegBitstreamConverter( | |
| 175 "h264_mp4toannexb", codec_context_)); | |
| 176 } else if (codec_context_->codec_id == CODEC_ID_MPEG4) { | |
| 177 converter_.reset(new media::FFmpegBitstreamConverter( | |
| 178 "mpeg4video_es", codec_context_)); | |
| 179 } else if (codec_context_->codec_id == CODEC_ID_WMV3) { | |
| 180 converter_.reset(new media::FFmpegBitstreamConverter( | |
| 181 "vc1_asftorcv", codec_context_)); | |
| 182 } else if (codec_context_->codec_id == CODEC_ID_VC1) { | |
| 183 converter_.reset(new media::FFmpegBitstreamConverter( | |
| 184 "vc1_asftoannexg", codec_context_)); | |
| 185 } | |
| 186 | |
| 187 if (converter_.get() && !converter_->Initialize()) { | |
| 188 converter_.reset(); | |
| 189 LOG(ERROR) << "failed to initialize h264_mp4toannexb filter"; | |
| 190 return false; | |
| 191 } | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 void FFmpegFileReader::Read(uint8** output, int* size) { | |
| 196 if (!format_context_ || !codec_context_ || target_stream_ == -1) { | |
| 197 *size = 0; | |
| 198 *output = NULL; | |
| 199 return; | |
| 200 } | |
| 201 | |
| 202 AVPacket packet; | |
| 203 bool found = false; | |
| 204 while (!found) { | |
| 205 int result = av_read_frame(format_context_, &packet); | |
| 206 if (result < 0) { | |
| 207 *output = NULL; | |
| 208 *size = 0; | |
| 209 return; | |
| 210 } | |
| 211 if (packet.stream_index == target_stream_) { | |
| 212 if (converter_.get() && !converter_->ConvertPacket(&packet)) { | |
| 213 LOG(ERROR) << "failed to convert AVPacket"; | |
| 214 } | |
| 215 *output = new uint8[packet.size]; | |
| 216 *size = packet.size; | |
| 217 memcpy(*output, packet.data, packet.size); | |
| 218 found = true; | |
| 219 } | |
| 220 av_free_packet(&packet); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 ////////////////////////////////////////////////////////////////////////////// | |
| 225 // H264FileReader | |
| 226 const int kH264ReadSize = 1024 * 1024; | |
| 227 | |
| 228 H264FileReader::H264FileReader(const FilePath& path) | |
| 229 : BasicFileReader(path), | |
| 230 read_buf_(new uint8[kH264ReadSize]), | |
| 231 current_(0), | |
| 232 used_(0) { | |
| 233 } | |
| 234 | |
| 235 H264FileReader::~H264FileReader() {} | |
| 236 | |
| 237 void H264FileReader::Read(uint8** output, int *size) { | |
| 238 // Fill the buffer when it's less than half full. | |
| 239 int read = 0; | |
| 240 if (used_ < kH264ReadSize / 2) { | |
| 241 read = fread(read_buf_.get(), 1, kH264ReadSize - used_, file()); | |
| 242 CHECK(read >= 0); | |
| 243 used_ += read; | |
| 244 } | |
| 245 | |
| 246 // If we failed to read. | |
| 247 if (current_ == used_) { | |
| 248 *output = NULL; | |
| 249 *size = 0; | |
| 250 return; | |
| 251 } | |
| 252 | |
| 253 // Try to find start code of 0x00, 0x00, 0x01. | |
| 254 bool found = false; | |
| 255 int pos = current_ + 3; | |
| 256 for (; pos < used_ - 2; ++pos) { | |
| 257 if (read_buf_[pos] == 0 && | |
| 258 read_buf_[pos+1] == 0 && | |
| 259 read_buf_[pos+2] == 1) { | |
| 260 found = true; | |
| 261 break; | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 // If next NALU is found. | |
| 266 if (found) { | |
| 267 CHECK(pos > current_); | |
| 268 *size = pos - current_; | |
| 269 *output = new uint8[*size]; | |
| 270 memcpy(*output, read_buf_.get() + current_, *size); | |
| 271 current_ = pos; | |
| 272 | |
| 273 // If we have used_ more than half of the available buffer. | |
| 274 // Then move the unused_ buffer to the front to give space | |
| 275 // for more incoming output. | |
| 276 if (current_ > used_ / 2) { | |
| 277 CHECK(used_ > current_); | |
| 278 memcpy(read_buf_.get(), | |
| 279 read_buf_.get() + current_, | |
| 280 used_ - current_); | |
| 281 used_ = used_ - current_; | |
| 282 current_ = 0; | |
| 283 } | |
| 284 return; | |
| 285 } | |
| 286 | |
| 287 // If next NALU is not found, assume the remaining data is a NALU | |
| 288 // and return the data. | |
| 289 CHECK(used_ > current_); | |
| 290 *size = used_ - current_; | |
| 291 *output = new uint8[*size]; | |
| 292 memcpy(*output, read_buf_.get() + current_, *size); | |
| 293 current_ = used_; | |
| 294 } | |
| 295 | |
| 296 } // namespace media | |
| OLD | NEW |