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 // Borrowed from media/tools/omx_test/file_reader_util.cc. |
| 6 // Added some functionalities related to timestamps on packets. |
| 7 |
| 8 #include "media/mf/file_reader_util.h" |
| 9 |
| 10 #include <algorithm> |
| 11 |
| 12 #include "base/scoped_comptr_win.h" |
| 13 #include "base/logging.h" |
| 14 #include "media/ffmpeg/ffmpeg_common.h" |
| 15 #include "media/filters/bitstream_converter.h" |
| 16 |
| 17 namespace media { |
| 18 |
| 19 ////////////////////////////////////////////////////////////////////////////// |
| 20 // FFmpegFileReader |
| 21 FFmpegFileReader::FFmpegFileReader(const std::string& filename) |
| 22 : filename_(filename), |
| 23 format_context_(NULL), |
| 24 codec_context_(NULL), |
| 25 target_stream_(-1), |
| 26 converter_(NULL), |
| 27 end_of_stream_(false) { |
| 28 } |
| 29 |
| 30 FFmpegFileReader::~FFmpegFileReader() { |
| 31 if (format_context_) |
| 32 av_close_input_file(format_context_); |
| 33 } |
| 34 |
| 35 bool FFmpegFileReader::Initialize() { |
| 36 int result = av_open_input_file(&format_context_, filename_.c_str(), |
| 37 NULL, 0, NULL); |
| 38 if (result < 0) { |
| 39 switch (result) { |
| 40 case AVERROR_NOFMT: |
| 41 LOG(ERROR) << "Error: File format not supported " |
| 42 << filename_; |
| 43 break; |
| 44 default: |
| 45 LOG(ERROR) << "Error: Could not open input for " |
| 46 << filename_ << ": " << result; |
| 47 break; |
| 48 } |
| 49 return false; |
| 50 } |
| 51 if (av_find_stream_info(format_context_) < 0) { |
| 52 LOG(ERROR) << "can't use FFmpeg to parse stream info"; |
| 53 return false; |
| 54 } |
| 55 |
| 56 for (size_t i = 0; i < format_context_->nb_streams; ++i) { |
| 57 codec_context_ = format_context_->streams[i]->codec; |
| 58 |
| 59 // Find the video stream. |
| 60 if (codec_context_->codec_type == CODEC_TYPE_VIDEO) { |
| 61 target_stream_ = i; |
| 62 break; |
| 63 } |
| 64 } |
| 65 if (target_stream_ == -1) { |
| 66 LOG(ERROR) << "no video in the stream"; |
| 67 return false; |
| 68 } |
| 69 |
| 70 // Initialize the bitstream filter if needed. |
| 71 // TODO(hclam): find a better way to identify mp4 container. |
| 72 if (codec_context_->codec_id == CODEC_ID_H264) { |
| 73 converter_.reset(new media::FFmpegBitstreamConverter( |
| 74 "h264_mp4toannexb", codec_context_)); |
| 75 } else if (codec_context_->codec_id == CODEC_ID_MPEG4) { |
| 76 converter_.reset(new media::FFmpegBitstreamConverter( |
| 77 "mpeg4video_es", codec_context_)); |
| 78 } else if (codec_context_->codec_id == CODEC_ID_WMV3) { |
| 79 converter_.reset(new media::FFmpegBitstreamConverter( |
| 80 "vc1_asftorcv", codec_context_)); |
| 81 } else if (codec_context_->codec_id == CODEC_ID_VC1) { |
| 82 converter_.reset(new media::FFmpegBitstreamConverter( |
| 83 "vc1_asftoannexg", codec_context_)); |
| 84 } |
| 85 if (converter_.get() && !converter_->Initialize()) { |
| 86 converter_.reset(); |
| 87 LOG(ERROR) << "failed to initialize h264_mp4toannexb filter"; |
| 88 return false; |
| 89 } |
| 90 return true; |
| 91 } |
| 92 |
| 93 void FFmpegFileReader::Read(uint8** output, int* size) { |
| 94 Read(output, size, NULL, NULL); |
| 95 } |
| 96 |
| 97 void FFmpegFileReader::Read(uint8** output, int* size, int64* timestamp, |
| 98 int64* duration) { |
| 99 if (!format_context_ || !codec_context_ || target_stream_ == -1) { |
| 100 *size = 0; |
| 101 *output = NULL; |
| 102 return; |
| 103 } |
| 104 AVPacket packet; |
| 105 bool found = false; |
| 106 while (!found) { |
| 107 int result = av_read_frame(format_context_, &packet); |
| 108 if (result < 0) { |
| 109 *output = NULL; |
| 110 *size = 0; |
| 111 end_of_stream_ = true; |
| 112 return; |
| 113 } |
| 114 if (packet.stream_index == target_stream_) { |
| 115 if (converter_.get() && !converter_->ConvertPacket(&packet)) { |
| 116 LOG(ERROR) << "failed to convert AVPacket"; |
| 117 } |
| 118 *output = new uint8[packet.size]; |
| 119 if (*output == NULL) { |
| 120 LOG(ERROR) << "Failed to allocate buffer for annex b stream"; |
| 121 *size = 0; |
| 122 return; |
| 123 } |
| 124 *size = packet.size; |
| 125 memcpy(*output, packet.data, packet.size); |
| 126 if (duration) { |
| 127 if (packet.duration == 0) { |
| 128 LOG(WARNING) << "Packet duration not known"; |
| 129 } |
| 130 // This is in AVCodecContext::time_base units |
| 131 *duration = ConvertFFmpegTimeBaseTo100Ns(packet.duration); |
| 132 } |
| 133 if (timestamp) { |
| 134 if (packet.pts == AV_NOPTS_VALUE) { |
| 135 LOG(ERROR) << "Packet presentation time not known"; |
| 136 *timestamp = 0L; |
| 137 } else { |
| 138 // This is in AVCodecContext::time_base units |
| 139 *timestamp = ConvertFFmpegTimeBaseTo100Ns(packet.pts); |
| 140 } |
| 141 } |
| 142 found = true; |
| 143 } |
| 144 av_free_packet(&packet); |
| 145 } |
| 146 } |
| 147 |
| 148 bool FFmpegFileReader::GetFrameRate(int* num, int *denom) const { |
| 149 if (!codec_context_) |
| 150 return false; |
| 151 |
| 152 // time_base = 1 / frame_rate |
| 153 *denom = codec_context_->time_base.num; |
| 154 *num = codec_context_->time_base.den; |
| 155 if (denom == 0) { |
| 156 *num = 0; |
| 157 return false; |
| 158 } |
| 159 return true; |
| 160 } |
| 161 |
| 162 bool FFmpegFileReader::GetWidth(int* width) const { |
| 163 if (!codec_context_) |
| 164 return false; |
| 165 *width = codec_context_->width; |
| 166 return true; |
| 167 } |
| 168 |
| 169 bool FFmpegFileReader::GetHeight(int* height) const { |
| 170 if (!codec_context_) |
| 171 return false; |
| 172 *height = codec_context_->height; |
| 173 return true; |
| 174 } |
| 175 |
| 176 bool FFmpegFileReader::GetAspectRatio(int* num, int* denom) const { |
| 177 if (!codec_context_) |
| 178 return false; |
| 179 AVRational aspect_ratio = codec_context_->sample_aspect_ratio; |
| 180 if (aspect_ratio.num == 0 || aspect_ratio.den == 0) |
| 181 return false; |
| 182 *num = aspect_ratio.num; |
| 183 *denom = aspect_ratio.den; |
| 184 return true; |
| 185 } |
| 186 |
| 187 int64 FFmpegFileReader::ConvertFFmpegTimeBaseTo100Ns( |
| 188 int64 time_base_unit) const { |
| 189 // FFmpeg units after time base conversion seems to be actually given in |
| 190 // milliseconds (instead of seconds...) so we need to multiply it by a factor |
| 191 // of 10,000 to convert it into units compatible with MF. |
| 192 CHECK(codec_context_) << "Codec context needs to be initialized"; |
| 193 return time_base_unit * 10000 * codec_context_->time_base.num / |
| 194 codec_context_->time_base.den; |
| 195 } |
| 196 |
| 197 } // namespace media |
OLD | NEW |