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 |