[APU] XMA: Cross-buffer split frames.
This commit is contained in:
parent
6547fa1748
commit
534e263b05
|
@ -359,6 +359,8 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
|
|
||||||
// is_dirty_ = true; // TODO
|
// is_dirty_ = true; // TODO
|
||||||
// is_dirty_ = false; // TODO
|
// is_dirty_ = false; // TODO
|
||||||
|
assert_false(data->stop_when_done);
|
||||||
|
assert_false(data->interrupt_when_done);
|
||||||
static int total_samples = 0;
|
static int total_samples = 0;
|
||||||
// Decode until we can't write any more data.
|
// Decode until we can't write any more data.
|
||||||
while (output_remaining_bytes > 0) {
|
while (output_remaining_bytes > 0) {
|
||||||
|
@ -367,91 +369,42 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_true(packets_skip_ == 0);
|
// assert_true(packets_skip_ == 0);
|
||||||
assert_true(split_frame_len_ == 0);
|
// assert_true(split_frame_len_ == 0);
|
||||||
assert_true(split_frame_len_partial_ == 0);
|
// assert_true(split_frame_len_partial_ == 0);
|
||||||
|
|
||||||
if (data->input_buffer_read_offset == 0) {
|
|
||||||
// Invalid offset. Go ahead and set it.
|
|
||||||
auto offset = xma::GetPacketFrameOffset(current_input_buffer);
|
|
||||||
if (offset == -1) {
|
|
||||||
// No more frames.
|
|
||||||
SwapInputBuffer(data);
|
|
||||||
// TODO partial frames? end?
|
|
||||||
assert_always("TODO");
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
data->input_buffer_read_offset = offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ValidFrameOffset(current_input_buffer, current_input_size,
|
|
||||||
data->input_buffer_read_offset)) {
|
|
||||||
XELOGAPU("XmaContext {}: Invalid read offset {}!", id(),
|
|
||||||
data->input_buffer_read_offset);
|
|
||||||
SwapInputBuffer(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Where are we in the buffer (in XMA jargon)
|
// Where are we in the buffer (in XMA jargon)
|
||||||
auto [packet_idx, frame_idx] =
|
int packet_idx, frame_idx, frame_count;
|
||||||
GetFrameNumber(current_input_buffer, current_input_size,
|
uint8_t* packet;
|
||||||
data->input_buffer_read_offset);
|
bool frame_last_split;
|
||||||
// TODO handle
|
|
||||||
assert_true(packet_idx >= 0);
|
|
||||||
assert_true(frame_idx >= 0);
|
|
||||||
auto packet = current_input_buffer + packet_idx * kBytesPerPacket;
|
|
||||||
// frames that belong to this packet
|
|
||||||
auto [frame_count, frame_last_split] = GetPacketFrameCount(packet);
|
|
||||||
assert_true(frame_count >= 0); // TODO end
|
|
||||||
// Current frame is split to next packet:
|
|
||||||
bool frame_is_split = frame_last_split && (frame_idx >= frame_count - 1);
|
|
||||||
|
|
||||||
PrepareDecoder(packet, data->sample_rate, num_channels);
|
BitStream stream(current_input_buffer, current_input_size * 8);
|
||||||
|
|
||||||
BitStream stream(current_input_buffer, (packet_idx + 1) * kBitsPerPacket);
|
|
||||||
stream.SetOffset(data->input_buffer_read_offset);
|
stream.SetOffset(data->input_buffer_read_offset);
|
||||||
int frame_len;
|
|
||||||
int frame_len_partial = static_cast<int>(stream.BitsRemaining());
|
|
||||||
if (frame_len_partial >= 15) {
|
|
||||||
frame_len = static_cast<int>(stream.Peek(15));
|
|
||||||
} else {
|
|
||||||
// assert_always();
|
|
||||||
frame_len = xma::kMaxFrameLength + 1;
|
|
||||||
}
|
|
||||||
assert_true(frame_is_split == (frame_len > frame_len_partial));
|
|
||||||
|
|
||||||
// TODO fix bitstream copy
|
// if we had a buffer swap try to skip packets first
|
||||||
std::memset(xma_frame_.data(), 0, xma_frame_.size());
|
if (packets_skip_ > 0) {
|
||||||
|
packet_idx =
|
||||||
auto padding_start = stream.Copy(xma_frame_.data() + 1,
|
GetFramePacketNumber(current_input_buffer, current_input_size,
|
||||||
std::min(frame_len, frame_len_partial));
|
data->input_buffer_read_offset);
|
||||||
assert_true(padding_start < 8);
|
while (packets_skip_ > 0) {
|
||||||
|
packets_skip_--;
|
||||||
if (frame_is_split) {
|
|
||||||
// go to next xma packet of this stream
|
|
||||||
// for (auto skip = xma::GetPacketSkipCount(packet) + 1; skip > 0; skip--)
|
|
||||||
auto skip = xma::GetPacketSkipCount(packet) + 1;
|
|
||||||
do {
|
|
||||||
packet += kBytesPerPacket;
|
|
||||||
packet_idx++;
|
packet_idx++;
|
||||||
if (packet_idx >= current_input_packet_count) {
|
if (packet_idx >= current_input_packet_count) {
|
||||||
packets_skip_ = skip;
|
|
||||||
split_frame_len_ = frame_len;
|
|
||||||
split_frame_len_partial_ = frame_len_partial;
|
|
||||||
SwapInputBuffer(data);
|
SwapInputBuffer(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
skip--;
|
}
|
||||||
/*
|
// invalid frame pointer but needed for us
|
||||||
if (skip == 0) {
|
data->input_buffer_read_offset = packet_idx * kBitsPerPacket;
|
||||||
std::tie(frame_count, frame_last_split) = GetPacketFrameCount(packet);
|
// continue;
|
||||||
if (frame_count == 0) {
|
}
|
||||||
skip = xma::GetPacketSkipCount(packet) + 1;
|
|
||||||
}
|
if (split_frame_len_) {
|
||||||
}
|
// handle a frame that was split over two packages
|
||||||
*/
|
packet_idx =
|
||||||
} while (skip > 0);
|
GetFramePacketNumber(current_input_buffer, current_input_size,
|
||||||
|
data->input_buffer_read_offset);
|
||||||
|
packet = current_input_buffer + packet_idx * kBytesPerPacket;
|
||||||
std::tie(frame_count, frame_last_split) = GetPacketFrameCount(packet);
|
std::tie(frame_count, frame_last_split) = GetPacketFrameCount(packet);
|
||||||
frame_idx = -1;
|
frame_idx = -1;
|
||||||
|
|
||||||
|
@ -459,37 +412,135 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
BitStream(current_input_buffer, (packet_idx + 1) * kBitsPerPacket);
|
BitStream(current_input_buffer, (packet_idx + 1) * kBitsPerPacket);
|
||||||
stream.SetOffset(packet_idx * kBitsPerPacket + 32);
|
stream.SetOffset(packet_idx * kBitsPerPacket + 32);
|
||||||
|
|
||||||
if (frame_len > xma::kMaxFrameLength) {
|
if (split_frame_len_ > xma::kMaxFrameLength) {
|
||||||
// TODO write CopyPeekMethod
|
// TODO write CopyPeekMethod
|
||||||
auto offset = stream.offset_bits();
|
auto offset = stream.offset_bits();
|
||||||
stream.Copy(
|
stream.Copy(
|
||||||
xma_frame_.data() + 1 + ((frame_len_partial + padding_start) / 8),
|
xma_frame_.data() + 1 +
|
||||||
15 - frame_len_partial);
|
((split_frame_len_partial_ + split_frame_padding_start_) / 8),
|
||||||
|
15 - split_frame_len_partial_);
|
||||||
stream.SetOffset(offset);
|
stream.SetOffset(offset);
|
||||||
BitStream slen(xma_frame_.data() + 1, 15 + padding_start);
|
BitStream slen(xma_frame_.data() + 1, 15 + split_frame_padding_start_);
|
||||||
slen.Advance(padding_start);
|
slen.Advance(split_frame_padding_start_);
|
||||||
frame_len = static_cast<int>(slen.Read(15));
|
split_frame_len_ = static_cast<int>(slen.Read(15));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame_count > 0) {
|
if (frame_count > 0) {
|
||||||
assert_true(xma::GetPacketFrameOffset(packet) - 32 ==
|
assert_true(xma::GetPacketFrameOffset(packet) - 32 ==
|
||||||
frame_len - frame_len_partial);
|
split_frame_len_ - split_frame_len_partial_);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto offset = stream.Copy(
|
auto offset = stream.Copy(
|
||||||
xma_frame_.data() + 1 + ((frame_len_partial + padding_start) / 8),
|
xma_frame_.data() + 1 +
|
||||||
frame_len - frame_len_partial);
|
((split_frame_len_partial_ + split_frame_padding_start_) / 8),
|
||||||
assert_true(offset == (padding_start + frame_len_partial) % 8);
|
split_frame_len_ - split_frame_len_partial_);
|
||||||
|
assert_true(offset ==
|
||||||
|
(split_frame_padding_start_ + split_frame_len_partial_) % 8);
|
||||||
|
} else {
|
||||||
|
if (data->input_buffer_read_offset % kBitsPerPacket == 0) {
|
||||||
|
// Invalid offset. Go ahead and set it.
|
||||||
|
auto offset =
|
||||||
|
xma::GetPacketFrameOffset(
|
||||||
|
current_input_buffer +
|
||||||
|
kBytesPerPacket * GetFramePacketNumber(
|
||||||
|
current_input_buffer, current_input_size,
|
||||||
|
data->input_buffer_read_offset)) +
|
||||||
|
data->input_buffer_read_offset;
|
||||||
|
if (offset == -1) {
|
||||||
|
// No more frames.
|
||||||
|
SwapInputBuffer(data);
|
||||||
|
// TODO partial frames? end?
|
||||||
|
XELOGE("XmaContext {}: TODO partial frames? end?", id());
|
||||||
|
assert_always("TODO");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
data->input_buffer_read_offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidFrameOffset(current_input_buffer, current_input_size,
|
||||||
|
data->input_buffer_read_offset)) {
|
||||||
|
XELOGAPU("XmaContext {}: Invalid read offset {}!", id(),
|
||||||
|
data->input_buffer_read_offset);
|
||||||
|
SwapInputBuffer(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where are we in the buffer (in XMA jargon)
|
||||||
|
std::tie(packet_idx, frame_idx) =
|
||||||
|
GetFrameNumber(current_input_buffer, current_input_size,
|
||||||
|
data->input_buffer_read_offset);
|
||||||
|
// TODO handle
|
||||||
|
assert_true(packet_idx >= 0);
|
||||||
|
assert_true(frame_idx >= 0);
|
||||||
|
packet = current_input_buffer + packet_idx * kBytesPerPacket;
|
||||||
|
// frames that belong to this packet
|
||||||
|
std::tie(frame_count, frame_last_split) = GetPacketFrameCount(packet);
|
||||||
|
assert_true(frame_count >= 0); // TODO end
|
||||||
|
|
||||||
|
PrepareDecoder(packet, data->sample_rate, num_channels);
|
||||||
|
|
||||||
|
// Current frame is split to next packet:
|
||||||
|
bool frame_is_split = frame_last_split && (frame_idx >= frame_count - 1);
|
||||||
|
|
||||||
|
stream =
|
||||||
|
BitStream(current_input_buffer, (packet_idx + 1) * kBitsPerPacket);
|
||||||
|
stream.SetOffset(data->input_buffer_read_offset);
|
||||||
|
// int frame_len;
|
||||||
|
// int frame_len_partial
|
||||||
|
split_frame_len_partial_ = static_cast<int>(stream.BitsRemaining());
|
||||||
|
if (split_frame_len_partial_ >= 15) {
|
||||||
|
split_frame_len_ = static_cast<int>(stream.Peek(15));
|
||||||
|
} else {
|
||||||
|
// assert_always();
|
||||||
|
split_frame_len_ = xma::kMaxFrameLength + 1;
|
||||||
|
}
|
||||||
|
assert_true(frame_is_split ==
|
||||||
|
(split_frame_len_ > split_frame_len_partial_));
|
||||||
|
|
||||||
|
// TODO fix bitstream copy
|
||||||
|
std::memset(xma_frame_.data(), 0, xma_frame_.size());
|
||||||
|
|
||||||
|
{
|
||||||
|
auto offset =
|
||||||
|
stream.Copy(xma_frame_.data() + 1,
|
||||||
|
std::min(split_frame_len_, split_frame_len_partial_));
|
||||||
|
assert_true(offset < 8);
|
||||||
|
split_frame_padding_start_ = static_cast<uint8_t>(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame_is_split) {
|
||||||
|
// go to next xma packet of this stream
|
||||||
|
packets_skip_ = xma::GetPacketSkipCount(packet) + 1;
|
||||||
|
while (packets_skip_ > 0) {
|
||||||
|
packets_skip_--;
|
||||||
|
packet += kBytesPerPacket;
|
||||||
|
packet_idx++;
|
||||||
|
if (packet_idx >= current_input_packet_count) {
|
||||||
|
SwapInputBuffer(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO guest might read this:
|
||||||
|
data->input_buffer_read_offset = packet_idx * kBitsPerPacket;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
av_packet_->data = xma_frame_.data();
|
av_packet_->data = xma_frame_.data();
|
||||||
av_packet_->size =
|
av_packet_->size = static_cast<int>(
|
||||||
static_cast<int>(1 + ((padding_start + frame_len) / 8) +
|
1 + ((split_frame_padding_start_ + split_frame_len_) / 8) +
|
||||||
(((padding_start + frame_len) % 8) ? 1 : 0));
|
(((split_frame_padding_start_ + split_frame_len_) % 8) ? 1 : 0));
|
||||||
|
|
||||||
auto padding_end = av_packet_->size * 8 - (8 + padding_start + frame_len);
|
auto padding_end = av_packet_->size * 8 -
|
||||||
|
(8 + split_frame_padding_start_ + split_frame_len_);
|
||||||
assert_true(padding_end < 8);
|
assert_true(padding_end < 8);
|
||||||
xma_frame_[0] = ((padding_start & 7) << 5) | ((padding_end & 7) << 2);
|
xma_frame_[0] =
|
||||||
|
((split_frame_padding_start_ & 7) << 5) | ((padding_end & 7) << 2);
|
||||||
|
|
||||||
|
split_frame_len_ = 0;
|
||||||
|
split_frame_len_partial_ = 0;
|
||||||
|
split_frame_padding_start_ = 0;
|
||||||
|
|
||||||
auto ret = avcodec_send_packet(av_context_, av_packet_);
|
auto ret = avcodec_send_packet(av_context_, av_packet_);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -497,7 +548,6 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
// TODO bail out
|
// TODO bail out
|
||||||
assert_always();
|
assert_always();
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = avcodec_receive_frame(av_context_, av_frame_);
|
ret = avcodec_receive_frame(av_context_, av_frame_);
|
||||||
/*
|
/*
|
||||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||||
|
@ -521,7 +571,7 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
assert_true(av_context_->sample_fmt == AV_SAMPLE_FMT_FLTP);
|
assert_true(av_context_->sample_fmt == AV_SAMPLE_FMT_FLTP);
|
||||||
// assert_true(frame_is_split == (frame_idx == -1));
|
// assert_true(frame_is_split == (frame_idx == -1));
|
||||||
|
|
||||||
// dump_raw(av_frame_, id());
|
// dump_raw(av_frame_, id());
|
||||||
ConvertFrame((const uint8_t**)av_frame_->data, num_channels,
|
ConvertFrame((const uint8_t**)av_frame_->data, num_channels,
|
||||||
kSamplesPerFrame, raw_frame_.data());
|
kSamplesPerFrame, raw_frame_.data());
|
||||||
// decoded_consumed_samples_ += kSamplesPerFrame;
|
// decoded_consumed_samples_ += kSamplesPerFrame;
|
||||||
|
@ -534,36 +584,31 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
|
|
||||||
total_samples += id_ == 0 ? kSamplesPerFrame : 0;
|
total_samples += id_ == 0 ? kSamplesPerFrame : 0;
|
||||||
|
|
||||||
size_t offset = data->input_buffer_read_offset;
|
uint32_t offset = data->input_buffer_read_offset;
|
||||||
// if (offset % (kBytesPerSample * 8) == 0) {
|
// if (offset % (kBytesPerSample * 8) == 0) {
|
||||||
// offset = xma::GetPacketFrameOffset(packet);
|
// offset = xma::GetPacketFrameOffset(packet);
|
||||||
// }
|
//}
|
||||||
offset = GetNextFrame(current_input_buffer, current_input_size, offset);
|
offset = static_cast<uint32_t>(
|
||||||
|
GetNextFrame(current_input_buffer, current_input_size, offset));
|
||||||
// assert_true((offset == 0) ==
|
// assert_true((offset == 0) ==
|
||||||
// (frame_is_split || (frame_idx + 1 >= frame_count)));
|
// (frame_is_split || (frame_idx + 1 >= frame_count)));
|
||||||
if (frame_idx + 1 >= frame_count) {
|
if (frame_idx + 1 >= frame_count) {
|
||||||
// Next packet
|
// Skip to next packet (no split frame)
|
||||||
auto skip = xma::GetPacketSkipCount(packet) + 1;
|
packets_skip_ = xma::GetPacketSkipCount(packet) + 1;
|
||||||
packet += skip * kBytesPerPacket;
|
while (packets_skip_ > 0) {
|
||||||
packet_idx += skip;
|
packets_skip_--;
|
||||||
// Next packet
|
packet_idx++;
|
||||||
if (packet_idx >= current_input_packet_count) {
|
if (packet_idx >= current_input_packet_count) {
|
||||||
// Buffer is fully used
|
SwapInputBuffer(data);
|
||||||
if (data->current_buffer == 0) {
|
return;
|
||||||
data->input_buffer_0_valid = 0;
|
|
||||||
} else {
|
|
||||||
data->input_buffer_1_valid = 0;
|
|
||||||
}
|
}
|
||||||
data->current_buffer ^= 1;
|
|
||||||
data->input_buffer_read_offset = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
// offset = packet_idx * kBitsPerPacket;
|
packet = current_input_buffer + packet_idx * kBytesPerPacket;
|
||||||
offset =
|
offset =
|
||||||
xma::GetPacketFrameOffset(packet) + packet_idx * kBitsPerPacket;
|
xma::GetPacketFrameOffset(packet) + packet_idx * kBitsPerPacket;
|
||||||
}
|
}
|
||||||
if (offset == 0) {
|
if (offset == 0 || frame_idx == -1) {
|
||||||
// Next packet
|
// Next packet but we already skipped to it
|
||||||
if (packet_idx >= current_input_packet_count) {
|
if (packet_idx >= current_input_packet_count) {
|
||||||
// Buffer is fully used
|
// Buffer is fully used
|
||||||
SwapInputBuffer(data);
|
SwapInputBuffer(data);
|
||||||
|
@ -573,10 +618,14 @@ void XmaContext::Decode(XMA_CONTEXT_DATA* data) {
|
||||||
xma::GetPacketFrameOffset(packet) + packet_idx * kBitsPerPacket;
|
xma::GetPacketFrameOffset(packet) + packet_idx * kBitsPerPacket;
|
||||||
}
|
}
|
||||||
// TODO buffer bounds check
|
// TODO buffer bounds check
|
||||||
|
assert_true(data->input_buffer_read_offset < offset);
|
||||||
data->input_buffer_read_offset = offset;
|
data->input_buffer_read_offset = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assert_true((split_frame_len_ != 0) == (data->input_buffer_read_offset ==
|
||||||
|
// 0));
|
||||||
|
|
||||||
// The game will kick us again with a new output buffer later.
|
// The game will kick us again with a new output buffer later.
|
||||||
// It's important that we only invalidate this if we actually wrote to it!!
|
// It's important that we only invalidate this if we actually wrote to it!!
|
||||||
if (output_rb.write_offset() == output_rb.read_offset()) {
|
if (output_rb.write_offset() == output_rb.read_offset()) {
|
||||||
|
@ -606,7 +655,7 @@ size_t XmaContext::GetNextFrame(uint8_t* block, size_t size,
|
||||||
// return false;
|
// return false;
|
||||||
// return next_packet;
|
// return next_packet;
|
||||||
return 0;
|
return 0;
|
||||||
} else if (len == 0x7FFF) {
|
} else if (len >= xma::kMaxFrameLength) {
|
||||||
assert_always("TODO");
|
assert_always("TODO");
|
||||||
// *bit_offset = next_packet;
|
// *bit_offset = next_packet;
|
||||||
// return false;
|
// return false;
|
||||||
|
@ -647,6 +696,11 @@ std::tuple<int, int> XmaContext::GetFrameNumber(uint8_t* block, size_t size,
|
||||||
auto packet_idx = GetFramePacketNumber(block, size, bit_offset);
|
auto packet_idx = GetFramePacketNumber(block, size, bit_offset);
|
||||||
|
|
||||||
if (packet_idx < 0 || (packet_idx + 1) * kBytesPerPacket > size) {
|
if (packet_idx < 0 || (packet_idx + 1) * kBytesPerPacket > size) {
|
||||||
|
assert_always();
|
||||||
|
return {packet_idx, -2};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bit_offset == 0) {
|
||||||
return {packet_idx, -1};
|
return {packet_idx, -1};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,7 @@ class XmaContext {
|
||||||
// bool split_frame_pending_ = false;
|
// bool split_frame_pending_ = false;
|
||||||
uint32_t split_frame_len_ = 0;
|
uint32_t split_frame_len_ = 0;
|
||||||
uint32_t split_frame_len_partial_ = 0;
|
uint32_t split_frame_len_partial_ = 0;
|
||||||
|
uint8_t split_frame_padding_start_ = 0;
|
||||||
// first byte contains bit offset information
|
// first byte contains bit offset information
|
||||||
std::array<uint8_t, 1 + 4096> xma_frame_;
|
std::array<uint8_t, 1 + 4096> xma_frame_;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue