下面列出了android.media.MediaCrypto#com.google.android.exoplayer2.util.Log 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@Nullable
private static TextInformationFrame parseIndexAndCountAttribute(
int type, String attributeName, ParsableByteArray data) {
int atomSize = data.readInt();
int atomType = data.readInt();
if (atomType == Atom.TYPE_data && atomSize >= 22) {
data.skipBytes(10); // version (1), flags (3), empty (4), empty (2)
int index = data.readUnsignedShort();
if (index > 0) {
String value = "" + index;
int count = data.readUnsignedShort();
if (count > 0) {
value += "/" + count;
}
return new TextInformationFrame(attributeName, /* description= */ null, value);
}
}
Log.w(TAG, "Failed to parse index/count attribute: " + Atom.getAtomTypeString(type));
return null;
}
private static boolean parseCue(String id, Matcher cueHeaderMatcher, ParsableByteArray webvttData,
WebvttCue.Builder builder, StringBuilder textBuilder, List<WebvttCssStyle> styles) {
try {
// Parse the cue start and end times.
builder.setStartTime(WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(1)))
.setEndTime(WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(2)));
} catch (NumberFormatException e) {
Log.w(TAG, "Skipping cue with bad header: " + cueHeaderMatcher.group());
return false;
}
parseCueSettingsList(cueHeaderMatcher.group(3), builder);
// Parse the cue text.
textBuilder.setLength(0);
String line;
while (!TextUtils.isEmpty(line = webvttData.readLine())) {
if (textBuilder.length() > 0) {
textBuilder.append("\n");
}
textBuilder.append(line.trim());
}
parseCueText(id, textBuilder.toString(), builder, styles);
return true;
}
@Override
public void onSystemTimeUsMismatch(
long audioTimestampPositionFrames,
long audioTimestampSystemTimeUs,
long systemTimeUs,
long playbackPositionUs) {
String message =
"Spurious audio timestamp (system clock mismatch): "
+ audioTimestampPositionFrames
+ ", "
+ audioTimestampSystemTimeUs
+ ", "
+ systemTimeUs
+ ", "
+ playbackPositionUs
+ ", "
+ getSubmittedFrames()
+ ", "
+ getWrittenFrames();
if (failOnSpuriousAudioTimestamp) {
throw new InvalidAudioTrackTimestampException(message);
}
Log.w(TAG, message);
}
private Download putDownload(Download download) {
// Downloads in terminal states shouldn't be in the downloads list.
Assertions.checkState(download.state != STATE_COMPLETED && download.state != STATE_FAILED);
int changedIndex = getDownloadIndex(download.request.id);
if (changedIndex == C.INDEX_UNSET) {
downloads.add(download);
Collections.sort(downloads, InternalHandler::compareStartTimes);
} else {
boolean needsSort = download.startTimeMs != downloads.get(changedIndex).startTimeMs;
downloads.set(changedIndex, download);
if (needsSort) {
Collections.sort(downloads, InternalHandler::compareStartTimes);
}
}
try {
downloadIndex.putDownload(download);
} catch (IOException e) {
Log.e(TAG, "Failed to update index.", e);
}
DownloadUpdate update =
new DownloadUpdate(download, /* isRemove= */ false, new ArrayList<>(downloads));
mainHandler.obtainMessage(MSG_DOWNLOAD_UPDATE, update).sendToTarget();
return download;
}
@Override
public synchronized void release() {
if (released) {
return;
}
listeners.clear();
removeStaleSpans();
try {
contentIndex.store();
} catch (IOException e) {
Log.e(TAG, "Storing index file failed", e);
} finally {
unlockFolder(cacheDir);
released = true;
}
}
/**
* @param cache The cache into which data should be written.
* @param fragmentSize For requests that should be fragmented into multiple cache files, this is
* the maximum size of a cache file in bytes. If set to {@link C#LENGTH_UNSET} then no
* fragmentation will occur. Using a small value allows for finer-grained cache eviction
* policies, at the cost of increased overhead both on the cache implementation and the file
* system. Values under {@code (2 * 1024 * 1024)} are not recommended.
* @param bufferSize The buffer size in bytes for writing to a cache file. A zero or negative
* value disables buffering.
*/
public CacheDataSink(Cache cache, long fragmentSize, int bufferSize) {
Assertions.checkState(
fragmentSize > 0 || fragmentSize == C.LENGTH_UNSET,
"fragmentSize must be positive or C.LENGTH_UNSET.");
if (fragmentSize != C.LENGTH_UNSET && fragmentSize < MIN_RECOMMENDED_FRAGMENT_SIZE) {
Log.w(
TAG,
"fragmentSize is below the minimum recommended value of "
+ MIN_RECOMMENDED_FRAGMENT_SIZE
+ ". This may cause poor cache performance.");
}
this.cache = Assertions.checkNotNull(cache);
this.fragmentSize = fragmentSize == C.LENGTH_UNSET ? Long.MAX_VALUE : fragmentSize;
this.bufferSize = bufferSize;
}
private void release() {
for (Task task : activeTasks.values()) {
task.cancel(/* released= */ true);
}
try {
downloadIndex.setDownloadingStatesToQueued();
} catch (IOException e) {
Log.e(TAG, "Failed to update index.", e);
}
downloads.clear();
thread.quit();
synchronized (this) {
released = true;
notifyAll();
}
}
private Builder derivePositionAnchorFromAlignment() {
if (textAlignment == null) {
positionAnchor = Cue.TYPE_UNSET;
} else {
switch (textAlignment) {
case ALIGN_NORMAL:
positionAnchor = Cue.ANCHOR_TYPE_START;
break;
case ALIGN_CENTER:
positionAnchor = Cue.ANCHOR_TYPE_MIDDLE;
break;
case ALIGN_OPPOSITE:
positionAnchor = Cue.ANCHOR_TYPE_END;
break;
default:
Log.w(TAG, "Unrecognized alignment: " + textAlignment);
positionAnchor = Cue.ANCHOR_TYPE_START;
break;
}
}
return this;
}
/**
* Parses a string containing a list of cue settings.
*
* @param cueSettingsList String containing the settings for a given cue.
* @param builder The {@link WebvttCue.Builder} where incremental construction takes place.
*/
/* package */ static void parseCueSettingsList(String cueSettingsList,
WebvttCue.Builder builder) {
// Parse the cue settings list.
Matcher cueSettingMatcher = CUE_SETTING_PATTERN.matcher(cueSettingsList);
while (cueSettingMatcher.find()) {
String name = cueSettingMatcher.group(1);
String value = cueSettingMatcher.group(2);
try {
if ("line".equals(name)) {
parseLineAttribute(value, builder);
} else if ("align".equals(name)) {
builder.setTextAlignment(parseTextAlignment(value));
} else if ("position".equals(name)) {
parsePositionAttribute(value, builder);
} else if ("size".equals(name)) {
builder.setWidth(WebvttParserUtil.parsePercentage(value));
} else {
Log.w(TAG, "Unknown cue setting " + name + ":" + value);
}
} catch (NumberFormatException e) {
Log.w(TAG, "Skipping bad cue setting: " + cueSettingMatcher.group());
}
}
}
/**
* Parses the event body of the subtitle.
*
* @param data A {@link ParsableByteArray} from which the body should be read.
* @param cues A list to which parsed cues will be added.
* @param cueTimesUs A sorted list to which parsed cue timestamps will be added.
*/
private void parseEventBody(ParsableByteArray data, List<List<Cue>> cues, List<Long> cueTimesUs) {
@Nullable
SsaDialogueFormat format = haveInitializationData ? dialogueFormatFromInitializationData : null;
@Nullable String currentLine;
while ((currentLine = data.readLine()) != null) {
if (currentLine.startsWith(FORMAT_LINE_PREFIX)) {
format = SsaDialogueFormat.fromFormatLine(currentLine);
} else if (currentLine.startsWith(DIALOGUE_LINE_PREFIX)) {
if (format == null) {
Log.w(TAG, "Skipping dialogue line before complete format: " + currentLine);
continue;
}
parseDialogueLine(currentLine, format, cues, cueTimesUs);
}
}
}
@Cue.AnchorType
private static int toPositionAnchor(@SsaStyle.SsaAlignment int alignment) {
switch (alignment) {
case SsaStyle.SSA_ALIGNMENT_BOTTOM_LEFT:
case SsaStyle.SSA_ALIGNMENT_MIDDLE_LEFT:
case SsaStyle.SSA_ALIGNMENT_TOP_LEFT:
return Cue.ANCHOR_TYPE_START;
case SsaStyle.SSA_ALIGNMENT_BOTTOM_CENTER:
case SsaStyle.SSA_ALIGNMENT_MIDDLE_CENTER:
case SsaStyle.SSA_ALIGNMENT_TOP_CENTER:
return Cue.ANCHOR_TYPE_MIDDLE;
case SsaStyle.SSA_ALIGNMENT_BOTTOM_RIGHT:
case SsaStyle.SSA_ALIGNMENT_MIDDLE_RIGHT:
case SsaStyle.SSA_ALIGNMENT_TOP_RIGHT:
return Cue.ANCHOR_TYPE_END;
case SsaStyle.SSA_ALIGNMENT_UNKNOWN:
return Cue.TYPE_UNSET;
default:
Log.w(TAG, "Unknown alignment: " + alignment);
return Cue.TYPE_UNSET;
}
}
private static Alignment parseTextAlignment(String s) {
switch (s) {
case "start":
case "left":
return Alignment.ALIGN_NORMAL;
case "center":
case "middle":
return Alignment.ALIGN_CENTER;
case "end":
case "right":
return Alignment.ALIGN_OPPOSITE;
default:
Log.w(TAG, "Invalid alignment value: " + s);
return null;
}
}
/**
* Loads the cache UID from the files belonging to the root directory.
*
* @param files The files belonging to the root directory.
* @return The loaded UID, or {@link #UID_UNSET} if a UID has not yet been created.
* @throws IOException If there is an error loading or generating the UID.
*/
private static long loadUid(File[] files) {
for (File file : files) {
String fileName = file.getName();
if (fileName.endsWith(UID_FILE_SUFFIX)) {
try {
return parseUid(fileName);
} catch (NumberFormatException e) {
// This should never happen, but if it does delete the malformed UID file and continue.
Log.e(TAG, "Malformed UID file: " + file);
file.delete();
}
}
}
return UID_UNSET;
}
private void release() {
for (Task task : activeTasks.values()) {
task.cancel(/* released= */ true);
}
try {
downloadIndex.setDownloadingStatesToQueued();
} catch (IOException e) {
Log.e(TAG, "Failed to update index.", e);
}
downloads.clear();
thread.quit();
synchronized (this) {
released = true;
notifyAll();
}
}
protected static int parseCea608AccessibilityChannel(
List<Descriptor> accessibilityDescriptors) {
for (int i = 0; i < accessibilityDescriptors.size(); i++) {
Descriptor descriptor = accessibilityDescriptors.get(i);
if ("urn:scte:dash:cc:cea-608:2015".equals(descriptor.schemeIdUri)
&& descriptor.value != null) {
Matcher accessibilityValueMatcher = CEA_608_ACCESSIBILITY_PATTERN.matcher(descriptor.value);
if (accessibilityValueMatcher.matches()) {
return Integer.parseInt(accessibilityValueMatcher.group(1));
} else {
Log.w(TAG, "Unable to parse CEA-608 channel number from: " + descriptor.value);
}
}
}
return Format.NO_VALUE;
}
@Nullable
private static ApicFrame parseCoverArt(ParsableByteArray data) {
int atomSize = data.readInt();
int atomType = data.readInt();
if (atomType == Atom.TYPE_data) {
int fullVersionInt = data.readInt();
int flags = Atom.parseFullAtomFlags(fullVersionInt);
String mimeType = flags == 13 ? "image/jpeg" : flags == 14 ? "image/png" : null;
if (mimeType == null) {
Log.w(TAG, "Unrecognized cover art flags: " + flags);
return null;
}
data.skipBytes(4); // empty (4)
byte[] pictureData = new byte[atomSize - 16];
data.readBytes(pictureData, 0, pictureData.length);
return new ApicFrame(
mimeType,
/* description= */ null,
/* pictureType= */ PICTURE_TYPE_FRONT_COVER,
pictureData);
}
Log.w(TAG, "Failed to parse cover art attribute");
return null;
}
private void onRemoveTaskStopped(Download download) {
if (download.state == STATE_RESTARTING) {
putDownloadWithState(
download, download.stopReason == STOP_REASON_NONE ? STATE_QUEUED : STATE_STOPPED);
syncTasks();
} else {
int removeIndex = getDownloadIndex(download.request.id);
downloads.remove(removeIndex);
try {
downloadIndex.removeDownload(download.request.id);
} catch (IOException e) {
Log.e(TAG, "Failed to remove from database");
}
DownloadUpdate update =
new DownloadUpdate(download, /* isRemove= */ true, new ArrayList<>(downloads));
mainHandler.obtainMessage(MSG_DOWNLOAD_UPDATE, update).sendToTarget();
}
}
@Override
public synchronized void release() {
if (released) {
return;
}
listeners.clear();
removeStaleSpans();
try {
contentIndex.store();
} catch (IOException e) {
Log.e(TAG, "Storing index file failed", e);
} finally {
unlockFolder(cacheDir);
released = true;
}
}
/**
* @param cache The cache into which data should be written.
* @param fragmentSize For requests that should be fragmented into multiple cache files, this is
* the maximum size of a cache file in bytes. If set to {@link C#LENGTH_UNSET} then no
* fragmentation will occur. Using a small value allows for finer-grained cache eviction
* policies, at the cost of increased overhead both on the cache implementation and the file
* system. Values under {@code (2 * 1024 * 1024)} are not recommended.
* @param bufferSize The buffer size in bytes for writing to a cache file. A zero or negative
* value disables buffering.
*/
public CacheDataSink(Cache cache, long fragmentSize, int bufferSize) {
Assertions.checkState(
fragmentSize > 0 || fragmentSize == C.LENGTH_UNSET,
"fragmentSize must be positive or C.LENGTH_UNSET.");
if (fragmentSize != C.LENGTH_UNSET && fragmentSize < MIN_RECOMMENDED_FRAGMENT_SIZE) {
Log.w(
TAG,
"fragmentSize is below the minimum recommended value of "
+ MIN_RECOMMENDED_FRAGMENT_SIZE
+ ". This may cause poor cache performance.");
}
this.cache = Assertions.checkNotNull(cache);
this.fragmentSize = fragmentSize == C.LENGTH_UNSET ? Long.MAX_VALUE : fragmentSize;
this.bufferSize = bufferSize;
}
/** Releases the given {@code mediaPeriod}, logging and suppressing any errors. */
private static void releaseMediaPeriod(
long endPositionUs, MediaSource mediaSource, MediaPeriod mediaPeriod) {
try {
if (endPositionUs != C.TIME_UNSET && endPositionUs != C.TIME_END_OF_SOURCE) {
mediaSource.releasePeriod(((ClippingMediaPeriod) mediaPeriod).mediaPeriod);
} else {
mediaSource.releasePeriod(mediaPeriod);
}
} catch (RuntimeException e) {
// There's nothing we can do.
Log.e(TAG, "Period release failed.", e);
}
}
@Override
protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlaybackException {
super.onStreamChanged(formats, offsetUs);
if (lastInputTimeUs != C.TIME_UNSET) {
if (pendingStreamChangeCount == pendingStreamChangeTimesUs.length) {
Log.w(
TAG,
"Too many stream changes, so dropping change at "
+ pendingStreamChangeTimesUs[pendingStreamChangeCount - 1]);
} else {
pendingStreamChangeCount++;
}
pendingStreamChangeTimesUs[pendingStreamChangeCount - 1] = lastInputTimeUs;
}
}
private static void parseFontSize(String expression, TtmlStyle out) throws
SubtitleDecoderException {
String[] expressions = Util.split(expression, "\\s+");
Matcher matcher;
if (expressions.length == 1) {
matcher = FONT_SIZE.matcher(expression);
} else if (expressions.length == 2){
matcher = FONT_SIZE.matcher(expressions[1]);
Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font"
+ " size and ignoring the first.");
} else {
throw new SubtitleDecoderException("Invalid number of entries for fontSize: "
+ expressions.length + ".");
}
if (matcher.matches()) {
String unit = matcher.group(3);
switch (unit) {
case "px":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL);
break;
case "em":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_EM);
break;
case "%":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
break;
default:
throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'.");
}
out.setFontSize(Float.valueOf(matcher.group(1)));
} else {
throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'.");
}
}
private static int parseUint8AttributeValue(ParsableByteArray data) {
data.skipBytes(4); // atomSize
int atomType = data.readInt();
if (atomType == Atom.TYPE_data) {
data.skipBytes(8); // version (1), flags (3), empty (4)
return data.readUnsignedByte();
}
Log.w(TAG, "Failed to parse uint8 attribute value");
return -1;
}
@Nullable
private static TextInformationFrame parseTextAttribute(
int type, String id, ParsableByteArray data) {
int atomSize = data.readInt();
int atomType = data.readInt();
if (atomType == Atom.TYPE_data) {
data.skipBytes(8); // version (1), flags (3), empty (4)
String value = data.readNullTerminatedString(atomSize - 16);
return new TextInformationFrame(id, /* description= */ null, value);
}
Log.w(TAG, "Failed to parse text attribute: " + Atom.getAtomTypeString(type));
return null;
}
private static int adjustMaxInputChannelCount(String name, String mimeType, int maxChannelCount) {
if (maxChannelCount > 1 || (Util.SDK_INT >= 26 && maxChannelCount > 0)) {
// The maximum channel count looks like it's been set correctly.
return maxChannelCount;
}
if (MimeTypes.AUDIO_MPEG.equals(mimeType)
|| MimeTypes.AUDIO_AMR_NB.equals(mimeType)
|| MimeTypes.AUDIO_AMR_WB.equals(mimeType)
|| MimeTypes.AUDIO_AAC.equals(mimeType)
|| MimeTypes.AUDIO_VORBIS.equals(mimeType)
|| MimeTypes.AUDIO_OPUS.equals(mimeType)
|| MimeTypes.AUDIO_RAW.equals(mimeType)
|| MimeTypes.AUDIO_FLAC.equals(mimeType)
|| MimeTypes.AUDIO_ALAW.equals(mimeType)
|| MimeTypes.AUDIO_MLAW.equals(mimeType)
|| MimeTypes.AUDIO_MSGSM.equals(mimeType)) {
// Platform code should have set a default.
return maxChannelCount;
}
// The maximum channel count looks incorrect. Adjust it to an assumed default.
int assumedMaxChannelCount;
if (MimeTypes.AUDIO_AC3.equals(mimeType)) {
assumedMaxChannelCount = 6;
} else if (MimeTypes.AUDIO_E_AC3.equals(mimeType)) {
assumedMaxChannelCount = 16;
} else {
// Default to the platform limit, which is 30.
assumedMaxChannelCount = 30;
}
Log.w(TAG, "AssumedMaxChannelAdjustment: " + name + ", [" + maxChannelCount + " to "
+ assumedMaxChannelCount + "]");
return assumedMaxChannelCount;
}
/**
* Returns whether the underlying library supports the specified MIME type.
*
* @param mimeType The MIME type to check.
* @param encoding The PCM encoding for raw audio.
*/
public static boolean supportsFormat(String mimeType, @C.PcmEncoding int encoding) {
String codecName = getCodecName(mimeType, encoding);
if (codecName == null) {
return false;
}
if (!ffmpegHasDecoder(codecName)) {
Log.w(TAG, "No " + codecName + " decoder available. Check the FFmpeg build configuration.");
return false;
}
return true;
}
/**
* Parses a PSSH atom. Version 0 and 1 PSSH atoms are supported.
*
* @param atom The atom to parse.
* @return The parsed PSSH atom. Null if the input is not a valid PSSH atom, or if the PSSH atom
* has an unsupported version.
*/
// TODO: Support parsing of the key ids for version 1 PSSH atoms.
private static @Nullable PsshAtom parsePsshAtom(byte[] atom) {
ParsableByteArray atomData = new ParsableByteArray(atom);
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
// Data too short.
return null;
}
atomData.setPosition(0);
int atomSize = atomData.readInt();
if (atomSize != atomData.bytesLeft() + 4) {
// Not an atom, or incorrect atom size.
return null;
}
int atomType = atomData.readInt();
if (atomType != Atom.TYPE_pssh) {
// Not an atom, or incorrect atom type.
return null;
}
int atomVersion = Atom.parseFullAtomVersion(atomData.readInt());
if (atomVersion > 1) {
Log.w(TAG, "Unsupported pssh version: " + atomVersion);
return null;
}
UUID uuid = new UUID(atomData.readLong(), atomData.readLong());
if (atomVersion == 1) {
int keyIdCount = atomData.readUnsignedIntToInt();
atomData.skipBytes(16 * keyIdCount);
}
int dataSize = atomData.readUnsignedIntToInt();
if (dataSize != atomData.bytesLeft()) {
// Incorrect dataSize.
return null;
}
byte[] data = new byte[dataSize];
atomData.readBytes(data, 0, dataSize);
return new PsshAtom(uuid, atomVersion, data);
}
@Override
public TrackOutput track(int id, int type) {
for (int i = 0; i < trackTypes.length; i++) {
if (type == trackTypes[i]) {
return sampleQueues[i];
}
}
Log.e(TAG, "Unmatched track of type: " + type);
return new DummyTrackOutput();
}
private static Pair<Integer, Integer> getAvcProfileAndLevel(String codec, String[] parts) {
if (parts.length < 2) {
// The codec has fewer parts than required by the AVC codec string format.
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
return null;
}
int profileInteger;
int levelInteger;
try {
if (parts[1].length() == 6) {
// Format: avc1.xxccyy, where xx is profile and yy level, both hexadecimal.
profileInteger = Integer.parseInt(parts[1].substring(0, 2), 16);
levelInteger = Integer.parseInt(parts[1].substring(4), 16);
} else if (parts.length >= 3) {
// Format: avc1.xx.[y]yy where xx is profile and [y]yy level, both decimal.
profileInteger = Integer.parseInt(parts[1]);
levelInteger = Integer.parseInt(parts[2]);
} else {
// We don't recognize the format.
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
return null;
}
} catch (NumberFormatException e) {
Log.w(TAG, "Ignoring malformed AVC codec string: " + codec);
return null;
}
int profile = AVC_PROFILE_NUMBER_TO_CONST.get(profileInteger, -1);
if (profile == -1) {
Log.w(TAG, "Unknown AVC profile: " + profileInteger);
return null;
}
int level = AVC_LEVEL_NUMBER_TO_CONST.get(levelInteger, -1);
if (level == -1) {
Log.w(TAG, "Unknown AVC level: " + levelInteger);
return null;
}
return new Pair<>(profile, level);
}
@Nullable
private static CommentFrame parseCommentAttribute(int type, ParsableByteArray data) {
int atomSize = data.readInt();
int atomType = data.readInt();
if (atomType == Atom.TYPE_data) {
data.skipBytes(8); // version (1), flags (3), empty (4)
String value = data.readNullTerminatedString(atomSize - 16);
return new CommentFrame(LANGUAGE_UNDEFINED, value, value);
}
Log.w(TAG, "Failed to parse comment attribute: " + Atom.getAtomTypeString(type));
return null;
}