shaka-packager/packager/app/stream_descriptor.cc

221 lines
6.8 KiB
C++
Raw Normal View History

// 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/app/stream_descriptor.h"
#include "packager/app/packager_util.h"
#include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/base/strings/string_split.h"
#include "packager/media/base/container_names.h"
#include "packager/media/base/language_utils.h"
namespace shaka {
namespace media {
namespace {
enum FieldType {
kUnknownField = 0,
kStreamSelectorField,
kInputField,
kOutputField,
kSegmentTemplateField,
kBandwidthField,
kLanguageField,
kOutputFormatField,
kHlsNameField,
kHlsGroupIdField,
kHlsPlaylistNameField,
kTrickPlayRateField,
};
struct FieldNameToTypeMapping {
const char* field_name;
FieldType field_type;
};
const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{"stream_selector", kStreamSelectorField},
{"stream", kStreamSelectorField},
{"input", kInputField},
{"in", kInputField},
{"output", kOutputField},
{"out", kOutputField},
{"init_segment", kOutputField},
{"segment_template", kSegmentTemplateField},
{"template", kSegmentTemplateField},
{"bandwidth", kBandwidthField},
{"bw", kBandwidthField},
{"bitrate", kBandwidthField},
{"language", kLanguageField},
{"lang", kLanguageField},
{"output_format", kOutputFormatField},
{"format", kOutputFormatField},
{"hls_name", kHlsNameField},
{"hls_group_id", kHlsGroupIdField},
{"playlist_name", kHlsPlaylistNameField},
{"trick_play_rate", kTrickPlayRateField},
};
FieldType GetFieldType(const std::string& field_name) {
for (size_t idx = 0; idx < arraysize(kFieldNameTypeMappings); ++idx) {
if (field_name == kFieldNameTypeMappings[idx].field_name)
return kFieldNameTypeMappings[idx].field_type;
}
return kUnknownField;
}
} // anonymous namespace
StreamDescriptor::StreamDescriptor() {}
StreamDescriptor::~StreamDescriptor() {}
bool InsertStreamDescriptor(const std::string& descriptor_string,
StreamDescriptorList* descriptor_list) {
StreamDescriptor descriptor;
// Split descriptor string into name/value pairs.
base::StringPairs pairs;
if (!base::SplitStringIntoKeyValuePairs(descriptor_string,
'=',
',',
&pairs)) {
LOG(ERROR) << "Invalid stream descriptors name/value pairs.";
return false;
}
for (base::StringPairs::const_iterator iter = pairs.begin();
iter != pairs.end(); ++iter) {
switch (GetFieldType(iter->first)) {
case kStreamSelectorField:
descriptor.stream_selector = iter->second;
break;
case kInputField:
descriptor.input = iter->second;
break;
case kOutputField:
descriptor.output = iter->second;
break;
case kSegmentTemplateField:
descriptor.segment_template = iter->second;
break;
case kBandwidthField: {
unsigned bw;
if (!base::StringToUint(iter->second, &bw)) {
LOG(ERROR) << "Non-numeric bandwidth specified.";
return false;
}
descriptor.bandwidth = bw;
break;
}
case kLanguageField: {
std::string language = LanguageToISO_639_2(iter->second);
if (language == "und") {
LOG(ERROR) << "Unknown/invalid language specified: " << iter->second;
return false;
}
descriptor.language = language;
break;
}
case kOutputFormatField: {
MediaContainerName output_format =
DetermineContainerFromFormatName(iter->second);
if (output_format == CONTAINER_UNKNOWN) {
LOG(ERROR) << "Unrecognized output format " << iter->second;
return false;
}
descriptor.output_format = output_format;
break;
}
case kHlsNameField: {
descriptor.hls_name = iter->second;
break;
}
case kHlsGroupIdField: {
descriptor.hls_group_id = iter->second;
break;
}
case kHlsPlaylistNameField: {
descriptor.hls_playlist_name = iter->second;
break;
}
case kTrickPlayRateField: {
unsigned rate;
if (!base::StringToUint(iter->second, &rate)) {
LOG(ERROR) << "Non-numeric trick play rate " << iter->second
<< " specified.";
return false;
}
if (rate == 0) {
LOG(ERROR) << "Stream trick_play_rate should be > 0.";
return false;
}
descriptor.trick_play_rate = rate;
break;
}
default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\").";
return false;
}
}
// Validate and insert the descriptor
if (descriptor.input.empty()) {
LOG(ERROR) << "Stream input not specified.";
return false;
}
if (!FLAGS_dump_stream_info && descriptor.stream_selector.empty()) {
LOG(ERROR) << "Stream stream_selector not specified.";
return false;
}
if (descriptor.output_format == CONTAINER_UNKNOWN) {
const std::string& output_name = descriptor.output.empty()
? descriptor.segment_template
: descriptor.output;
if (!output_name.empty()) {
descriptor.output_format = DetermineContainerFromFileName(output_name);
if (descriptor.output_format == CONTAINER_UNKNOWN) {
LOG(ERROR) << "Unable to determine output format for file "
<< output_name;
return false;
}
}
}
if (descriptor.output_format == MediaContainerName::CONTAINER_MPEG2TS) {
if (descriptor.segment_template.empty()) {
LOG(ERROR) << "Please specify segment_template. Single file TS output is "
"not supported.";
return false;
}
// Note that MPEG2 TS doesn't need a separate initialization segment, so
// output field is not needed.
if (!descriptor.output.empty()) {
LOG(WARNING) << "TS output '" << descriptor.output
<< "' ignored. TS muxer does not support initialization "
"segment generation.";
}
}
// For TS output, segment template is sufficient, and does not require an
// output entry.
const bool output_specified =
!descriptor.output.empty() ||
(descriptor.output_format == CONTAINER_MPEG2TS &&
!descriptor.segment_template.empty());
if (!FLAGS_dump_stream_info && !output_specified) {
LOG(ERROR) << "Stream output not specified.";
return false;
}
descriptor_list->insert(descriptor);
return true;
}
} // namespace media
} // namespace shaka