Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 <map> | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "services/media/framework/safe_clone.h" | |
| 9 #include "services/media/framework_ffmpeg/ffmpeg_demux.h" | |
| 10 #include "services/media/framework_ffmpeg/ffmpeg_io.h" | |
| 11 #include "services/media/framework_ffmpeg/ffmpeg_type_converters.h" | |
| 12 | |
| 13 namespace mojo { | |
| 14 namespace media { | |
| 15 | |
| 16 class FfmpegDemuxImpl : public FfmpegDemux { | |
| 17 public: | |
| 18 FfmpegDemuxImpl(); | |
| 19 | |
| 20 ~FfmpegDemuxImpl() override; | |
| 21 | |
| 22 // Demux implementation. | |
| 23 Result Init(std::shared_ptr<Reader> reader) override; | |
| 24 | |
| 25 std::unique_ptr<Metadata> metadata() const override; | |
| 26 | |
| 27 const std::vector<DemuxStream*>& streams() const override; | |
| 28 | |
| 29 // MultistreamSource implementation. | |
| 30 size_t stream_count() const override; | |
| 31 | |
| 32 PacketPtr PullPacket(size_t* stream_index_out) override; | |
| 33 | |
| 34 private: | |
| 35 class FfmpegDemuxStream : public DemuxStream { | |
| 36 public: | |
| 37 FfmpegDemuxStream(AVFormatContext* format_context, size_t index); | |
| 38 | |
| 39 ~FfmpegDemuxStream() override; | |
| 40 | |
| 41 // Demux::DemuxStream implementation. | |
| 42 size_t index() const override; | |
| 43 | |
| 44 std::unique_ptr<StreamType> stream_type() const override; | |
| 45 | |
| 46 private: | |
| 47 AVStream* stream_; | |
| 48 size_t index_; | |
| 49 std::unique_ptr<StreamType> stream_type_; | |
| 50 }; | |
| 51 | |
| 52 // Specialized packet implementation. | |
| 53 class DemuxPacket : public Packet { | |
| 54 public: | |
| 55 ~DemuxPacket() override { | |
|
johngro
2016/03/01 01:31:38
Packet's destructor is protected, DemuxPacket's sh
dalesat
2016/03/01 20:43:02
Made the destructor protected. Packet::Create crea
| |
| 56 av_free_packet(&av_packet); | |
|
johngro
2016/03/01 01:31:39
what happens if the av_packet was never allocated
dalesat
2016/03/01 20:43:02
Not sure what you mean. This packet implementation
johngro
2016/03/01 22:07:26
yes, but nothing in this class made certain that a
dalesat
2016/03/02 18:35:36
Acknowledged.
| |
| 57 } | |
| 58 | |
| 59 // Packet implementation. | |
| 60 int64_t presentation_time() const override { return av_packet.pts; }; | |
| 61 | |
| 62 uint64_t duration() const override { return av_packet.duration; }; | |
| 63 | |
| 64 bool end_of_stream() const override { return false; } | |
|
johngro
2016/03/01 01:31:39
if PTS, duration and EOS are core pieces of metada
dalesat
2016/03/01 20:43:02
We already have TODOs to revisit the packet defini
| |
| 65 | |
| 66 size_t size() const override { return size_t(av_packet.size); } | |
| 67 | |
| 68 void* payload() const override { | |
| 69 return reinterpret_cast<void*>(av_packet.data); | |
| 70 } | |
| 71 | |
| 72 AVPacket av_packet; | |
|
johngro
2016/03/01 01:31:39
this should be private and named av_packet_
dalesat
2016/03/01 20:43:02
Done.
| |
| 73 | |
| 74 protected: | |
| 75 void Release() override { delete this; } | |
|
johngro
2016/03/01 01:31:39
so, Packet instances seem to be managed using a st
dalesat
2016/03/01 20:43:01
We already have TODOs to revisit this. I took this
| |
| 76 }; | |
| 77 | |
| 78 struct AVFormatContextDeleter { | |
| 79 inline void operator()(AVFormatContext* ptr) const { | |
| 80 avformat_free_context(ptr); | |
| 81 } | |
| 82 }; | |
| 83 | |
| 84 struct AVIOContextDeleter { | |
| 85 inline void operator()(AVIOContext* ptr) const { | |
| 86 FreeAvioContext(ptr); | |
| 87 } | |
| 88 }; | |
| 89 | |
| 90 // Produces an end-of-stream packet for next_stream_to_end_. | |
| 91 PacketPtr PullEndOfStreamPacket(size_t* stream_index_out); | |
| 92 | |
| 93 // Copies metadata from the specified source into map. | |
| 94 void CopyMetadata( | |
| 95 AVDictionary* source, | |
| 96 std::map<std::string, std::string>& map); | |
| 97 | |
| 98 std::shared_ptr<Reader> reader_; | |
| 99 std::unique_ptr<AVFormatContext, AVFormatContextDeleter> format_context_; | |
| 100 std::unique_ptr<AVIOContext, AVIOContextDeleter> io_context_; | |
| 101 std::vector<DemuxStream*> streams_; | |
| 102 std::unique_ptr<Metadata> metadata_; | |
| 103 int64_t next_presentation_time_; | |
| 104 int next_stream_to_end_; // -1 means don't end. streams_.size() means stop. | |
| 105 }; | |
| 106 | |
| 107 // static | |
| 108 std::shared_ptr<Demux> FfmpegDemux::Create() { | |
| 109 return std::shared_ptr<Demux>(new FfmpegDemuxImpl()); | |
| 110 } | |
| 111 | |
| 112 FfmpegDemuxImpl::FfmpegDemuxImpl() : next_stream_to_end_(-1) {} | |
| 113 | |
| 114 FfmpegDemuxImpl::~FfmpegDemuxImpl() {} | |
| 115 | |
| 116 Result FfmpegDemuxImpl::Init(std::shared_ptr<Reader> reader) { | |
| 117 static const uint64_t kNanosecondsPerMicrosecond = 1000; | |
|
johngro
2016/03/01 01:31:39
constexpr
dalesat
2016/03/01 20:43:02
Done.
| |
| 118 | |
| 119 reader_ = reader; | |
| 120 | |
| 121 AVFormatContext* format_context = avformat_alloc_context(); | |
| 122 | |
| 123 io_context_.reset(CreateAvioContext(reader.get())); | |
| 124 if (!io_context_) { | |
| 125 LOG(ERROR) << "CreateAvioContext failed (allocation failure)"; | |
|
johngro
2016/03/01 01:31:38
this leaks format_context
I made a similar commen
dalesat
2016/03/01 20:43:01
Moved this allocation down to where it belongs. Ad
| |
| 126 return Result::kInternalError; | |
| 127 } | |
| 128 | |
| 129 format_context->flags |= AVFMT_FLAG_CUSTOM_IO | AVFMT_FLAG_FAST_SEEK; | |
| 130 format_context->pb = io_context_.get(); | |
| 131 | |
| 132 int r = avformat_open_input(&format_context, nullptr, nullptr, nullptr); | |
|
johngro
2016/03/01 01:31:39
These two operations (open_input and find_stream_i
dalesat
2016/03/01 20:43:02
Done.
dalesat
2016/03/01 20:43:02
Done.
| |
| 133 format_context_.reset(format_context); | |
| 134 if (r < 0) { | |
| 135 return Result::kInternalError; | |
| 136 } | |
| 137 | |
| 138 r = avformat_find_stream_info(format_context_.get(), nullptr); | |
| 139 if (r < 0) { | |
| 140 LOG(ERROR) << "avformat_find_stream_info failed, result " << r; | |
| 141 return Result::kInternalError; | |
| 142 } | |
| 143 | |
| 144 std::map<std::string, std::string> metadata_map; | |
| 145 | |
| 146 CopyMetadata(format_context_->metadata, metadata_map); | |
| 147 for (uint i = 0; i < format_context_->nb_streams; i++) { | |
| 148 streams_.push_back(new FfmpegDemuxStream(format_context_.get(), i)); | |
| 149 CopyMetadata(format_context_->streams[i]->metadata, metadata_map); | |
| 150 } | |
| 151 | |
| 152 metadata_ = Metadata::Create( | |
| 153 format_context_->duration * kNanosecondsPerMicrosecond, | |
| 154 metadata_map["TITLE"], | |
| 155 metadata_map["ARTIST"], | |
| 156 metadata_map["ALBUM"], | |
| 157 metadata_map["PUBLISHER"], | |
| 158 metadata_map["GENRE"], | |
| 159 metadata_map["COMPOSER"]); | |
| 160 | |
| 161 return Result::kOk; | |
| 162 } | |
| 163 | |
| 164 std::unique_ptr<Metadata> FfmpegDemuxImpl::metadata() const { | |
| 165 return SafeClone(metadata_); | |
| 166 } | |
| 167 | |
| 168 const std::vector<Demux::DemuxStream*>& FfmpegDemuxImpl::streams() const { | |
| 169 return streams_; | |
| 170 } | |
| 171 | |
| 172 size_t FfmpegDemuxImpl::stream_count() const { | |
| 173 if (!format_context_) { | |
| 174 return 0; | |
| 175 } | |
| 176 return format_context_->nb_streams; | |
| 177 } | |
| 178 | |
| 179 PacketPtr FfmpegDemuxImpl::PullPacket(size_t* stream_index_out) { | |
| 180 DCHECK(stream_index_out); | |
| 181 | |
| 182 if (next_stream_to_end_ != -1) { | |
| 183 // We're producing end-of-stream packets for all the streams. | |
| 184 return PullEndOfStreamPacket(stream_index_out); | |
| 185 } | |
| 186 | |
| 187 FfmpegDemuxImpl::DemuxPacket* demux_packet = | |
| 188 new FfmpegDemuxImpl::DemuxPacket(); | |
| 189 | |
| 190 av_init_packet(&demux_packet->av_packet); | |
|
johngro
2016/03/01 01:31:39
FfmpegDemuxImpl::DemuxPacket owns the call to av_f
dalesat
2016/03/01 20:43:02
Done.
| |
| 191 demux_packet->av_packet.data = nullptr; | |
| 192 demux_packet->av_packet.size = 0; | |
|
johngro
2016/03/01 01:31:39
access to av_packet should exposed using an access
dalesat
2016/03/01 20:43:02
Done.
| |
| 193 | |
| 194 if (av_read_frame(format_context_.get(), &demux_packet->av_packet) < 0) { | |
| 195 // End of stream. Start producing end-of-stream packets for all the streams. | |
| 196 delete demux_packet; | |
| 197 next_stream_to_end_ = 0; | |
| 198 return PullEndOfStreamPacket(stream_index_out); | |
| 199 } | |
| 200 | |
| 201 *stream_index_out = | |
| 202 static_cast<size_t>(demux_packet->av_packet.stream_index); | |
| 203 next_presentation_time_ = | |
| 204 demux_packet->presentation_time() + demux_packet->duration(); | |
|
johngro
2016/03/01 01:31:39
either PTS or duration may be AV_NOPTS_VALUE and n
dalesat
2016/03/01 20:43:01
Acknowledged.
johngro
2016/03/01 22:07:26
Great, if you agree that this can be an issue, but
dalesat
2016/03/02 18:35:36
Done.
| |
| 205 | |
| 206 return PacketPtr(demux_packet); | |
| 207 } | |
| 208 | |
| 209 PacketPtr FfmpegDemuxImpl::PullEndOfStreamPacket(size_t* stream_index_out) { | |
| 210 DCHECK(next_stream_to_end_ >= 0); | |
| 211 | |
| 212 if (static_cast<std::size_t>(next_stream_to_end_) >= streams_.size()) { | |
| 213 NOTREACHED() << "PullPacket called after all streams have ended"; | |
| 214 return nullptr; | |
| 215 } | |
| 216 | |
| 217 *stream_index_out = next_stream_to_end_++; | |
| 218 return Packet::CreateEndOfStream(next_presentation_time_); | |
| 219 } | |
| 220 | |
| 221 void FfmpegDemuxImpl::CopyMetadata( | |
| 222 AVDictionary* source, | |
| 223 std::map<std::string, std::string>& map) { | |
| 224 if (source == nullptr) { | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 for (AVDictionaryEntry *entry = | |
| 229 av_dict_get(source, "", nullptr, AV_DICT_IGNORE_SUFFIX); | |
| 230 entry != nullptr; | |
| 231 entry = av_dict_get(source, "", entry, AV_DICT_IGNORE_SUFFIX)) { | |
| 232 if (map.find(entry->key) == map.end()) { | |
| 233 map.emplace(entry->key, entry->value); | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 FfmpegDemuxImpl::FfmpegDemuxStream::FfmpegDemuxStream( | |
| 239 AVFormatContext* format_context, | |
|
johngro
2016/03/01 01:31:39
Ownership of format_context is not being transferr
dalesat
2016/03/01 20:43:02
Done.
| |
| 240 size_t index) : | |
| 241 stream_(format_context->streams[index]), index_(index) { | |
| 242 stream_type_ = StreamTypeFromAVCodecContext(stream_->codec); | |
| 243 } | |
| 244 | |
| 245 FfmpegDemuxImpl::FfmpegDemuxStream::~FfmpegDemuxStream() {} | |
| 246 | |
| 247 size_t FfmpegDemuxImpl::FfmpegDemuxStream::index() const { | |
| 248 return index_; | |
| 249 } | |
| 250 | |
| 251 std::unique_ptr<StreamType> FfmpegDemuxImpl::FfmpegDemuxStream::stream_type() | |
| 252 const { | |
| 253 return SafeClone(stream_type_); | |
| 254 } | |
| 255 | |
| 256 } // namespace media | |
| 257 } // namespace mojo | |
| OLD | NEW |