// File: cpearce[P]OggIndex/src/Validate.cpp
// Function: Theora::Decode(ogg_page*, long&)
virtual ogg_int64_t Decode(ogg_page* page, ogg_int64_t& end_time) {
    end_time = -1;
    if ((ogg_uint32_t)ogg_page_serialno(page) != mSerial) {
      return -1;
    }
    ogg_int64_t start_time = INT_MAX;

    assert(AllBufferedGranuleposInvalid());

    const int shift = mInfo.keyframe_granule_shift;
    ogg_packet op;
    int ret = ogg_stream_pagein(&mStreamState, page);
    assert(ret == 0);
    while ((ret = ogg_stream_packetout(&mStreamState, &op)) != 0) {
      if (ret == -1) {
        cout << "WARNING: Theora decoder out of sync!" << endl;
        continue;
      }
      if (!mReadHeaders) {
        ogg_int32_t ret = th_decode_headerin(&mInfo,
                                             &mComment,
                                             &mSetup,
                                             &op);
        if (ret > 0) {
          mHeaderPacketsRead++;
          mReadHeaders = (mHeaderPacketsRead == 3);
          if (mReadHeaders) {
            mCtx = th_decode_alloc(&mInfo, mSetup);
            assert(mCtx != NULL);
            mFrameDuration = (1000 * mInfo.fps_denominator) / mInfo.fps_numerator;
          }
          continue;
        } else {
          cout << "th_decode_headerin returned " << ret << endl;
          return false;
        }
      }

      PacketInfo p;
      p.granulepos = op.granulepos;
      p.isKeyFrame = th_packet_iskeyframe(&op) != 0;

      mBuffer.push_back(p);

      if (p.granulepos == -1) {
        continue;
      }

      assert(op.granulepos == -1 || op.granulepos == ogg_page_granulepos(page));

      // We've got buffered packets, and the last one has a known
      // granulepos. Use its granulepos to calculate the other granulepos.          
      list<PacketInfo>::reverse_iterator rev_itr = mBuffer.rbegin();
      PacketInfo *prev = &(*rev_itr);
      rev_itr++;
      while (rev_itr != mBuffer.rend()) {
        PacketInfo* p = &(*rev_itr);
        assert(prev->granulepos != -1);
        ogg_int64_t granulepos;
        if (p->isKeyFrame) {
          granulepos =
            (th_granule_frame(mCtx, prev->granulepos) + TheoraVersion(&mInfo,3,2,1) - 1) << shift;
        } else {
          if (prev->isKeyFrame) {
            // The previous frame is a keyframe, so we can't just subtract 1
            // from the "keyframe offset" part of its granulepos, as it
            // doesn't have one! So fake it, take the keyframe offset as the
            // max possible keyframe offset. This means the granulepos claims
            // that it depends on the wrong keyframe, but at least its granule
            // number will be correct, so the times we calculate from this
            // granulepos will also be correct.
            ogg_int64_t frameno = th_granule_frame(mCtx, prev->granulepos);
            ogg_int64_t max_offset = min((frameno - 1), (ogg_int64_t)(1 << shift) - 1);
            ogg_int64_t granule = frameno +
                                  TheoraVersion(&mInfo,3,2,1) - 1 - max_offset;
            assert(granule > 0);
            granulepos = (granule << shift) + max_offset;
          } else {
            assert((prev->granulepos & ((1 << shift) - 1)) > 0);
            granulepos = prev->granulepos - 1;
          }
        }
        p->granulepos = granulepos;
        assert(th_granule_frame(mCtx, p->granulepos) ==
               th_granule_frame(mCtx, prev->granulepos) - 1);
        assert(Time(p->granulepos) < Time(prev->granulepos));
        prev = p;
        rev_itr++;
      }
      assert(mBuffer.front().granulepos != -1);
      assert(mBuffer.size() > 0);

      ogg_int64_t time = NextKeyframeTime();
      if (time != -1) {
        start_time = min(time, start_time);
        end_time = start_time + mFrameDuration;
        ogg_int64_t page_end_time = Time(ogg_page_granulepos(page));
        assert(time <= page_end_time);
      }        
    }
   
    return (start_time != INT_MAX) ? start_time : -1;
  }

// Assembly:
subq $0x128, %rsp            # imm = 0x128
movq %rdi, 0x118(%rsp)
movq %rsi, 0x110(%rsp)
movq %rdx, 0x108(%rsp)
movq 0x118(%rsp), %rax
movq %rax, 0x38(%rsp)
movq 0x108(%rsp), %rax
movq $-0x1, (%rax)
movq 0x110(%rsp), %rdi
callq 0x5350
movq 0x38(%rsp), %rcx
cmpl 0x8(%rcx), %eax
je 0x196a3
movq $-0x1, 0x120(%rsp)
jmp 0x19e7b
movq 0x38(%rsp), %rdi
movq $0x7fffffff, 0x100(%rsp) # imm = 0x7FFFFFFF
callq 0x1a1f0
testb $0x1, %al
jne 0x196bf
jmp 0x196c1
jmp 0x196e0
leaq 0x5813(%rip), %rdi      # 0x1eedb
leaq 0x5278(%rip), %rsi      # 0x1e947
movl $0xa0, %edx
leaq 0x560d(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x38(%rsp), %rdi
movl 0x1fc(%rdi), %eax
movl %eax, 0xfc(%rsp)
addq $0x10, %rdi
movq 0x110(%rsp), %rsi
callq 0x5620
movl %eax, 0xc4(%rsp)
cmpl $0x0, 0xc4(%rsp)
jne 0x19716
jmp 0x19735
leaq 0x38e7(%rip), %rdi      # 0x1d004
leaq 0x5223(%rip), %rsi      # 0x1e947
movl $0xa5, %edx
leaq 0x55b8(%rip), %rcx      # 0x1ece8
callq 0x5280
jmp 0x19737
movq 0x38(%rsp), %rdi
addq $0x10, %rdi
leaq 0xc8(%rsp), %rsi
callq 0x5680
movl %eax, 0xc4(%rsp)
cmpl $0x0, %eax
je 0x19e46
cmpl $-0x1, 0xc4(%rsp)
jne 0x1978b
movq 0xe862(%rip), %rdi      # 0x27fd0
leaq 0x55b1(%rip), %rsi      # 0x1ed26
callq 0x53d0
movq %rax, %rdi
movq 0xe83c(%rip), %rsi      # 0x27fc0
callq 0x5430
jmp 0x19737
movq 0x38(%rsp), %rax
testb $0x1, 0xc(%rax)
jne 0x198c9
movq 0x38(%rsp), %rdx
movq %rdx, %rdi
addq $0x1c0, %rdi            # imm = 0x1C0
movq %rdx, %rsi
addq $0x200, %rsi            # imm = 0x200
addq $0x220, %rdx            # imm = 0x220
leaq 0xc8(%rsp), %rcx
callq 0x56d0
movl %eax, 0xc0(%rsp)
cmpl $0x0, 0xc0(%rsp)
jle 0x19887
movq 0x38(%rsp), %rax
movl 0x230(%rax), %ecx
addl $0x1, %ecx
movl %ecx, 0x230(%rax)
cmpl $0x3, 0x230(%rax)
sete %cl
andb $0x1, %cl
movb %cl, 0xc(%rax)
testb $0x1, 0xc(%rax)
je 0x19882
movq 0x38(%rsp), %rax
movq %rax, %rdi
addq $0x1c0, %rdi            # imm = 0x1C0
movq 0x220(%rax), %rsi
callq 0x54d0
movq %rax, %rcx
movq 0x38(%rsp), %rax
movq %rcx, 0x228(%rax)
cmpq $0x0, 0x228(%rax)
je 0x1983c
jmp 0x1985b
leaq 0x3b88(%rip), %rdi      # 0x1d3cb
leaq 0x50fd(%rip), %rsi      # 0x1e947
movl $0xb5, %edx
leaq 0x5492(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x38(%rsp), %rcx
imull $0x3e8, 0x1e0(%rcx), %eax # imm = 0x3E8
xorl %edx, %edx
divl 0x1dc(%rcx)
movl %eax, %ecx
movq 0x38(%rsp), %rax
movl %ecx, %ecx
movq %rcx, 0x238(%rax)
jmp 0x19737
movq 0xe742(%rip), %rdi      # 0x27fd0
leaq 0x54b6(%rip), %rsi      # 0x1ed4b
callq 0x53d0
movq %rax, %rdi
movl 0xc0(%rsp), %esi
callq 0x57a0
movq %rax, %rdi
movq 0xe70d(%rip), %rsi      # 0x27fc0
callq 0x5430
movq $0x0, 0x120(%rsp)
jmp 0x19e7b
movq 0xe8(%rsp), %rax
movq %rax, 0xb0(%rsp)
leaq 0xc8(%rsp), %rdi
callq 0x5320
movq 0x38(%rsp), %rdi
cmpl $0x0, %eax
setne %al
andb $0x1, %al
movb %al, 0xb8(%rsp)
addq $0x1a8, %rdi            # imm = 0x1A8
leaq 0xb0(%rsp), %rsi
callq 0x1a2a0
cmpq $-0x1, 0xb0(%rsp)
jne 0x1991e
jmp 0x19737
movb $0x1, %al
cmpq $-0x1, 0xe8(%rsp)
movb %al, 0x37(%rsp)
je 0x1995b
movq 0xe8(%rsp), %rax
movq %rax, 0x28(%rsp)
movq 0x110(%rsp), %rdi
callq 0x56e0
movq %rax, %rcx
movq 0x28(%rsp), %rax
cmpq %rcx, %rax
sete %al
movb %al, 0x37(%rsp)
movb 0x37(%rsp), %al
testb $0x1, %al
jne 0x19965
jmp 0x19967
jmp 0x19986
leaq 0x53fa(%rip), %rdi      # 0x1ed68
leaq 0x4fd2(%rip), %rsi      # 0x1e947
movl $0xc9, %edx
leaq 0x5367(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x38(%rsp), %rsi
addq $0x1a8, %rsi            # imm = 0x1A8
leaq 0xa8(%rsp), %rdi
callq 0x1a2e0
leaq 0xa8(%rsp), %rdi
callq 0x1a330
movq %rax, 0xa0(%rsp)
leaq 0x98(%rsp), %rdi
leaq 0xa8(%rsp), %rsi
xorl %edx, %edx
callq 0x1a360
movq 0x38(%rsp), %rsi
addq $0x1a8, %rsi            # imm = 0x1A8
leaq 0x90(%rsp), %rdi
callq 0x1a3d0
leaq 0xa8(%rsp), %rdi
leaq 0x90(%rsp), %rsi
callq 0x1a3a0
testb $0x1, %al
jne 0x19a02
jmp 0x19d29
leaq 0xa8(%rsp), %rdi
callq 0x1a330
movq %rax, 0x88(%rsp)
movq 0xa0(%rsp), %rax
cmpq $-0x1, (%rax)
je 0x19a27
jmp 0x19a46
leaq 0x537c(%rip), %rdi      # 0x1edaa
leaq 0x4f12(%rip), %rsi      # 0x1e947
movl $0xd2, %edx
leaq 0x52a7(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x88(%rsp), %rax
testb $0x1, 0x8(%rax)
je 0x19abd
movq 0x38(%rsp), %rax
movq 0x228(%rax), %rdi
movq 0xa0(%rsp), %rax
movq (%rax), %rsi
callq 0x50b0
movq 0x38(%rsp), %rdi
movq %rax, 0x20(%rsp)
addq $0x1c0, %rdi            # imm = 0x1C0
movl $0x3, %esi
movl $0x2, %edx
movl $0x1, %ecx
callq 0x169b0
movl %eax, %ecx
movq 0x20(%rsp), %rax
movslq %ecx, %rcx
addq %rcx, %rax
subq $0x1, %rax
movl 0xfc(%rsp), %ecx
shlq %cl, %rax
movq %rax, 0x80(%rsp)
jmp 0x19c1e
movq 0xa0(%rsp), %rax
testb $0x1, 0x8(%rax)
je 0x19bba
movq 0x38(%rsp), %rax
movq 0x228(%rax), %rdi
movq 0xa0(%rsp), %rax
movq (%rax), %rsi
callq 0x50b0
movq %rax, 0x78(%rsp)
movq 0x78(%rsp), %rax
subq $0x1, %rax
movq %rax, 0x68(%rsp)
movl 0xfc(%rsp), %ecx
movl $0x1, %eax
shll %cl, %eax
cltq
subq $0x1, %rax
movq %rax, 0x60(%rsp)
leaq 0x68(%rsp), %rdi
leaq 0x60(%rsp), %rsi
callq 0x9090
movq 0x38(%rsp), %rdi
movq (%rax), %rax
movq %rax, 0x70(%rsp)
movq 0x78(%rsp), %rax
movq %rax, 0x18(%rsp)
addq $0x1c0, %rdi            # imm = 0x1C0
movl $0x3, %esi
movl $0x2, %edx
movl $0x1, %ecx
callq 0x169b0
movl %eax, %ecx
movq 0x18(%rsp), %rax
movslq %ecx, %rcx
addq %rcx, %rax
subq $0x1, %rax
subq 0x70(%rsp), %rax
movq %rax, 0x58(%rsp)
cmpq $0x0, 0x58(%rsp)
jle 0x19b7d
jmp 0x19b9c
leaq 0x391b(%rip), %rdi      # 0x1d49f
leaq 0x4dbc(%rip), %rsi      # 0x1e947
movl $0xe4, %edx
leaq 0x5151(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x58(%rsp), %rax
movl 0xfc(%rsp), %ecx
shlq %cl, %rax
addq 0x70(%rsp), %rax
movq %rax, 0x80(%rsp)
jmp 0x19c1c
movq 0xa0(%rsp), %rax
movq (%rax), %rax
movl 0xfc(%rsp), %ecx
movl $0x1, %edx
shll %cl, %edx
movl %edx, %ecx
subl $0x1, %ecx
movslq %ecx, %rcx
andq %rcx, %rax
cmpq $0x0, %rax
jle 0x19be6
jmp 0x19c05
leaq 0x51d4(%rip), %rdi      # 0x1edc1
leaq 0x4d53(%rip), %rsi      # 0x1e947
movl $0xe7, %edx
leaq 0x50e8(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0xa0(%rsp), %rax
movq (%rax), %rax
subq $0x1, %rax
movq %rax, 0x80(%rsp)
jmp 0x19c1e
movq 0x38(%rsp), %rax
movq 0x80(%rsp), %rdx
movq 0x88(%rsp), %rcx
movq %rdx, (%rcx)
movq 0x228(%rax), %rdi
movq 0x88(%rsp), %rax
movq (%rax), %rsi
callq 0x50b0
movq %rax, %rcx
movq 0x38(%rsp), %rax
movq %rcx, 0x10(%rsp)
movq 0x228(%rax), %rdi
movq 0xa0(%rsp), %rax
movq (%rax), %rsi
callq 0x50b0
movq %rax, %rcx
movq 0x10(%rsp), %rax
subq $0x1, %rcx
cmpq %rcx, %rax
jne 0x19c84
jmp 0x19ca3
leaq 0x5162(%rip), %rdi      # 0x1eded
leaq 0x4cb5(%rip), %rsi      # 0x1e947
movl $0xed, %edx
leaq 0x504a(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x38(%rsp), %rdi
movq 0x88(%rsp), %rax
movq (%rax), %rsi
callq 0x1a420
movq 0x38(%rsp), %rdi
movq %rax, 0x8(%rsp)
movq 0xa0(%rsp), %rax
movq (%rax), %rsi
callq 0x1a420
movq %rax, %rcx
movq 0x8(%rsp), %rax
cmpq %rcx, %rax
jge 0x19ce1
jmp 0x19d00
leaq 0x515b(%rip), %rdi      # 0x1ee43
leaq 0x4c58(%rip), %rsi      # 0x1e947
movl $0xee, %edx
leaq 0x4fed(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x88(%rsp), %rax
movq %rax, 0xa0(%rsp)
leaq 0x50(%rsp), %rdi
leaq 0xa8(%rsp), %rsi
xorl %edx, %edx
callq 0x1a360
jmp 0x199cb
movq 0x38(%rsp), %rdi
addq $0x1a8, %rdi            # imm = 0x1A8
callq 0x1a480
cmpq $-0x1, (%rax)
je 0x19d42
jmp 0x19d61
leaq 0x5127(%rip), %rdi      # 0x1ee70
leaq 0x4bf7(%rip), %rsi      # 0x1e947
movl $0xf2, %edx
leaq 0x4f8c(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x38(%rsp), %rdi
addq $0x1a8, %rdi            # imm = 0x1A8
callq 0x1a4b0
cmpq $0x0, %rax
jbe 0x19d7a
jmp 0x19d99
leaq 0x5110(%rip), %rdi      # 0x1ee91
leaq 0x4bbf(%rip), %rsi      # 0x1e947
movl $0xf3, %edx
leaq 0x4f54(%rip), %rcx      # 0x1ece8
callq 0x5280
movq 0x38(%rsp), %rdi
callq 0x1a4e0
movq %rax, 0x48(%rsp)
cmpq $-0x1, 0x48(%rsp)
je 0x19e41
leaq 0x48(%rsp), %rdi
leaq 0x100(%rsp), %rsi
callq 0x9090
movq %rax, %rcx
movq 0x38(%rsp), %rax
movq (%rcx), %rcx
movq %rcx, 0x100(%rsp)
movq 0x100(%rsp), %rcx
addq 0x238(%rax), %rcx
movq 0x108(%rsp), %rax
movq %rcx, (%rax)
movq 0x110(%rsp), %rdi
callq 0x56e0
movq 0x38(%rsp), %rdi
movq %rax, %rsi
callq 0x1a420
movq %rax, 0x40(%rsp)
movq 0x48(%rsp), %rax
cmpq 0x40(%rsp), %rax
jg 0x19e20
jmp 0x19e3f
leaq 0x507d(%rip), %rdi      # 0x1eea4
leaq 0x4b19(%rip), %rsi      # 0x1e947
movl $0xfa, %edx
leaq 0x4eae(%rip), %rcx      # 0x1ece8
callq 0x5280
jmp 0x19e41
jmp 0x19737
cmpq $0x7fffffff, 0x100(%rsp) # imm = 0x7FFFFFFF
je 0x19e62
movq 0x100(%rsp), %rax
movq %rax, (%rsp)
jmp 0x19e6f
movq $-0x1, %rax
movq %rax, (%rsp)
jmp 0x19e6f
movq (%rsp), %rax
movq %rax, 0x120(%rsp)
movq 0x120(%rsp), %rax
addq $0x128, %rsp            # imm = 0x128
retq
nopl (%rax,%rax)