00001 #include "StdAfx.h"
00002 #include "AviFile.h"
00003
00004
00005
00006
00007
00008 CAviFile::CAviFile(IAsyncReader* pAsyncReader, HRESULT& hr)
00009 : CBaseSplitterFile(pAsyncReader, hr)
00010 {
00011 if(FAILED(hr)) return;
00012 hr = Init();
00013 }
00014
00015 template<typename T>
00016 HRESULT CAviFile::Read(T& var, int offset)
00017 {
00018 memset(&var, 0, sizeof(var));
00019 HRESULT hr = Read((BYTE*)&var + offset, sizeof(var) - offset);
00020 return hr;
00021 }
00022
00023 HRESULT CAviFile::Init()
00024 {
00025 Seek(0);
00026 DWORD dw[3];
00027 if(S_OK != Read(dw) || dw[0] != FCC('RIFF') || (dw[2] != FCC('AVI ') && dw[2] != FCC('AVIX')))
00028 return E_FAIL;
00029
00030 Seek(0);
00031 HRESULT hr = Parse(0, GetLength());
00032 if(m_movis.GetCount() == 0)
00033 return E_FAIL;
00034
00035 if(m_avih.dwStreams == 0 && m_strms.GetCount() > 0)
00036 m_avih.dwStreams = m_strms.GetCount();
00037
00038 if(m_avih.dwStreams != m_strms.GetCount())
00039 return E_FAIL;
00040
00041 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00042 {
00043 strm_t* s = m_strms[i];
00044 if(s->strh.fccType != FCC('auds')) continue;
00045 WAVEFORMATEX* wfe = (WAVEFORMATEX*)s->strf.GetData();
00046 if(wfe->wFormatTag == 0x55 && wfe->nBlockAlign == 1152
00047 && s->strh.dwScale == 1 && s->strh.dwRate != wfe->nSamplesPerSec)
00048 {
00049
00050 s->strh.dwScale = 1152;
00051 s->strh.dwRate = wfe->nSamplesPerSec;
00052 }
00053 }
00054
00055 if(FAILED(BuildIndex()))
00056 EmptyIndex();
00057
00058 return S_OK;
00059 }
00060
00061 HRESULT CAviFile::Parse(DWORD parentid, __int64 end)
00062 {
00063 HRESULT hr = S_OK;
00064
00065 CAutoPtr<strm_t> strm;
00066
00067 while(S_OK == hr && GetPos() < end)
00068 {
00069 UINT64 pos = GetPos();
00070
00071 DWORD id = 0, size;
00072 if(S_OK != Read(id) || id == 0)
00073 return E_FAIL;
00074
00075 if(id == FCC('RIFF') || id == FCC('LIST'))
00076 {
00077 if(S_OK != Read(size) || S_OK != Read(id))
00078 return E_FAIL;
00079
00080 size += (size&1) + 8;
00081
00082 TRACE(_T("CAviFile::Parse(..): LIST '%c%c%c%c'\n"),
00083 TCHAR((id>>0)&0xff),
00084 TCHAR((id>>8)&0xff),
00085 TCHAR((id>>16)&0xff),
00086 TCHAR((id>>24)&0xff));
00087
00088 if(id == FCC('movi'))
00089 {
00090 m_movis.AddTail(pos);
00091 }
00092 else
00093 {
00094 hr = Parse(id, pos + size);
00095 }
00096 }
00097 else
00098 {
00099 if(S_OK != Read(size))
00100 return E_FAIL;
00101
00102 TRACE(_T("CAviFile::Parse(..): '%c%c%c%c'\n"),
00103 TCHAR((id>>0)&0xff),
00104 TCHAR((id>>8)&0xff),
00105 TCHAR((id>>16)&0xff),
00106 TCHAR((id>>24)&0xff));
00107
00108 if(parentid == FCC('INFO') && size > 0)
00109 {
00110 switch(id)
00111 {
00112 case FCC('IARL'):
00113 case FCC('IART'):
00114 case FCC('ICMS'):
00115 case FCC('ICMT'):
00116 case FCC('ICOP'):
00117 case FCC('ICRD'):
00118 case FCC('ICRP'):
00119 case FCC('IDIM'):
00120 case FCC('IDPI'):
00121 case FCC('IENG'):
00122 case FCC('IGNR'):
00123 case FCC('IKEY'):
00124 case FCC('ILGT'):
00125 case FCC('IMED'):
00126 case FCC('INAM'):
00127 case FCC('IPLT'):
00128 case FCC('IPRD'):
00129 case FCC('ISBJ'):
00130 case FCC('ISFT'):
00131 case FCC('ISHP'):
00132 case FCC('ISRC'):
00133 case FCC('ISRF'):
00134 case FCC('ITCH'):
00135 {
00136 CStringA str;
00137 if(S_OK != Read((BYTE*)str.GetBufferSetLength(size), size)) return E_FAIL;
00138 m_info[id] = str;
00139 break;
00140 }
00141 }
00142 }
00143
00144 switch(id)
00145 {
00146 case FCC('avih'):
00147 m_avih.fcc = FCC('avih');
00148 m_avih.cb = size;
00149 if(S_OK != Read(m_avih, 8)) return E_FAIL;
00150 break;
00151 case FCC('strh'):
00152 if(!strm) strm.Attach(new strm_t());
00153 strm->strh.fcc = FCC('strh');
00154 strm->strh.cb = size;
00155 if(S_OK != Read(strm->strh, 8)) return E_FAIL;
00156 break;
00157 case FCC('strn'):
00158 if(S_OK != Read((BYTE*)strm->strn.GetBufferSetLength(size), size)) return E_FAIL;
00159 break;
00160 case FCC('strf'):
00161 if(!strm) strm.Attach(new strm_t());
00162 strm->strf.SetSize(size);
00163 if(S_OK != Read(strm->strf.GetData(), size)) return E_FAIL;
00164 break;
00165 case FCC('indx'):
00166 if(!strm) strm.Attach(new strm_t());
00167 ASSERT(strm->indx == NULL);
00168 strm->indx.Attach((AVISUPERINDEX*)new BYTE[size + 8]);
00169 strm->indx->fcc = FCC('indx');
00170 strm->indx->cb = size;
00171 if(S_OK != Read((BYTE*)(AVISUPERINDEX*)strm->indx + 8, size)) return E_FAIL;
00172 ASSERT(strm->indx->wLongsPerEntry == 4 && strm->indx->bIndexType == AVI_INDEX_OF_INDEXES);
00173 break;
00174 case FCC('dmlh'):
00175 if(S_OK != Read(m_dmlh)) return E_FAIL;
00176 break;
00177 case FCC('vprp'):
00178
00179 break;
00180 case FCC('idx1'):
00181 ASSERT(m_idx1 == NULL);
00182 m_idx1.Attach((AVIOLDINDEX*)new BYTE[size + 8]);
00183 m_idx1->fcc = FCC('idx1');
00184 m_idx1->cb = size;
00185 if(S_OK != Read((BYTE*)(AVIOLDINDEX*)m_idx1 + 8, size)) return E_FAIL;
00186 break;
00187 }
00188
00189 size += (size&1) + 8;
00190 }
00191
00192 Seek(pos + size);
00193 }
00194
00195 if(strm) m_strms.Add(strm);
00196
00197 return hr;
00198 }
00199
00200 REFERENCE_TIME CAviFile::GetTotalTime()
00201 {
00202 REFERENCE_TIME t = 0;
00203
00204 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00205 {
00206 strm_t* s = m_strms[i];
00207 REFERENCE_TIME t2 = s->GetRefTime(s->cs.GetCount(), s->totalsize);
00208 t = max(t, t2);
00209 }
00210
00211 if(t == 0) t = 10i64*m_avih.dwMicroSecPerFrame*m_avih.dwTotalFrames;
00212
00213 return(t);
00214 }
00215
00216 HRESULT CAviFile::BuildIndex()
00217 {
00218 EmptyIndex();
00219
00220 int nSuperIndexes = 0;
00221
00222 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00223 {
00224 strm_t* s = m_strms[i];
00225 if(s->indx && s->indx->nEntriesInUse > 0) nSuperIndexes++;
00226 }
00227
00228 if(nSuperIndexes == m_avih.dwStreams)
00229 {
00230 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00231 {
00232 strm_t* s = m_strms[i];
00233
00234 AVISUPERINDEX* idx = (AVISUPERINDEX*)s->indx;
00235
00236 DWORD nEntriesInUse = 0;
00237
00238 for(int j = 0; j < (int)idx->nEntriesInUse; j++)
00239 {
00240 Seek(idx->aIndex[j].qwOffset);
00241
00242 AVISTDINDEX stdidx;
00243 if(S_OK != Read((BYTE*)&stdidx, FIELD_OFFSET(AVISTDINDEX, aIndex)))
00244 {
00245 EmptyIndex();
00246 return E_FAIL;
00247 }
00248
00249 nEntriesInUse += stdidx.nEntriesInUse;
00250 }
00251
00252 s->cs.SetSize(nEntriesInUse);
00253
00254 DWORD frame = 0;
00255 UINT64 size = 0;
00256
00257 for(int j = 0; j < (int)idx->nEntriesInUse; j++)
00258 {
00259 Seek(idx->aIndex[j].qwOffset);
00260
00261 CAutoPtr<AVISTDINDEX> p((AVISTDINDEX*)new BYTE[idx->aIndex[j].dwSize]);
00262 if(!p || S_OK != Read((BYTE*)(AVISTDINDEX*)p, idx->aIndex[j].dwSize))
00263 {
00264 EmptyIndex();
00265 return E_FAIL;
00266 }
00267
00268 for(int k = 0, l = 0; k < (int)p->nEntriesInUse; k++)
00269 {
00270 s->cs[frame].size = size;
00271 s->cs[frame].filepos = p->qwBaseOffset + p->aIndex[k].dwOffset;
00272 s->cs[frame].fKeyFrame = !(p->aIndex[k].dwSize&AVISTDINDEX_DELTAFRAME)
00273 || s->strh.fccType == FCC('auds');
00274 s->cs[frame].fChunkHdr = false;
00275 s->cs[frame].orgsize = p->aIndex[k].dwSize&AVISTDINDEX_SIZEMASK;
00276
00277 if(m_idx1)
00278 {
00279 s->cs[frame].filepos -= 8;
00280 s->cs[frame].fChunkHdr = true;
00281 }
00282
00283 frame++;
00284 size += s->GetChunkSize(p->aIndex[k].dwSize&AVISTDINDEX_SIZEMASK);
00285 }
00286 }
00287
00288 s->totalsize = size;
00289 }
00290 }
00291 else if(AVIOLDINDEX* idx = m_idx1)
00292 {
00293 int len = idx->cb/sizeof(idx->aIndex[0]);
00294
00295 UINT64 offset = m_movis.GetHead() + 8;
00296
00297 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00298 {
00299 strm_t* s = m_strms[i];
00300
00301 int nFrames = 0;
00302
00303 for(int j = 0; j < len; j++)
00304 {
00305 if(TRACKNUM(idx->aIndex[j].dwChunkId) == i)
00306 nFrames++;
00307 }
00308
00309 s->cs.SetSize(nFrames);
00310
00311 DWORD frame = 0;
00312 UINT64 size = 0;
00313
00314 for(int j = 0, k = 0; j < len; j++)
00315 {
00316 DWORD TrackNumber = TRACKNUM(idx->aIndex[j].dwChunkId);
00317
00318 if(TrackNumber == i)
00319 {
00320 if(j == 0 && idx->aIndex[j].dwOffset > offset)
00321 {
00322 DWORD id;
00323 Seek(offset + idx->aIndex[j].dwOffset);
00324 Read(id);
00325 if(id != idx->aIndex[j].dwChunkId)
00326 {
00327 TRACE(_T("WARNING: CAviFile::Init() detected absolute chunk addressing in \'idx1\'"));
00328 offset = 0;
00329 }
00330 }
00331
00332 s->cs[frame].size = size;
00333 s->cs[frame].filepos = offset + idx->aIndex[j].dwOffset;
00334 s->cs[frame].fKeyFrame = !!(idx->aIndex[j].dwFlags&AVIIF_KEYFRAME)
00335 || s->strh.fccType == FCC('auds')
00336 || frame == 0;
00337 s->cs[frame].fChunkHdr = j == len-1 || idx->aIndex[j].dwOffset != idx->aIndex[j+1].dwOffset;
00338 s->cs[frame].orgsize = idx->aIndex[j].dwSize;
00339
00340 frame++;
00341 size += s->GetChunkSize(idx->aIndex[j].dwSize);
00342 }
00343 }
00344
00345 s->totalsize = size;
00346 }
00347 }
00348
00349 m_idx1.Free();
00350 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00351 m_strms[i]->indx.Free();
00352
00353 return S_OK;
00354 }
00355
00356 void CAviFile::EmptyIndex()
00357 {
00358 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00359 {
00360 strm_t* s = m_strms[i];
00361 s->cs.RemoveAll();
00362 s->totalsize = 0;
00363 }
00364 }
00365
00366 bool CAviFile::IsInterleaved(bool fKeepInfo)
00367 {
00368 if(m_strms.GetCount() < 2)
00369 return(true);
00370
00371
00372
00373
00374 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00375 m_strms[i]->cs2.SetSize(m_strms[i]->cs.GetSize());
00376
00377 DWORD* curchunks = new DWORD[m_avih.dwStreams];
00378 UINT64* cursizes = new UINT64[m_avih.dwStreams];
00379
00380 memset(curchunks, 0, sizeof(DWORD)*m_avih.dwStreams);
00381 memset(cursizes, 0, sizeof(UINT64)*m_avih.dwStreams);
00382
00383 int end = 0;
00384
00385 while(1)
00386 {
00387 UINT64 fpmin = _I64_MAX;
00388
00389 DWORD n = -1;
00390 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00391 {
00392 int curchunk = curchunks[i];
00393 CArray<strm_t::chunk>& cs = m_strms[i]->cs;
00394 if(curchunk >= cs.GetSize()) continue;
00395 UINT64 fp = cs[curchunk].filepos;
00396 if(fp < fpmin) {fpmin = fp; n = i;}
00397 }
00398 if(n == -1) break;
00399
00400 strm_t* s = m_strms[n];
00401 DWORD& curchunk = curchunks[n];
00402 UINT64& cursize = cursizes[n];
00403
00404 if(!s->IsRawSubtitleStream())
00405 {
00406 strm_t::chunk2& cs2 = s->cs2[curchunk];
00407 cs2.t = (DWORD)(s->GetRefTime(curchunk, cursize)>>13);
00408
00409 cs2.n = end++;
00410 }
00411
00412 cursize = s->cs[curchunk].size;
00413 curchunk++;
00414 }
00415
00416 memset(curchunks, 0, sizeof(DWORD)*m_avih.dwStreams);
00417
00418 strm_t::chunk2 cs2last = {-1, 0};
00419
00420 bool fInterleaved = true;
00421
00422 while(fInterleaved)
00423 {
00424 strm_t::chunk2 cs2min = {LONG_MAX, LONG_MAX};
00425
00426 int n = -1;
00427 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00428 {
00429 int curchunk = curchunks[i];
00430 if(curchunk >= m_strms[i]->cs2.GetSize()) continue;
00431 strm_t::chunk2& cs2 = m_strms[i]->cs2[curchunk];
00432 if(cs2.t < cs2min.t) {cs2min = cs2; n = i;}
00433 }
00434 if(n == -1) break;
00435
00436 curchunks[n]++;
00437
00438 if(cs2last.t >= 0 && abs(cs2min.n - cs2last.n) >= 1000)
00439 fInterleaved = false;
00440
00441 cs2last = cs2min;
00442 }
00443
00444 delete [] curchunks;
00445 delete [] cursizes;
00446
00447 if(fInterleaved && !fKeepInfo)
00448 {
00449
00450 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00451 m_strms[i]->cs2.SetSize(0);
00452 }
00453
00454 return(fInterleaved);
00455 }
00456
00457 REFERENCE_TIME CAviFile::strm_t::GetRefTime(DWORD frame, UINT64 size)
00458 {
00459 REFERENCE_TIME rt = -1;
00460
00461 if(strh.fccType == FCC('auds'))
00462 {
00463 WAVEFORMATEX* wfe = (WAVEFORMATEX*)strf.GetData();
00464
00465 rt = wfe->nBlockAlign && strh.dwRate
00466 ? ((10000000i64 * size + (wfe->nBlockAlign>>1)) / wfe->nBlockAlign * strh.dwScale + (strh.dwRate>>1)) / strh.dwRate
00467 : 0;
00468 }
00469 else
00470 {
00471 rt = strh.dwRate
00472 ? 10000i64 * ((1000i64 * frame * strh.dwScale + (strh.dwRate>>1)) / strh.dwRate)
00473 : 0;
00474 }
00475
00476 return(rt);
00477 }
00478
00479 int CAviFile::strm_t::GetFrame(REFERENCE_TIME rt)
00480 {
00481 int frame = -1;
00482
00483 rt /= 10000;
00484
00485 if(strh.fccType == FCC('auds'))
00486 {
00487 WAVEFORMATEX* wfe = (WAVEFORMATEX*)strf.GetData();
00488
00489 INT64 size = strh.dwScale
00490 ? ((rt * strh.dwRate + strh.dwScale/2) / strh.dwScale * wfe->nBlockAlign + 1000/2) / 1000
00491 : 0;
00492
00493 for(frame = 0; frame < cs.GetCount(); frame++)
00494 {
00495 if(cs[frame].size > size)
00496 {
00497 frame--;
00498 break;
00499 }
00500 }
00501 }
00502 else
00503 {
00504 frame = strh.dwScale
00505 ? (int)(((rt * strh.dwRate + strh.dwScale/2) / strh.dwScale + 1000/2) / 1000)
00506 : 0;
00507 }
00508
00509 if(frame >= cs.GetCount())
00510 frame = cs.GetCount()-1;
00511
00512 return(frame);
00513 }
00514
00515 int CAviFile::strm_t::GetKeyFrame(REFERENCE_TIME rt)
00516 {
00517 int i = GetFrame(rt);
00518 for(; i > 0; i--) {if(cs[i].fKeyFrame) break;}
00519 return(i);
00520 }
00521
00522 DWORD CAviFile::strm_t::GetChunkSize(DWORD size)
00523 {
00524 if(strh.fccType == FCC('auds'))
00525 {
00526 WORD nBlockAlign = ((WAVEFORMATEX*)strf.GetData())->nBlockAlign;
00527 size = nBlockAlign
00528 ? (size + (nBlockAlign-1)) / nBlockAlign * nBlockAlign
00529 : 0;
00530 }
00531
00532 return(size);
00533 }
00534
00535 bool CAviFile::strm_t::IsRawSubtitleStream()
00536 {
00537 return strh.fccType == FCC('txts') && cs.GetCount() == 1;
00538 }