| Index: media/mf/file_reader_util.cc
|
| ===================================================================
|
| --- media/mf/file_reader_util.cc (revision 0)
|
| +++ media/mf/file_reader_util.cc (revision 0)
|
| @@ -0,0 +1,197 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +//
|
| +// Borrowed from media/tools/omx_test/file_reader_util.cc.
|
| +// Added some functionalities related to timestamps on packets.
|
| +
|
| +#include "media/mf/file_reader_util.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/scoped_comptr_win.h"
|
| +#include "base/logging.h"
|
| +#include "media/ffmpeg/ffmpeg_common.h"
|
| +#include "media/filters/bitstream_converter.h"
|
| +
|
| +namespace media {
|
| +
|
| +//////////////////////////////////////////////////////////////////////////////
|
| +// FFmpegFileReader
|
| +FFmpegFileReader::FFmpegFileReader(const std::string& filename)
|
| + : filename_(filename),
|
| + format_context_(NULL),
|
| + codec_context_(NULL),
|
| + target_stream_(-1),
|
| + converter_(NULL),
|
| + end_of_stream_(false) {
|
| +}
|
| +
|
| +FFmpegFileReader::~FFmpegFileReader() {
|
| + if (format_context_)
|
| + av_close_input_file(format_context_);
|
| +}
|
| +
|
| +bool FFmpegFileReader::Initialize() {
|
| + int result = av_open_input_file(&format_context_, filename_.c_str(),
|
| + NULL, 0, NULL);
|
| + if (result < 0) {
|
| + switch (result) {
|
| + case AVERROR_NOFMT:
|
| + LOG(ERROR) << "Error: File format not supported "
|
| + << filename_;
|
| + break;
|
| + default:
|
| + LOG(ERROR) << "Error: Could not open input for "
|
| + << filename_ << ": " << result;
|
| + break;
|
| + }
|
| + return false;
|
| + }
|
| + if (av_find_stream_info(format_context_) < 0) {
|
| + LOG(ERROR) << "can't use FFmpeg to parse stream info";
|
| + return false;
|
| + }
|
| +
|
| + for (size_t i = 0; i < format_context_->nb_streams; ++i) {
|
| + codec_context_ = format_context_->streams[i]->codec;
|
| +
|
| + // Find the video stream.
|
| + if (codec_context_->codec_type == CODEC_TYPE_VIDEO) {
|
| + target_stream_ = i;
|
| + break;
|
| + }
|
| + }
|
| + if (target_stream_ == -1) {
|
| + LOG(ERROR) << "no video in the stream";
|
| + return false;
|
| + }
|
| +
|
| + // Initialize the bitstream filter if needed.
|
| + // TODO(hclam): find a better way to identify mp4 container.
|
| + if (codec_context_->codec_id == CODEC_ID_H264) {
|
| + converter_.reset(new media::FFmpegBitstreamConverter(
|
| + "h264_mp4toannexb", codec_context_));
|
| + } else if (codec_context_->codec_id == CODEC_ID_MPEG4) {
|
| + converter_.reset(new media::FFmpegBitstreamConverter(
|
| + "mpeg4video_es", codec_context_));
|
| + } else if (codec_context_->codec_id == CODEC_ID_WMV3) {
|
| + converter_.reset(new media::FFmpegBitstreamConverter(
|
| + "vc1_asftorcv", codec_context_));
|
| + } else if (codec_context_->codec_id == CODEC_ID_VC1) {
|
| + converter_.reset(new media::FFmpegBitstreamConverter(
|
| + "vc1_asftoannexg", codec_context_));
|
| + }
|
| + if (converter_.get() && !converter_->Initialize()) {
|
| + converter_.reset();
|
| + LOG(ERROR) << "failed to initialize h264_mp4toannexb filter";
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void FFmpegFileReader::Read(uint8** output, int* size) {
|
| + Read(output, size, NULL, NULL);
|
| +}
|
| +
|
| +void FFmpegFileReader::Read(uint8** output, int* size, int64* timestamp,
|
| + int64* duration) {
|
| + if (!format_context_ || !codec_context_ || target_stream_ == -1) {
|
| + *size = 0;
|
| + *output = NULL;
|
| + return;
|
| + }
|
| + AVPacket packet;
|
| + bool found = false;
|
| + while (!found) {
|
| + int result = av_read_frame(format_context_, &packet);
|
| + if (result < 0) {
|
| + *output = NULL;
|
| + *size = 0;
|
| + end_of_stream_ = true;
|
| + return;
|
| + }
|
| + if (packet.stream_index == target_stream_) {
|
| + if (converter_.get() && !converter_->ConvertPacket(&packet)) {
|
| + LOG(ERROR) << "failed to convert AVPacket";
|
| + }
|
| + *output = new uint8[packet.size];
|
| + if (*output == NULL) {
|
| + LOG(ERROR) << "Failed to allocate buffer for annex b stream";
|
| + *size = 0;
|
| + return;
|
| + }
|
| + *size = packet.size;
|
| + memcpy(*output, packet.data, packet.size);
|
| + if (duration) {
|
| + if (packet.duration == 0) {
|
| + LOG(WARNING) << "Packet duration not known";
|
| + }
|
| + // This is in AVCodecContext::time_base units
|
| + *duration = ConvertFFmpegTimeBaseTo100Ns(packet.duration);
|
| + }
|
| + if (timestamp) {
|
| + if (packet.pts == AV_NOPTS_VALUE) {
|
| + LOG(ERROR) << "Packet presentation time not known";
|
| + *timestamp = 0L;
|
| + } else {
|
| + // This is in AVCodecContext::time_base units
|
| + *timestamp = ConvertFFmpegTimeBaseTo100Ns(packet.pts);
|
| + }
|
| + }
|
| + found = true;
|
| + }
|
| + av_free_packet(&packet);
|
| + }
|
| +}
|
| +
|
| +bool FFmpegFileReader::GetFrameRate(int* num, int *denom) const {
|
| + if (!codec_context_)
|
| + return false;
|
| +
|
| + // time_base = 1 / frame_rate
|
| + *denom = codec_context_->time_base.num;
|
| + *num = codec_context_->time_base.den;
|
| + if (denom == 0) {
|
| + *num = 0;
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool FFmpegFileReader::GetWidth(int* width) const {
|
| + if (!codec_context_)
|
| + return false;
|
| + *width = codec_context_->width;
|
| + return true;
|
| +}
|
| +
|
| +bool FFmpegFileReader::GetHeight(int* height) const {
|
| + if (!codec_context_)
|
| + return false;
|
| + *height = codec_context_->height;
|
| + return true;
|
| +}
|
| +
|
| +bool FFmpegFileReader::GetAspectRatio(int* num, int* denom) const {
|
| + if (!codec_context_)
|
| + return false;
|
| + AVRational aspect_ratio = codec_context_->sample_aspect_ratio;
|
| + if (aspect_ratio.num == 0 || aspect_ratio.den == 0)
|
| + return false;
|
| + *num = aspect_ratio.num;
|
| + *denom = aspect_ratio.den;
|
| + return true;
|
| +}
|
| +
|
| +int64 FFmpegFileReader::ConvertFFmpegTimeBaseTo100Ns(
|
| + int64 time_base_unit) const {
|
| + // FFmpeg units after time base conversion seems to be actually given in
|
| + // milliseconds (instead of seconds...) so we need to multiply it by a factor
|
| + // of 10,000 to convert it into units compatible with MF.
|
| + CHECK(codec_context_) << "Codec context needs to be initialized";
|
| + return time_base_unit * 10000 * codec_context_->time_base.num /
|
| + codec_context_->time_base.den;
|
| +}
|
| +
|
| +} // namespace media
|
|
|