shaka-packager/packager/media/base/muxer_util.cc

183 lines
6.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/muxer_util.h"
#include <inttypes.h>
#include <string>
#include <vector>
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_split.h"
#include "packager/base/strings/stringprintf.h"
#include "packager/media/base/video_stream_info.h"
namespace shaka {
namespace {
bool ValidateFormatTag(const std::string& format_tag) {
DCHECK(!format_tag.empty());
// Format tag should follow this prototype: %0[width]d.
if (format_tag.size() > 3 && format_tag[0] == '%' && format_tag[1] == '0' &&
format_tag[format_tag.size() - 1] == 'd') {
unsigned out;
if (base::StringToUint(format_tag.substr(2, format_tag.size() - 3), &out))
return true;
}
LOG(ERROR) << "SegmentTemplate: Format tag should follow this prototype: "
<< "%0[width]d if exist.";
return false;
}
} // namespace
namespace media {
bool ValidateSegmentTemplate(const std::string& segment_template) {
if (segment_template.empty())
return false;
std::vector<std::string> splits;
base::SplitString(segment_template, '$', &splits);
// ISO/IEC 23009-1:2012 5.3.9.4.4 Template-based Segment URL construction.
// Allowed identifiers: $$, $RepresentationID$, $Number$, $Bandwidth$, $Time$.
// "$" always appears in pairs, so there should be odd number of splits.
// We also allow $DecodeTime$ in order to help work around this longstanding
// bug in Chrome:
// https://bugs.chromium.org/p/chromium/issues/detail?id=398130#c13
// cf. https://github.com/google/shaka-packager/issues/113
if (splits.size() % 2 == 0) {
LOG(ERROR) << "SegmentTemplate: '$' should appear in pairs.";
return false;
}
bool has_number = false;
bool has_time = false;
// Every second substring in split output should be an identifier.
for (size_t i = 1; i < splits.size(); i += 2) {
// Each identifier may be suffixed, within the enclosing $ characters,
// with an additional format tag aligned with the printf format tag as
// defined in IEEE 1003.1-2008 [10] following this prototype: %0[width]d.
size_t format_pos = splits[i].find('%');
std::string identifier = splits[i].substr(0, format_pos);
if (format_pos != std::string::npos) {
if (!ValidateFormatTag(splits[i].substr(format_pos)))
return false;
}
// TODO(kqyang): Support "RepresentationID".
if (identifier == "RepresentationID") {
NOTIMPLEMENTED() << "SegmentTemplate: $RepresentationID$ is not supported "
"yet.";
return false;
} else if (identifier == "Number") {
has_number = true;
} else if (identifier == "Time" || identifier == "DecodeTime") {
has_time = true;
} else if (identifier == "") {
if (format_pos != std::string::npos) {
LOG(ERROR) << "SegmentTemplate: $$ should not have any format tags.";
return false;
}
} else if (identifier != "Bandwidth") {
LOG(ERROR) << "SegmentTemplate: '$" << splits[i]
<< "$' is not a valid identifier.";
return false;
}
}
if (has_number && has_time) {
LOG(ERROR) << "SegmentTemplate: $Number$ and $Time$/$DecodeTime$ should not co-exist.";
return false;
}
if (!has_number && !has_time) {
LOG(ERROR) << "SegmentTemplate: $Number$ or $Time$/$DecodeTime$ should exist.";
return false;
}
// Note: The below check is skipped.
// Strings outside identifiers shall only contain characters that are
// permitted within URLs according to RFC 1738.
return true;
}
std::string GetSegmentName(const std::string& segment_template,
uint64_t segment_start_time,
uint64_t segment_start_decode_time,
uint32_t segment_index,
uint32_t bandwidth) {
DCHECK(ValidateSegmentTemplate(segment_template));
std::vector<std::string> splits;
base::SplitString(segment_template, '$', &splits);
// "$" always appears in pairs, so there should be odd number of splits.
DCHECK_EQ(1u, splits.size() % 2);
std::string segment_name;
for (size_t i = 0; i < splits.size(); ++i) {
// Every second substring in split output should be an identifier.
// Simply copy the non-identifier part.
if (i % 2 == 0) {
segment_name += splits[i];
continue;
}
if (splits[i].empty()) {
// "$$" is an escape sequence, replaced with a single "$".
segment_name += "$";
continue;
}
size_t format_pos = splits[i].find('%');
std::string identifier = splits[i].substr(0, format_pos);
DCHECK(identifier == "Number" || identifier == "Time" ||
identifier == "DecodeTime" || identifier == "Bandwidth");
std::string format_tag;
if (format_pos != std::string::npos) {
format_tag = splits[i].substr(format_pos);
DCHECK(ValidateFormatTag(format_tag));
// Replace %d formatting to correctly format uint64_t.
format_tag = format_tag.substr(0, format_tag.size() - 1) + PRIu64;
} else {
// Default format tag "%01d", modified to format uint64_t correctly.
format_tag = "%01" PRIu64;
}
if (identifier == "Number") {
// SegmentNumber starts from 1.
segment_name += base::StringPrintf(
format_tag.c_str(), static_cast<uint64_t>(segment_index + 1));
} else if (identifier == "Time") {
segment_name +=
base::StringPrintf(format_tag.c_str(), segment_start_time);
} else if (identifier == "DecodeTime") {
segment_name +=
base::StringPrintf(format_tag.c_str(), segment_start_decode_time);
} else if (identifier == "Bandwidth") {
segment_name += base::StringPrintf(format_tag.c_str(),
static_cast<uint64_t>(bandwidth));
}
}
return segment_name;
}
KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info,
uint32_t max_sd_pixels) {
if (stream_info.stream_type() == kStreamAudio)
return KeySource::TRACK_TYPE_AUDIO;
if (stream_info.stream_type() != kStreamVideo)
return KeySource::TRACK_TYPE_UNKNOWN;
DCHECK_EQ(kStreamVideo, stream_info.stream_type());
const VideoStreamInfo& video_stream_info =
static_cast<const VideoStreamInfo&>(stream_info);
uint32_t pixels = video_stream_info.width() * video_stream_info.height();
return (pixels > max_sd_pixels) ? KeySource::TRACK_TYPE_HD
: KeySource::TRACK_TYPE_SD;
}
} // namespace media
} // namespace shaka