Commit 105120bd authored by David Flynn's avatar David Flynn
Browse files

hls/m51025: support multi-frame sequences

This adds:
 - a frame_idx syntax element to every geometry slice header.
 - (decoder) handling of a frame boundary data unit.

Frame boundaries are represented by changes in the value of frame_idx
or by the presence of a frame boundary data unit.
parent 8c37a777
...@@ -80,8 +80,10 @@ public: ...@@ -80,8 +80,10 @@ public:
//========================================================================== //==========================================================================
private: private:
void activateParameterSets(const GeometryBrickHeader& gbh);
int decodeGeometryBrick(const PayloadBuffer& buf); int decodeGeometryBrick(const PayloadBuffer& buf);
void decodeAttributeBrick(const PayloadBuffer& buf); void decodeAttributeBrick(const PayloadBuffer& buf);
bool frameIdxChanged(const GeometryBrickHeader& gbh) const;
//========================================================================== //==========================================================================
...@@ -97,6 +99,9 @@ private: ...@@ -97,6 +99,9 @@ private:
// Current identifier of payloads with the same geometry // Current identifier of payloads with the same geometry
int _sliceId; int _sliceId;
// The last decoded frame_idx
int _currentFrameIdx;
// Position of the slice in the translated+scaled co-ordinate system. // Position of the slice in the translated+scaled co-ordinate system.
Vec3<int> _sliceOrigin; Vec3<int> _sliceOrigin;
......
...@@ -86,7 +86,7 @@ struct EncoderParams { ...@@ -86,7 +86,7 @@ struct EncoderParams {
class PCCTMC3Encoder3 { class PCCTMC3Encoder3 {
public: public:
PCCTMC3Encoder3() = default; PCCTMC3Encoder3();
PCCTMC3Encoder3(const PCCTMC3Encoder3&) = default; PCCTMC3Encoder3(const PCCTMC3Encoder3&) = default;
PCCTMC3Encoder3& operator=(const PCCTMC3Encoder3& rhs) = default; PCCTMC3Encoder3& operator=(const PCCTMC3Encoder3& rhs) = default;
~PCCTMC3Encoder3() = default; ~PCCTMC3Encoder3() = default;
...@@ -138,6 +138,10 @@ private: ...@@ -138,6 +138,10 @@ private:
// Identifies the current tile // Identifies the current tile
int _tileId; int _tileId;
// Current frame number.
// NB: only the log2_max_frame_idx LSBs are sampled for frame_idx
int _frameCounter;
// Map quantized points to the original input points // Map quantized points to the original input points
std::multimap<Vec3<double>, int32_t> quantizedToOrigin; std::multimap<Vec3<double>, int32_t> quantizedToOrigin;
}; };
......
...@@ -54,6 +54,7 @@ namespace pcc { ...@@ -54,6 +54,7 @@ namespace pcc {
void void
PCCTMC3Decoder3::init() PCCTMC3Decoder3::init()
{ {
_currentFrameIdx = -1;
_sps = nullptr; _sps = nullptr;
_gps = nullptr; _gps = nullptr;
_spss.clear(); _spss.clear();
...@@ -63,15 +64,23 @@ PCCTMC3Decoder3::init() ...@@ -63,15 +64,23 @@ PCCTMC3Decoder3::init()
//============================================================================ //============================================================================
static bool
payloadStartsNewSlice(PayloadType type)
{
return type == PayloadType::kGeometryBrick
|| type == PayloadType::kFrameBoundaryMarker;
}
//============================================================================
int int
PCCTMC3Decoder3::decompress( PCCTMC3Decoder3::decompress(
const PayloadBuffer* buf, const PayloadBuffer* buf,
std::function<void(const PCCPointSet3&)> onOutputCloud) std::function<void(const PCCPointSet3&)> onOutputCloud)
{ {
// todo(df): call _accumCloud.clear() at start of frame
// Starting a new geometry brick/slice/tile, transfer any // Starting a new geometry brick/slice/tile, transfer any
// finished points to the output accumulator // finished points to the output accumulator
if (!buf || buf->type == PayloadType::kGeometryBrick) { if (!buf || payloadStartsNewSlice(buf->type)) {
if (size_t numPoints = _currentPointCloud.getPointCount()) { if (size_t numPoints = _currentPointCloud.getPointCount()) {
for (size_t i = 0; i < numPoints; i++) for (size_t i = 0; i < numPoints; i++)
for (int k = 0; k < 3; k++) for (int k = 0; k < 3; k++)
...@@ -83,6 +92,7 @@ PCCTMC3Decoder3::decompress( ...@@ -83,6 +92,7 @@ PCCTMC3Decoder3::decompress(
if (!buf) { if (!buf) {
// flush decoder, output pending cloud if any // flush decoder, output pending cloud if any
onOutputCloud(_accumCloud); onOutputCloud(_accumCloud);
_accumCloud.clear();
return 0; return 0;
} }
...@@ -93,8 +103,21 @@ PCCTMC3Decoder3::decompress( ...@@ -93,8 +103,21 @@ PCCTMC3Decoder3::decompress(
case PayloadType::kAttributeParameterSet: storeAps(parseAps(*buf)); return 0; case PayloadType::kAttributeParameterSet: storeAps(parseAps(*buf)); return 0;
// the frame boundary marker flushes the current frame.
// NB: frame counter is reset to avoid outputing a runt point cloud
// on the next slice.
case PayloadType::kFrameBoundaryMarker:
onOutputCloud(_accumCloud);
_accumCloud.clear();
_currentFrameIdx = -1;
return 0;
case PayloadType::kGeometryBrick: case PayloadType::kGeometryBrick:
// todo(df): call onOutputCloud when starting a new cloud activateParameterSets(parseGbhIds(*buf));
if (frameIdxChanged(parseGbh(*_sps, *_gps, *buf, nullptr))) {
onOutputCloud(_accumCloud);
_accumCloud.clear();
}
return decodeGeometryBrick(*buf); return decodeGeometryBrick(*buf);
case PayloadType::kAttributeBrick: decodeAttributeBrick(*buf); return 0; case PayloadType::kAttributeBrick: decodeAttributeBrick(*buf); return 0;
...@@ -145,14 +168,21 @@ PCCTMC3Decoder3::storeTileInventory(TileInventory&& inventory) ...@@ -145,14 +168,21 @@ PCCTMC3Decoder3::storeTileInventory(TileInventory&& inventory)
} }
//========================================================================== //==========================================================================
// Initialise the point cloud storage and decode a single geometry slice.
int bool
PCCTMC3Decoder3::decodeGeometryBrick(const PayloadBuffer& buf) PCCTMC3Decoder3::frameIdxChanged(const GeometryBrickHeader& gbh) const
{ {
assert(buf.type == PayloadType::kGeometryBrick); // dont treat the first frame in the sequence as a frame boundary
std::cout << "positions bitstream size " << buf.size() << " B\n"; if (_currentFrameIdx < 0)
return false;
return _currentFrameIdx != gbh.frame_idx;
}
//==========================================================================
void
PCCTMC3Decoder3::activateParameterSets(const GeometryBrickHeader& gbh)
{
// HACK: assume activation of the first SPS and GPS // HACK: assume activation of the first SPS and GPS
// todo(df): parse brick header here for propper sps & gps activation // todo(df): parse brick header here for propper sps & gps activation
// -- this is currently inconsistent between trisoup and octree // -- this is currently inconsistent between trisoup and octree
...@@ -160,6 +190,16 @@ PCCTMC3Decoder3::decodeGeometryBrick(const PayloadBuffer& buf) ...@@ -160,6 +190,16 @@ PCCTMC3Decoder3::decodeGeometryBrick(const PayloadBuffer& buf)
assert(!_gpss.empty()); assert(!_gpss.empty());
_sps = &_spss.cbegin()->second; _sps = &_spss.cbegin()->second;
_gps = &_gpss.cbegin()->second; _gps = &_gpss.cbegin()->second;
}
//==========================================================================
// Initialise the point cloud storage and decode a single geometry slice.
int
PCCTMC3Decoder3::decodeGeometryBrick(const PayloadBuffer& buf)
{
assert(buf.type == PayloadType::kGeometryBrick);
std::cout << "positions bitstream size " << buf.size() << " B\n";
// todo(df): replace with attribute mapping // todo(df): replace with attribute mapping
bool hasColour = std::any_of( bool hasColour = std::any_of(
...@@ -181,9 +221,10 @@ PCCTMC3Decoder3::decodeGeometryBrick(const PayloadBuffer& buf) ...@@ -181,9 +221,10 @@ PCCTMC3Decoder3::decodeGeometryBrick(const PayloadBuffer& buf)
clock_user.start(); clock_user.start();
int gbhSize; int gbhSize;
_gbh = parseGbh(*_gps, buf, &gbhSize); _gbh = parseGbh(*_sps, *_gps, buf, &gbhSize);
_sliceId = _gbh.geom_slice_id; _sliceId = _gbh.geom_slice_id;
_sliceOrigin = _gbh.geomBoxOrigin; _sliceOrigin = _gbh.geomBoxOrigin;
_currentFrameIdx = _gbh.frame_idx;
EntropyDecoder arithmeticDecoder; EntropyDecoder arithmeticDecoder;
arithmeticDecoder.enableBypassStream(_sps->cabac_bypass_stream_enabled_flag); arithmeticDecoder.enableBypassStream(_sps->cabac_bypass_stream_enabled_flag);
......
...@@ -50,6 +50,11 @@ namespace pcc { ...@@ -50,6 +50,11 @@ namespace pcc {
//============================================================================ //============================================================================
PCCTMC3Encoder3::PCCTMC3Encoder3() : _frameCounter(-1)
{}
//============================================================================
int int
PCCTMC3Encoder3::compress( PCCTMC3Encoder3::compress(
const PCCPointSet3& inputPointCloud, const PCCPointSet3& inputPointCloud,
...@@ -57,6 +62,9 @@ PCCTMC3Encoder3::compress( ...@@ -57,6 +62,9 @@ PCCTMC3Encoder3::compress(
std::function<void(const PayloadBuffer&)> outputFn, std::function<void(const PayloadBuffer&)> outputFn,
PCCPointSet3* reconstructedCloud) PCCPointSet3* reconstructedCloud)
{ {
// start of frame
_frameCounter++;
fixupParameterSets(params); fixupParameterSets(params);
// Determine input bounding box (for SPS metadata) if not manually set // Determine input bounding box (for SPS metadata) if not manually set
...@@ -264,6 +272,9 @@ PCCTMC3Encoder3::fixupParameterSets(EncoderParams* params) ...@@ -264,6 +272,9 @@ PCCTMC3Encoder3::fixupParameterSets(EncoderParams* params)
params->sps.profileCompatibility.profile_compatibility_flags = 0; params->sps.profileCompatibility.profile_compatibility_flags = 0;
params->sps.level = 0; params->sps.level = 0;
// use one bit to indicate frame boundaries
params->sps.log2_max_frame_idx = 1;
// the encoder writes out the slice origin in the GBH // the encoder writes out the slice origin in the GBH
// NB: this may be disabled during encoding // NB: this may be disabled during encoding
params->gps.geom_box_present_flag = true; params->gps.geom_box_present_flag = true;
...@@ -456,11 +467,12 @@ PCCTMC3Encoder3::encodeGeometryBrick(PayloadBuffer* buf) ...@@ -456,11 +467,12 @@ PCCTMC3Encoder3::encodeGeometryBrick(PayloadBuffer* buf)
gbh.geom_geom_parameter_set_id = _gps->gps_geom_parameter_set_id; gbh.geom_geom_parameter_set_id = _gps->gps_geom_parameter_set_id;
gbh.geom_slice_id = _sliceId; gbh.geom_slice_id = _sliceId;
gbh.geom_tile_id = std::max(0, _tileId); gbh.geom_tile_id = std::max(0, _tileId);
gbh.frame_idx = _frameCounter & ((1 << _sps->log2_max_frame_idx) - 1);
gbh.geomBoxOrigin = _sliceOrigin; gbh.geomBoxOrigin = _sliceOrigin;
gbh.geom_box_log2_scale = 0; gbh.geom_box_log2_scale = 0;
gbh.geom_max_node_size_log2 = nodeSizeLog2; gbh.geom_max_node_size_log2 = nodeSizeLog2;
gbh.geom_num_points = int(pointCloud.getPointCount()); gbh.geom_num_points = int(pointCloud.getPointCount());
write(*_gps, gbh, buf); write(*_sps, *_gps, gbh, buf);
// todo(df): remove estimate when arithmetic codec is replaced // todo(df): remove estimate when arithmetic codec is replaced
int maxAcBufLen = int(pointCloud.getPointCount()) * 3 * 4 + 1024; int maxAcBufLen = int(pointCloud.getPointCount()) * 3 * 4 + 1024;
......
...@@ -53,6 +53,7 @@ enum class PayloadType ...@@ -53,6 +53,7 @@ enum class PayloadType
kAttributeParameterSet = 3, kAttributeParameterSet = 3,
kAttributeBrick = 4, kAttributeBrick = 4,
kTileInventory = 5, kTileInventory = 5,
kFrameBoundaryMarker = 6,
}; };
//============================================================================ //============================================================================
...@@ -158,6 +159,9 @@ struct SequenceParameterSet { ...@@ -158,6 +159,9 @@ struct SequenceParameterSet {
// NB: attributeSets.size() = num_attribute_sets // NB: attributeSets.size() = num_attribute_sets
std::vector<AttributeDescription> attributeSets; std::vector<AttributeDescription> attributeSets;
// The number of bits to use for frame_idx
int log2_max_frame_idx;
// Controls whether bypass bins are written to a seperate sub-stream, or // Controls whether bypass bins are written to a seperate sub-stream, or
// encoded as ep bins via CABAC. // encoded as ep bins via CABAC.
bool cabac_bypass_stream_enabled_flag; bool cabac_bypass_stream_enabled_flag;
...@@ -224,6 +228,7 @@ struct GeometryBrickHeader { ...@@ -224,6 +228,7 @@ struct GeometryBrickHeader {
int geom_geom_parameter_set_id; int geom_geom_parameter_set_id;
int geom_tile_id; int geom_tile_id;
int geom_slice_id; int geom_slice_id;
int frame_idx;
// derived from geom_box_origin_{x,y,z} * (1 << geom_box_log2_scale) // derived from geom_box_origin_{x,y,z} * (1 << geom_box_log2_scale)
Vec3<int> geomBoxOrigin; Vec3<int> geomBoxOrigin;
......
...@@ -111,6 +111,7 @@ write(const SequenceParameterSet& sps) ...@@ -111,6 +111,7 @@ write(const SequenceParameterSet& sps)
} }
} }
bs.writeUn(5, sps.log2_max_frame_idx);
bs.write(sps.cabac_bypass_stream_enabled_flag); bs.write(sps.cabac_bypass_stream_enabled_flag);
bool sps_extension_flag = false; bool sps_extension_flag = false;
...@@ -171,6 +172,7 @@ parseSps(const PayloadBuffer& buf) ...@@ -171,6 +172,7 @@ parseSps(const PayloadBuffer& buf)
} }
} }
bs.readUn(5, &sps.log2_max_frame_idx);
bs.read(&sps.cabac_bypass_stream_enabled_flag); bs.read(&sps.cabac_bypass_stream_enabled_flag);
bool sps_extension_flag = bs.read(); bool sps_extension_flag = bs.read();
...@@ -371,6 +373,7 @@ parseAps(const PayloadBuffer& buf) ...@@ -371,6 +373,7 @@ parseAps(const PayloadBuffer& buf)
void void
write( write(
const SequenceParameterSet& sps,
const GeometryParameterSet& gps, const GeometryParameterSet& gps,
const GeometryBrickHeader& gbh, const GeometryBrickHeader& gbh,
PayloadBuffer* buf) PayloadBuffer* buf)
...@@ -381,6 +384,7 @@ write( ...@@ -381,6 +384,7 @@ write(
bs.writeUe(gbh.geom_geom_parameter_set_id); bs.writeUe(gbh.geom_geom_parameter_set_id);
bs.writeUe(gbh.geom_tile_id); bs.writeUe(gbh.geom_tile_id);
bs.writeUe(gbh.geom_slice_id); bs.writeUe(gbh.geom_slice_id);
bs.writeUn(sps.log2_max_frame_idx, gbh.frame_idx);
if (gps.geom_box_present_flag) { if (gps.geom_box_present_flag) {
int geomBoxLog2Scale = gbh.geomBoxLog2Scale(gps); int geomBoxLog2Scale = gbh.geomBoxLog2Scale(gps);
...@@ -404,7 +408,10 @@ write( ...@@ -404,7 +408,10 @@ write(
GeometryBrickHeader GeometryBrickHeader
parseGbh( parseGbh(
const GeometryParameterSet& gps, const PayloadBuffer& buf, int* bytesRead) const SequenceParameterSet& sps,
const GeometryParameterSet& gps,
const PayloadBuffer& buf,
int* bytesRead)
{ {
GeometryBrickHeader gbh; GeometryBrickHeader gbh;
assert(buf.type == PayloadType::kGeometryBrick); assert(buf.type == PayloadType::kGeometryBrick);
...@@ -413,6 +420,7 @@ parseGbh( ...@@ -413,6 +420,7 @@ parseGbh(
bs.readUe(&gbh.geom_geom_parameter_set_id); bs.readUe(&gbh.geom_geom_parameter_set_id);
bs.readUe(&gbh.geom_tile_id); bs.readUe(&gbh.geom_tile_id);
bs.readUe(&gbh.geom_slice_id); bs.readUe(&gbh.geom_slice_id);
bs.readUn(sps.log2_max_frame_idx, &gbh.frame_idx);
if (gps.geom_box_present_flag) { if (gps.geom_box_present_flag) {
if (gps.geom_box_log2_scale_present_flag) if (gps.geom_box_log2_scale_present_flag)
...@@ -440,6 +448,25 @@ parseGbh( ...@@ -440,6 +448,25 @@ parseGbh(
return gbh; return gbh;
} }
//----------------------------------------------------------------------------
GeometryBrickHeader
parseGbhIds(const PayloadBuffer& buf)
{
GeometryBrickHeader gbh;
assert(buf.type == PayloadType::kGeometryBrick);
auto bs = makeBitReader(buf.begin(), buf.end());
bs.readUe(&gbh.geom_geom_parameter_set_id);
bs.readUe(&gbh.geom_tile_id);
bs.readUe(&gbh.geom_slice_id);
/* NB: this function only decodes ids at the start of the header. */
/* NB: do not attempt to parse any further */
return gbh;
}
//============================================================================ //============================================================================
void void
......
...@@ -55,6 +55,7 @@ TileInventory parseTileInventory(const PayloadBuffer& buf); ...@@ -55,6 +55,7 @@ TileInventory parseTileInventory(const PayloadBuffer& buf);
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void write( void write(
const SequenceParameterSet& sps,
const GeometryParameterSet& gps, const GeometryParameterSet& gps,
const GeometryBrickHeader& gbh, const GeometryBrickHeader& gbh,
PayloadBuffer* buf); PayloadBuffer* buf);
...@@ -65,11 +66,20 @@ void write( ...@@ -65,11 +66,20 @@ void write(
PayloadBuffer* buf); PayloadBuffer* buf);
GeometryBrickHeader parseGbh( GeometryBrickHeader parseGbh(
const GeometryParameterSet& gps, const PayloadBuffer& buf, int* bytesRead); const SequenceParameterSet& sps,
const GeometryParameterSet& gps,
const PayloadBuffer& buf,
int* bytesRead);
AttributeBrickHeader parseAbh( AttributeBrickHeader parseAbh(
const AttributeParameterSet& aps, const PayloadBuffer& buf, int* bytesRead); const AttributeParameterSet& aps, const PayloadBuffer& buf, int* bytesRead);
/**
* Parse @buf, decoding only the parameter set, slice, tile.
* NB: the returned header is intentionally incomplete.
*/
GeometryBrickHeader parseGbhIds(const PayloadBuffer& buf);
/** /**
* Parse @buf, decoding only the parameter set and slice ids. * Parse @buf, decoding only the parameter set and slice ids.
* NB: the returned header is intentionally incomplete. * NB: the returned header is intentionally incomplete.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment