seektable.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #include "seektable.h"
  2. #include "demuxer.h"
  3. static int GetStreamNumber(uint32_t id)
  4. {
  5. char *stream_data = (char *)(&id);
  6. if (!isxdigit(stream_data[0]) || !isxdigit(stream_data[1]))
  7. return -1;
  8. stream_data[2] = 0;
  9. int stream_number = strtoul(stream_data, 0, 16);
  10. return stream_number;
  11. }
  12. nsavi::SeekTable::SeekTable(int stream_number, bool require_keyframes, const nsavi::HeaderList *header_list)
  13. : header_list(header_list), stream_number(stream_number), require_keyframes(require_keyframes), super_index(0), indices_processed(0), super_index_duration(0)
  14. {
  15. stream = header_list->stream_list[stream_number].stream_header;
  16. const nsavi::INDX *index = header_list->stream_list[stream_number].stream_index;
  17. if (index)
  18. {
  19. if (index->index_type == nsavi::indx_type_master)
  20. {
  21. super_index = (nsavi::AVISUPERINDEX *)index;
  22. }
  23. else if (index->index_type == nsavi::indx_type_chunk)
  24. {
  25. AddIndex(index, 0);
  26. }
  27. }
  28. }
  29. nsavi::SeekTable::~SeekTable()
  30. {
  31. }
  32. const nsavi::SeekEntry *nsavi::SeekTable::GetSeekPoint(int &timestamp_ms, int current_ms, int seek_direction)
  33. {
  34. int last_time = 0;
  35. const nsavi::SeekEntry *last_entry = 0;
  36. // TODO: binary search
  37. for (SeekEntries::iterator itr=seek_entries.begin();itr!=seek_entries.end();itr++)
  38. {
  39. if (itr->first > timestamp_ms)
  40. {
  41. if (seek_direction == SEEK_FORWARD && current_ms >= last_time)
  42. {
  43. itr++;
  44. if (itr != seek_entries.end())
  45. {
  46. last_entry = &itr->second;
  47. last_time = itr->first;
  48. }
  49. else
  50. {
  51. return 0;
  52. }
  53. }
  54. timestamp_ms = last_time;
  55. return last_entry;
  56. }
  57. last_entry = &itr->second;
  58. last_time = itr->first;
  59. }
  60. if (seek_direction == SEEK_FORWARD && current_ms >= last_time)
  61. {
  62. return 0;
  63. }
  64. else
  65. {
  66. timestamp_ms = last_time;
  67. return last_entry;
  68. }
  69. }
  70. void nsavi::SeekTable::AddIndex(const IDX1 *index)
  71. {
  72. // TODO: calculate total bytes at the same time, so we can get a more accurate bitrate
  73. data_processed[index] = true;
  74. uint64_t total_time = 0;
  75. uint64_t stream_rate = (uint64_t)stream->rate;
  76. //if (!require_keyframes)
  77. // seek_entries.reserve(seek_entries.size() + index->index_count);
  78. for (uint32_t i=0;i!=index->index_count;i++)
  79. {
  80. const nsavi::IDX1_INDEX &this_index = index->indices[i];
  81. int this_stream_number = GetStreamNumber(this_index.chunk_id);
  82. if (this_stream_number == stream_number && (!require_keyframes || this_index.flags & nsavi::idx1_flags_keyframe))
  83. {
  84. int timestamp = (int)(total_time * 1000ULL / stream_rate);
  85. SeekEntry &entry = seek_entries[timestamp];
  86. #ifdef SEEK_TABLE_STORE_CHUNK_HEADER
  87. entry.chunk_id = this_index.chunk_id;
  88. entry.chunk_size = this_index.chunk_size;
  89. #endif
  90. entry.file_position = this_index.offset;
  91. entry.stream_time = total_time;
  92. entry.timestamp = timestamp;
  93. entry.absolute = false;
  94. }
  95. if (this_stream_number == stream_number && !(this_index.flags & nsavi::idx1_flags_no_duration))
  96. {
  97. if (stream->sample_size)
  98. {
  99. uint64_t samples = this_index.chunk_size / stream->sample_size;
  100. total_time += stream->scale * samples;
  101. }
  102. else
  103. total_time += stream->scale;
  104. }
  105. }
  106. }
  107. void nsavi::SeekTable::AddIndex(const INDX *index, uint64_t start_time)
  108. {
  109. if (index->index_type == nsavi::indx_type_chunk)
  110. {
  111. const nsavi::AVISTDINDEX *chunk_index = (nsavi::AVISTDINDEX *)index;
  112. //if (!require_keyframes)
  113. // seek_entries.reserve(seek_entries.size() + chunk_index->indx.number_of_entries);
  114. start_time *= stream->scale;
  115. uint64_t stream_rate = (uint64_t)stream->rate;
  116. for (uint32_t i=0;i!=chunk_index->indx.number_of_entries;i++)
  117. {
  118. const INDX_CHUNK_ENTRY &this_index = chunk_index->entries[i];
  119. if (!require_keyframes || !(this_index.size & 0x80000000))
  120. {
  121. int timestamp = (int)(start_time * 1000ULL / stream_rate);
  122. SeekEntry &entry = seek_entries[timestamp];
  123. #ifdef SEEK_TABLE_STORE_CHUNK_HEADER
  124. entry.chunk_id = chunk_index->indx.chunk_id;
  125. entry.chunk_size = this_index.size & ~0x80000000;
  126. #endif
  127. entry.file_position = chunk_index->base_offset + this_index.offset;
  128. entry.stream_time = start_time;
  129. entry.timestamp = timestamp;
  130. entry.absolute = true;
  131. }
  132. if (stream->sample_size)
  133. {
  134. uint64_t samples = this_index.size / stream->sample_size;
  135. start_time += stream->scale * samples;
  136. }
  137. else
  138. start_time += stream->scale;
  139. }
  140. }
  141. }
  142. bool nsavi::SeekTable::GetIndexLocation(int timestamp, uint64_t *position, uint64_t *start_time)
  143. {
  144. // TODO: use timestamp more effectively
  145. if (super_index)
  146. {
  147. for (uint32_t i=indices_processed;i!=super_index->indx.number_of_entries;i++)
  148. {
  149. indices_processed++;
  150. *start_time = super_index_duration;
  151. super_index_duration += super_index->entries[i].duration;
  152. *position = super_index->entries[i].offset;
  153. return true;
  154. }
  155. }
  156. return false;
  157. }