2014-06-27 23:07:36 +00:00
|
|
|
// 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
|
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/app/stream_descriptor.h"
|
2014-06-27 23:07:36 +00:00
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#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"
|
2016-06-30 22:25:52 +00:00
|
|
|
#include "packager/media/base/container_names.h"
|
2017-03-10 23:18:42 +00:00
|
|
|
#include "packager/media/base/language_utils.h"
|
2014-06-27 23:07:36 +00:00
|
|
|
|
2016-05-20 21:19:33 +00:00
|
|
|
namespace shaka {
|
2014-06-27 23:07:36 +00:00
|
|
|
namespace media {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
enum FieldType {
|
|
|
|
kUnknownField = 0,
|
|
|
|
kStreamSelectorField,
|
|
|
|
kInputField,
|
|
|
|
kOutputField,
|
|
|
|
kSegmentTemplateField,
|
|
|
|
kBandwidthField,
|
2015-02-02 19:27:32 +00:00
|
|
|
kLanguageField,
|
2015-12-16 18:20:13 +00:00
|
|
|
kOutputFormatField,
|
2016-04-16 22:58:47 +00:00
|
|
|
kHlsNameField,
|
|
|
|
kHlsGroupIdField,
|
|
|
|
kHlsPlaylistNameField,
|
2017-02-27 17:22:25 +00:00
|
|
|
kTrickPlayRateField,
|
2014-06-27 23:07:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FieldNameToTypeMapping {
|
|
|
|
const char* field_name;
|
|
|
|
FieldType field_type;
|
|
|
|
};
|
|
|
|
|
|
|
|
const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
|
2016-04-16 22:58:47 +00:00
|
|
|
{"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},
|
2017-02-27 17:22:25 +00:00
|
|
|
{"trick_play_rate", kTrickPlayRateField},
|
2014-06-27 23:07:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2015-12-16 18:20:13 +00:00
|
|
|
StreamDescriptor::StreamDescriptor()
|
|
|
|
: bandwidth(0), output_format(CONTAINER_UNKNOWN) {}
|
2014-06-27 23:07:36 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-02-02 19:27:32 +00:00
|
|
|
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;
|
|
|
|
}
|
2015-12-16 18:20:13 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-04-16 22:58:47 +00:00
|
|
|
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;
|
|
|
|
}
|
2014-06-27 23:07:36 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-04-16 22:58:47 +00:00
|
|
|
|
2016-06-30 22:25:52 +00:00
|
|
|
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.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-14 00:02:23 +00:00
|
|
|
// 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) {
|
2014-06-27 23:07:36 +00:00
|
|
|
LOG(ERROR) << "Stream output not specified.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
descriptor_list->insert(descriptor);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace media
|
2016-05-20 21:19:33 +00:00
|
|
|
} // namespace shaka
|