#include "Ap4AvcParser.h"
#include "Ap4Utils.h"
AP4_NalParser::AP4_NalParser() :
m_State(STATE_RESET),
m_ZeroTrail(0)
{
}
void
AP4_NalParser::Unescape(AP4_DataBuffer &data)
{
unsigned int zero_count = 0;
unsigned int bytes_removed = 0;
AP4_UI08* out = data.UseData();
const AP4_UI08* in = data.GetData();
AP4_Size in_size = data.GetDataSize();
for (unsigned int i=0; i<in_size; i++) {
if (zero_count == 2 && in[i] == 3 && i+1 < in_size && in[i+1] <= 3) {
++bytes_removed;
zero_count = 0;
} else {
out[i-bytes_removed] = in[i];
if (in[i] == 0) {
++zero_count;
} else {
zero_count = 0;
}
}
}
data.SetDataSize(in_size-bytes_removed);
}
unsigned int
AP4_NalParser::CountEmulationPreventionBytes(const AP4_UI08* data,
unsigned int data_size,
unsigned int unescaped_size)
{
unsigned int zero_count = 0;
unsigned int bytes_produced = 0;
unsigned int emulation_prevention_bytes = 0;
if (data_size <= 2) {
return 0;
}
for (unsigned int i=0; i<data_size; i++) {
if (zero_count == 2 && data[i] == 3 && i+1 < data_size && data[i+1] <= 3) {
++emulation_prevention_bytes;
zero_count = 0;
} else {
if (++bytes_produced >= unescaped_size) {
break;
}
if (data[i] == 0) {
++zero_count;
} else {
zero_count = 0;
}
}
}
return emulation_prevention_bytes;
}
AP4_Result
AP4_NalParser::Feed(const void* data,
AP4_Size data_size,
AP4_Size& bytes_consumed,
const AP4_DataBuffer*& nalu,
bool is_eos)
{
nalu = NULL;
bytes_consumed = 0;
unsigned int data_offset;
unsigned int payload_start = 0;
unsigned int payload_end = 0;
bool found_nalu = false;
for (data_offset=0; data_offset<data_size && !found_nalu; data_offset++) {
unsigned char byte = ((const unsigned char*)data)[data_offset];
switch (m_State) {
case STATE_RESET:
if (byte == 0) {
m_State = STATE_START_CODE_1;
}
break;
case STATE_START_CODE_1:
if (byte == 0) {
m_State = STATE_START_CODE_2;
} else {
m_State = STATE_RESET;
}
break;
case STATE_START_CODE_2:
if (byte == 0) break;
if (byte == 1) {
m_State = STATE_START_NALU;
} else {
m_State = STATE_RESET;
}
break;
case STATE_START_NALU:
m_Buffer.SetDataSize(0);
m_ZeroTrail = 0;
payload_start = payload_end = data_offset;
m_State = STATE_IN_NALU;
case STATE_IN_NALU:
if (byte == 0) {
++m_ZeroTrail;
++payload_end;
break;
}
if (m_ZeroTrail >= 2) {
if (byte == 1) {
found_nalu = true;
m_State = STATE_START_NALU;
break;
} else {
++payload_end;
}
} else {
++payload_end;
}
m_ZeroTrail = 0;
break;
}
}
if (is_eos && m_State == STATE_IN_NALU && data_offset == data_size) {
found_nalu = true;
m_ZeroTrail = 0;
m_State = STATE_RESET;
}
if (payload_end > payload_start) {
AP4_Size current_payload_size = m_Buffer.GetDataSize();
m_Buffer.SetDataSize(m_Buffer.GetDataSize()+(payload_end-payload_start));
AP4_CopyMemory(((unsigned char *)m_Buffer.UseData())+current_payload_size,
((const unsigned char*)data)+payload_start,
payload_end-payload_start);
}
bytes_consumed = data_offset;
if (found_nalu) {
if (m_ZeroTrail >= 3 && m_Buffer.GetDataSize() >= 3) {
m_Buffer.SetDataSize(m_Buffer.GetDataSize()-3);
} else if (m_ZeroTrail >= 2 && m_Buffer.GetDataSize() >= 2) {
m_Buffer.SetDataSize(m_Buffer.GetDataSize()-2);
}
m_ZeroTrail = 0;
nalu = &m_Buffer;
}
return AP4_SUCCESS;
}
AP4_Result
AP4_NalParser::Reset()
{
m_State = STATE_RESET;
m_ZeroTrail = 0;
m_Buffer.SetDataSize(0);
return AP4_SUCCESS;
}