use of androidx.media3.common.util.ParsableBitArray in project media by androidx.
the class DtsUtil method getNormalizedFrameHeader.
private static ParsableBitArray getNormalizedFrameHeader(byte[] frameHeader) {
if (frameHeader[0] == FIRST_BYTE_BE) {
// The frame is already 16-bit mode, big endian.
return new ParsableBitArray(frameHeader);
}
// Data is not normalized, but we don't want to modify frameHeader.
frameHeader = Arrays.copyOf(frameHeader, frameHeader.length);
if (isLittleEndianFrameHeader(frameHeader)) {
// Change endianness.
for (int i = 0; i < frameHeader.length - 1; i += 2) {
byte temp = frameHeader[i];
frameHeader[i] = frameHeader[i + 1];
frameHeader[i + 1] = temp;
}
}
ParsableBitArray frameBits = new ParsableBitArray(frameHeader);
if (frameHeader[0] == (byte) (SYNC_VALUE_14B_BE >> 24)) {
// Discard the 2 most significant bits of each 16 bit word.
ParsableBitArray scratchBits = new ParsableBitArray(frameHeader);
while (scratchBits.bitsLeft() >= 16) {
scratchBits.skipBits(2);
frameBits.putInt(scratchBits.readBits(14), 14);
}
}
frameBits.reset(frameHeader);
return frameBits;
}
use of androidx.media3.common.util.ParsableBitArray in project media by androidx.
the class AppInfoTableDecoder method parseAit.
@Nullable
private static Metadata parseAit(ParsableBitArray sectionData) {
// tableId, section_syntax_indication, reserved_future_use, reserved
sectionData.skipBits(12);
int sectionLength = sectionData.readBits(12);
int endOfSection = sectionData.getBytePosition() + sectionLength - 4;
// test_application_flag, application_type, reserved, version_number, current_next_indicator,
// section_number, last_section_number, reserved_future_use
sectionData.skipBits(44);
int commonDescriptorsLength = sectionData.readBits(12);
// Since we currently only keep URL and control code, which are unique per application,
// there is no useful information in common descriptor.
sectionData.skipBytes(commonDescriptorsLength);
// reserved_future_use, application_loop_length
sectionData.skipBits(16);
ArrayList<AppInfoTable> appInfoTables = new ArrayList<>();
while (sectionData.getBytePosition() < endOfSection) {
@Nullable String urlBase = null;
@Nullable String urlExtension = null;
// application_identifier
sectionData.skipBits(48);
int controlCode = sectionData.readBits(8);
// reserved_future_use
sectionData.skipBits(4);
int applicationDescriptorsLoopLength = sectionData.readBits(12);
int positionOfNextApplication = sectionData.getBytePosition() + applicationDescriptorsLoopLength;
while (sectionData.getBytePosition() < positionOfNextApplication) {
int descriptorTag = sectionData.readBits(8);
int descriptorLength = sectionData.readBits(8);
int positionOfNextDescriptor = sectionData.getBytePosition() + descriptorLength;
if (descriptorTag == DESCRIPTOR_TRANSPORT_PROTOCOL) {
// See section 5.3.6.
int protocolId = sectionData.readBits(16);
// label
sectionData.skipBits(8);
if (protocolId == TRANSPORT_PROTOCOL_HTTP) {
// See section 5.3.6.2.
while (sectionData.getBytePosition() < positionOfNextDescriptor) {
int urlBaseLength = sectionData.readBits(8);
urlBase = sectionData.readBytesAsString(urlBaseLength, Charsets.US_ASCII);
int extensionCount = sectionData.readBits(8);
for (int urlExtensionIndex = 0; urlExtensionIndex < extensionCount; urlExtensionIndex++) {
int urlExtensionLength = sectionData.readBits(8);
sectionData.skipBytes(urlExtensionLength);
}
}
}
} else if (descriptorTag == DESCRIPTOR_SIMPLE_APPLICATION_LOCATION) {
// See section 5.3.7.
urlExtension = sectionData.readBytesAsString(descriptorLength, Charsets.US_ASCII);
}
sectionData.setPosition(positionOfNextDescriptor * 8);
}
sectionData.setPosition(positionOfNextApplication * 8);
if (urlBase != null && urlExtension != null) {
appInfoTables.add(new AppInfoTable(controlCode, urlBase + urlExtension));
}
}
return appInfoTables.isEmpty() ? null : new Metadata(appInfoTables);
}
use of androidx.media3.common.util.ParsableBitArray in project media by androidx.
the class DvbParser method paintPixelDataSubBlock.
/**
* Draws a pixel data sub-block, as defined by ETSI EN 300 743 7.2.5.1, into a canvas.
*/
private static void paintPixelDataSubBlock(byte[] pixelData, int[] clutEntries, int regionDepth, int horizontalAddress, int verticalAddress, @Nullable Paint paint, Canvas canvas) {
ParsableBitArray data = new ParsableBitArray(pixelData);
int column = horizontalAddress;
int line = verticalAddress;
@Nullable byte[] clutMapTable2To4 = null;
@Nullable byte[] clutMapTable2To8 = null;
@Nullable byte[] clutMapTable4To8 = null;
while (data.bitsLeft() != 0) {
int dataType = data.readBits(8);
switch(dataType) {
case DATA_TYPE_2BP_CODE_STRING:
@Nullable byte[] clutMapTable2ToX;
if (regionDepth == REGION_DEPTH_8_BIT) {
clutMapTable2ToX = clutMapTable2To8 == null ? defaultMap2To8 : clutMapTable2To8;
} else if (regionDepth == REGION_DEPTH_4_BIT) {
clutMapTable2ToX = clutMapTable2To4 == null ? defaultMap2To4 : clutMapTable2To4;
} else {
clutMapTable2ToX = null;
}
column = paint2BitPixelCodeString(data, clutEntries, clutMapTable2ToX, column, line, paint, canvas);
data.byteAlign();
break;
case DATA_TYPE_4BP_CODE_STRING:
@Nullable byte[] clutMapTable4ToX;
if (regionDepth == REGION_DEPTH_8_BIT) {
clutMapTable4ToX = clutMapTable4To8 == null ? defaultMap4To8 : clutMapTable4To8;
} else {
clutMapTable4ToX = null;
}
column = paint4BitPixelCodeString(data, clutEntries, clutMapTable4ToX, column, line, paint, canvas);
data.byteAlign();
break;
case DATA_TYPE_8BP_CODE_STRING:
column = paint8BitPixelCodeString(data, clutEntries, /* clutMapTable= */
null, column, line, paint, canvas);
break;
case DATA_TYPE_24_TABLE_DATA:
clutMapTable2To4 = buildClutMapTable(4, 4, data);
break;
case DATA_TYPE_28_TABLE_DATA:
clutMapTable2To8 = buildClutMapTable(4, 8, data);
break;
case DATA_TYPE_48_TABLE_DATA:
clutMapTable4To8 = buildClutMapTable(16, 8, data);
break;
case DATA_TYPE_END_LINE:
column = horizontalAddress;
line += 2;
break;
default:
// Do nothing.
break;
}
}
}
use of androidx.media3.common.util.ParsableBitArray in project media by androidx.
the class DvbParser method decode.
/**
* Decodes a subtitling packet, returning a list of parsed {@link Cue}s.
*
* @param data The subtitling packet data to decode.
* @param limit The limit in {@code data} at which to stop decoding.
* @return The parsed {@link Cue}s.
*/
public List<Cue> decode(byte[] data, int limit) {
// Parse the input data.
ParsableBitArray dataBitArray = new ParsableBitArray(data, limit);
while (// sync_byte (8) + segment header (40)
dataBitArray.bitsLeft() >= 48 && dataBitArray.readBits(8) == 0x0F) {
parseSubtitlingSegment(dataBitArray, subtitleService);
}
@Nullable PageComposition pageComposition = subtitleService.pageComposition;
if (pageComposition == null) {
return Collections.emptyList();
}
// Update the canvas bitmap if necessary.
DisplayDefinition displayDefinition = subtitleService.displayDefinition != null ? subtitleService.displayDefinition : defaultDisplayDefinition;
if (bitmap == null || displayDefinition.width + 1 != bitmap.getWidth() || displayDefinition.height + 1 != bitmap.getHeight()) {
bitmap = Bitmap.createBitmap(displayDefinition.width + 1, displayDefinition.height + 1, Bitmap.Config.ARGB_8888);
canvas.setBitmap(bitmap);
}
// Build the cues.
List<Cue> cues = new ArrayList<>();
SparseArray<PageRegion> pageRegions = pageComposition.regions;
for (int i = 0; i < pageRegions.size(); i++) {
// Save clean clipping state.
canvas.save();
PageRegion pageRegion = pageRegions.valueAt(i);
int regionId = pageRegions.keyAt(i);
RegionComposition regionComposition = subtitleService.regions.get(regionId);
// Clip drawing to the current region and display definition window.
int baseHorizontalAddress = pageRegion.horizontalAddress + displayDefinition.horizontalPositionMinimum;
int baseVerticalAddress = pageRegion.verticalAddress + displayDefinition.verticalPositionMinimum;
int clipRight = min(baseHorizontalAddress + regionComposition.width, displayDefinition.horizontalPositionMaximum);
int clipBottom = min(baseVerticalAddress + regionComposition.height, displayDefinition.verticalPositionMaximum);
canvas.clipRect(baseHorizontalAddress, baseVerticalAddress, clipRight, clipBottom);
ClutDefinition clutDefinition = subtitleService.cluts.get(regionComposition.clutId);
if (clutDefinition == null) {
clutDefinition = subtitleService.ancillaryCluts.get(regionComposition.clutId);
if (clutDefinition == null) {
clutDefinition = defaultClutDefinition;
}
}
SparseArray<RegionObject> regionObjects = regionComposition.regionObjects;
for (int j = 0; j < regionObjects.size(); j++) {
int objectId = regionObjects.keyAt(j);
RegionObject regionObject = regionObjects.valueAt(j);
ObjectData objectData = subtitleService.objects.get(objectId);
if (objectData == null) {
objectData = subtitleService.ancillaryObjects.get(objectId);
}
if (objectData != null) {
@Nullable Paint paint = objectData.nonModifyingColorFlag ? null : defaultPaint;
paintPixelDataSubBlocks(objectData, clutDefinition, regionComposition.depth, baseHorizontalAddress + regionObject.horizontalPosition, baseVerticalAddress + regionObject.verticalPosition, paint, canvas);
}
}
if (regionComposition.fillFlag) {
int color;
if (regionComposition.depth == REGION_DEPTH_8_BIT) {
color = clutDefinition.clutEntries8Bit[regionComposition.pixelCode8Bit];
} else if (regionComposition.depth == REGION_DEPTH_4_BIT) {
color = clutDefinition.clutEntries4Bit[regionComposition.pixelCode4Bit];
} else {
color = clutDefinition.clutEntries2Bit[regionComposition.pixelCode2Bit];
}
fillRegionPaint.setColor(color);
canvas.drawRect(baseHorizontalAddress, baseVerticalAddress, baseHorizontalAddress + regionComposition.width, baseVerticalAddress + regionComposition.height, fillRegionPaint);
}
cues.add(new Cue.Builder().setBitmap(Bitmap.createBitmap(bitmap, baseHorizontalAddress, baseVerticalAddress, regionComposition.width, regionComposition.height)).setPosition((float) baseHorizontalAddress / displayDefinition.width).setPositionAnchor(Cue.ANCHOR_TYPE_START).setLine((float) baseVerticalAddress / displayDefinition.height, Cue.LINE_TYPE_FRACTION).setLineAnchor(Cue.ANCHOR_TYPE_START).setSize((float) regionComposition.width / displayDefinition.width).setBitmapHeight((float) regionComposition.height / displayDefinition.height).build());
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Restore clean clipping state.
canvas.restore();
}
return Collections.unmodifiableList(cues);
}
use of androidx.media3.common.util.ParsableBitArray in project media by androidx.
the class H263Reader method parseCsdBuffer.
/**
* Parses a codec-specific data buffer, returning the {@link Format} of the media.
*
* @param csdBuffer The buffer to parse.
* @param volStartPosition The byte offset of the start of the video object layer in the buffer.
* @param formatId The ID for the generated format.
* @return The {@link Format} of the media represented in the buffer.
*/
private static Format parseCsdBuffer(CsdBuffer csdBuffer, int volStartPosition, String formatId) {
byte[] csdData = Arrays.copyOf(csdBuffer.data, csdBuffer.length);
ParsableBitArray buffer = new ParsableBitArray(csdData);
buffer.skipBytes(volStartPosition);
// Parse the video object layer defined in ISO 14496-2 (2001) subsection 6.2.3.
// video_object_layer_start_code
buffer.skipBytes(4);
// random_accessible_vol
buffer.skipBit();
// video_object_type_indication
buffer.skipBits(8);
if (buffer.readBit()) {
// is_object_layer_identifier
// video_object_layer_verid
buffer.skipBits(4);
// video_object_layer_priority
buffer.skipBits(3);
}
float pixelWidthHeightRatio;
int aspectRatioInfo = buffer.readBits(4);
if (aspectRatioInfo == 0x0F) {
// extended_PAR
int parWidth = buffer.readBits(8);
int parHeight = buffer.readBits(8);
if (parHeight == 0) {
Log.w(TAG, "Invalid aspect ratio");
pixelWidthHeightRatio = 1f;
} else {
pixelWidthHeightRatio = (float) parWidth / parHeight;
}
} else if (aspectRatioInfo < PIXEL_WIDTH_HEIGHT_RATIO_BY_ASPECT_RATIO_INFO.length) {
pixelWidthHeightRatio = PIXEL_WIDTH_HEIGHT_RATIO_BY_ASPECT_RATIO_INFO[aspectRatioInfo];
} else {
Log.w(TAG, "Invalid aspect ratio");
pixelWidthHeightRatio = 1f;
}
if (buffer.readBit()) {
// vol_control_parameters
// chroma_format
buffer.skipBits(2);
// low_delay
buffer.skipBits(1);
if (buffer.readBit()) {
// vbv_parameters
// first_half_bit_rate
buffer.skipBits(15);
// marker_bit
buffer.skipBit();
// latter_half_bit_rate
buffer.skipBits(15);
// marker_bit
buffer.skipBit();
// first_half_vbv_buffer_size
buffer.skipBits(15);
// marker_bit
buffer.skipBit();
// latter_half_vbv_buffer_size
buffer.skipBits(3);
// first_half_vbv_occupancy
buffer.skipBits(11);
// marker_bit
buffer.skipBit();
// latter_half_vbv_occupancy
buffer.skipBits(15);
// marker_bit
buffer.skipBit();
}
}
int videoObjectLayerShape = buffer.readBits(2);
if (videoObjectLayerShape != VIDEO_OBJECT_LAYER_SHAPE_RECTANGULAR) {
Log.w(TAG, "Unhandled video object layer shape");
}
// marker_bit
buffer.skipBit();
int vopTimeIncrementResolution = buffer.readBits(16);
// marker_bit
buffer.skipBit();
if (buffer.readBit()) {
// fixed_vop_rate
if (vopTimeIncrementResolution == 0) {
Log.w(TAG, "Invalid vop_increment_time_resolution");
} else {
vopTimeIncrementResolution--;
int numBits = 0;
while (vopTimeIncrementResolution > 0) {
++numBits;
vopTimeIncrementResolution >>= 1;
}
// fixed_vop_time_increment
buffer.skipBits(numBits);
}
}
// marker_bit
buffer.skipBit();
int videoObjectLayerWidth = buffer.readBits(13);
// marker_bit
buffer.skipBit();
int videoObjectLayerHeight = buffer.readBits(13);
// marker_bit
buffer.skipBit();
// interlaced
buffer.skipBit();
return new Format.Builder().setId(formatId).setSampleMimeType(MimeTypes.VIDEO_MP4V).setWidth(videoObjectLayerWidth).setHeight(videoObjectLayerHeight).setPixelWidthHeightRatio(pixelWidthHeightRatio).setInitializationData(Collections.singletonList(csdData)).build();
}
Aggregations