| // Copyright 2018 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/media/sandbox/format_guesstimator.h" |
| |
| #include <algorithm> |
| #include <vector> |
| |
| #include "base/path_service.h" |
| #include "net/base/net_util.h" |
| #include "starboard/file.h" |
| #include "starboard/memory.h" |
| #include "starboard/string.h" |
| #include "starboard/types.h" |
| |
| namespace cobalt { |
| namespace media { |
| namespace sandbox { |
| |
| namespace { |
| |
| // Can be called as: |
| // IsFormat("https://example.com/audio.mp4", ".mp4") |
| // IsFormat("cobalt/demos/video.webm", ".webm") |
| bool IsFormat(const std::string& path_or_url, const std::string& format) { |
| DCHECK(!format.empty() && format[0] == '.') << "Invalid format: " << format; |
| return path_or_url.size() > format.size() && |
| path_or_url.substr(path_or_url.size() - format.size()) == format; |
| } |
| |
| FilePath ResolvePath(const std::string& path) { |
| FilePath result(path); |
| if (!result.IsAbsolute()) { |
| FilePath content_path; |
| PathService::Get(base::DIR_TEST_DATA, &content_path); |
| CHECK(content_path.IsAbsolute()); |
| result = content_path.Append(result); |
| } |
| if (SbFileCanOpen(result.value().c_str(), kSbFileOpenOnly | kSbFileRead)) { |
| return result; |
| } |
| return FilePath(); |
| } |
| |
| // Read the first 4096 bytes of the local file specified by |path|. If the size |
| // of the size is less than 4096 bytes, the function reads all of its content. |
| std::vector<uint8_t> ReadHeader(const FilePath& path) { |
| const int64_t kHeaderSize = 4096; |
| |
| starboard::ScopedFile file(path.value().c_str(), |
| kSbFileOpenOnly | kSbFileRead); |
| int64_t bytes_to_read = std::min(kHeaderSize, file.GetSize()); |
| DCHECK_GE(bytes_to_read, 0); |
| std::vector<uint8_t> buffer(bytes_to_read); |
| auto bytes_read = |
| file.Read(reinterpret_cast<char*>(buffer.data()), bytes_to_read); |
| DCHECK_EQ(bytes_to_read, bytes_read); |
| |
| return buffer; |
| } |
| |
| // Find the first occurrence of |str| in |data|. If there is no |str| contained |
| // inside |data|, it returns -1. |
| off_t FindString(const std::vector<uint8_t>& data, const char* str) { |
| size_t size_of_str = SbStringGetLength(str); |
| for (off_t offset = 0; offset + size_of_str < data.size(); ++offset) { |
| if (SbMemoryCompare(data.data() + offset, str, size_of_str) == 0) { |
| return offset; |
| } |
| } |
| return -1; |
| } |
| |
| } // namespace |
| |
| FormatGuesstimator::FormatGuesstimator(const std::string& path_or_url) { |
| GURL url(path_or_url); |
| if (url.is_valid()) { |
| // If it is a url, assume that it is a progressive video. |
| InitializeAsProgressive(url); |
| return; |
| } |
| FilePath path = ResolvePath(path_or_url); |
| if (path.empty() || !SbFileCanOpen(path.value().c_str(), kSbFileRead)) { |
| return; |
| } |
| if (IsFormat(path_or_url, ".mp4")) { |
| InitializeAsMp4(path); |
| } else if (IsFormat(path_or_url, ".webm")) { |
| InitializeAsWebM(path); |
| } |
| } |
| |
| void FormatGuesstimator::InitializeAsProgressive(const GURL& url) { |
| // Mp4 is the only supported progressive format. |
| if (!IsFormat(url.spec(), ".mp4")) { |
| return; |
| } |
| progressive_url_ = url; |
| mime_ = "video/mp4"; |
| codecs_ = "avc1.640028, mp4a.40.2"; |
| } |
| |
| void FormatGuesstimator::InitializeAsMp4(const FilePath& path) { |
| std::vector<uint8_t> header = ReadHeader(path); |
| if (FindString(header, "ftypmp") == 4) { |
| progressive_url_ = net::FilePathToFileURL(path); |
| mime_ = "video/mp4"; |
| codecs_ = "avc1.640028, mp4a.40.2"; |
| return; |
| } |
| if (FindString(header, "ftypdash") == -1) { |
| return; |
| } |
| if (FindString(header, "vide") != -1) { |
| adaptive_path_ = path; |
| mime_ = "video/mp4"; |
| codecs_ = "avc1.640028"; |
| return; |
| } |
| if (FindString(header, "soun") != -1) { |
| adaptive_path_ = path; |
| mime_ = "audio/mp4"; |
| codecs_ = "mp4a.40.2"; |
| return; |
| } |
| } |
| |
| void FormatGuesstimator::InitializeAsWebM(const FilePath& path) { |
| std::vector<uint8_t> header = ReadHeader(path); |
| adaptive_path_ = path; |
| if (FindString(header, "OpusHead") != -1) { |
| mime_ = "video/webm"; |
| codecs_ = "vp9"; |
| } else { |
| mime_ = "audio/webm"; |
| codecs_ = "opus"; |
| } |
| } |
| |
| } // namespace sandbox |
| } // namespace media |
| } // namespace cobalt |