1
0

PlayThread.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. #include "main.h"
  2. #include "../winamp/wa_ipc.h"
  3. #include "VideoThread.h"
  4. #include "AudioSample.h"
  5. #include "api__in_mp4.h"
  6. #include <assert.h>
  7. #include <api/service/waservicefactory.h>
  8. #include "../nu/AudioOutput.h"
  9. #include <strsafe.h>
  10. const DWORD PAUSE_TIMEOUT = 100; // number of milliseconds to sleep for when paused
  11. static bool audio_opened;
  12. static HANDLE events[3];
  13. static bool done;
  14. bool open_mp4(const wchar_t *fn);
  15. static AudioSample *sample = 0;
  16. static DWORD waitTime;
  17. static MP4SampleId nextSampleId;
  18. Nullsoft::Utility::LockGuard play_mp4_guard;
  19. static MP4Duration first_timestamp=0;
  20. class MP4Wait
  21. {
  22. public:
  23. int WaitOrAbort(int time_in_ms)
  24. {
  25. WaitForMultipleObjects(3, events, FALSE, INFINITE); // pauseEvent signal state is opposite of pause state
  26. int ret = WaitForMultipleObjects(2, events, FALSE, time_in_ms);
  27. if (ret == WAIT_TIMEOUT)
  28. return 0;
  29. if (ret == WAIT_OBJECT_0+1)
  30. return 2;
  31. return 1;
  32. }
  33. };
  34. nu::AudioOutput<MP4Wait> audio_output(&mod);
  35. MP4Duration GetClock()
  36. {
  37. if (audio)
  38. {
  39. return audio_output.GetFirstTimestamp() + mod.outMod->GetOutputTime();
  40. }
  41. else if (video)
  42. {
  43. return video_clock.GetOutputTime();
  44. }
  45. else
  46. {
  47. return 0;
  48. }
  49. }
  50. static void OutputSample(AudioSample *sample)
  51. {
  52. if (first)
  53. {
  54. first_timestamp = MP4ConvertFromTrackTimestamp(MP4hFile, audio_track, sample->timestamp, MP4_MSECS_TIME_SCALE);
  55. first = false;
  56. }
  57. if (sample->result == MP4_SUCCESS)
  58. {
  59. if (!audio_opened)
  60. {
  61. audio_opened=true;
  62. if (audio_output.Open(first_timestamp, sample->numChannels, sample->sampleRate, sample->bitsPerSample) == false)
  63. {
  64. PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  65. return ;
  66. }
  67. unsigned __int32 pregap = 0;
  68. unsigned __int32 postgap = 0;
  69. GetGaps(MP4hFile, pregap, postgap);
  70. audio_output.SetDelays(0, pregap, postgap);
  71. mod.SetInfo(audio_bitrate + video_bitrate, sample->sampleRate / 1000, sample->numChannels, 1);
  72. }
  73. int skip = 0;
  74. int sample_size = (sample->bitsPerSample / 8) * sample->numChannels;
  75. int outSamples = MulDiv(sample->outputValid, m_timescale, sample->sampleRate * sample_size);
  76. /* if (!audio_chunk && outSamples > sample->duration)
  77. outSamples = (int)sample->duration; */
  78. if (sample->offset > 0)
  79. {
  80. int cut = (int)min(outSamples, sample->offset);
  81. outSamples -= cut;
  82. skip = cut;
  83. }
  84. size_t outSize = MulDiv(sample_size * sample->sampleRate, outSamples, m_timescale);
  85. if (audio_bitrate != sample->bitrate)
  86. {
  87. audio_bitrate = sample->bitrate;
  88. mod.SetInfo(audio_bitrate + video_bitrate, -1, -1, 1);
  89. }
  90. if (audio_output.Write(sample->output + MulDiv(sample_size * sample->sampleRate, skip, m_timescale), outSize) == 1)
  91. {
  92. return ;
  93. }
  94. if (sample->sampleId == numSamples) // done!
  95. done = true; // TODO: probably don't want to bail out yet if video is playing
  96. }
  97. }
  98. static bool DecodeAudioSample(AudioSample *sample)
  99. {
  100. if (m_needseek != -1)
  101. {
  102. sample->outputValid = 0;
  103. sample->outputCursor = sample->output;
  104. sample->result = MP4_SUCCESS;
  105. sample->sampleRate = m_timescale;
  106. audio->GetOutputProperties(&sample->sampleRate, &sample->numChannels, &sample->bitsPerSample);
  107. if (audio->GetCurrentBitrate(&sample->bitrate) != MP4_SUCCESS || !sample->bitrate)
  108. sample->bitrate = audio_bitrate;
  109. }
  110. else
  111. {
  112. sample->outputValid = sample->outputSize;
  113. sample->outputCursor = 0;
  114. sample->result = audio->DecodeSample(sample->input, sample->inputValid, sample->output, &sample->outputValid);
  115. if (sample->inputValid == 0 && sample->outputValid == 0) {
  116. return false;
  117. }
  118. sample->sampleRate = m_timescale;
  119. audio->GetOutputProperties(&sample->sampleRate, &sample->numChannels, &sample->bitsPerSample);
  120. if (audio->GetCurrentBitrate(&sample->bitrate) != MP4_SUCCESS || !sample->bitrate)
  121. sample->bitrate = audio_bitrate;
  122. OutputSample(sample);
  123. }
  124. return true;
  125. }
  126. static void ReadNextAudioSample()
  127. {
  128. if (nextSampleId > numSamples)
  129. {
  130. return;
  131. }
  132. unsigned __int32 buffer_size = sample->inputSize;
  133. bool sample_read = false;
  134. play_mp4_guard.Lock();
  135. if (audio_chunk)
  136. sample_read = MP4ReadChunk(MP4hFile, audio_track, nextSampleId++, (unsigned __int8 **)&sample->input, &buffer_size, &sample->timestamp, &sample->duration);
  137. else
  138. sample_read = MP4ReadSample(MP4hFile, audio_track, nextSampleId++, (unsigned __int8 **)&sample->input, &buffer_size, &sample->timestamp, &sample->duration, &sample->offset);
  139. play_mp4_guard.Unlock();
  140. if (sample_read)
  141. {
  142. sample->inputValid = buffer_size;
  143. if (audio_chunk)
  144. {
  145. sample->duration = 0;
  146. sample->offset = 0;
  147. }
  148. sample->sampleId = nextSampleId-1;
  149. DecodeAudioSample(sample);
  150. }
  151. }
  152. static bool BuildAudioBuffers()
  153. {
  154. size_t outputFrameSize;
  155. //if (audio->OutputFrameSize(&outputFrameSize) != MP4_SUCCESS || !outputFrameSize)
  156. //{
  157. outputFrameSize = 8192 * 6; // fallback size
  158. //}
  159. u_int32_t maxSize = 0;
  160. if (audio)
  161. {
  162. if (audio_chunk)
  163. maxSize = 65536; // TODO!!!!
  164. else
  165. maxSize = MP4GetTrackMaxSampleSize(MP4hFile, audio_track);
  166. if (!maxSize)
  167. return 0;
  168. sample = new AudioSample(maxSize, outputFrameSize);
  169. if (!sample->OK())
  170. {
  171. delete sample;
  172. return false;
  173. }
  174. }
  175. if (video)
  176. {
  177. maxSize = MP4GetTrackMaxSampleSize(MP4hFile, video_track);
  178. video_sample = new VideoSample(maxSize);
  179. if (!video_sample->OK())
  180. {
  181. delete video_sample;
  182. return false;
  183. }
  184. }
  185. return true;
  186. }
  187. DWORD WINAPI PlayProc(LPVOID lpParameter)
  188. {
  189. // set an event when we start. this keeps Windows from queueing an APC before the thread proc even starts (evil, evil windows)
  190. HANDLE threadCreatedEvent = (HANDLE)lpParameter;
  191. SetEvent(threadCreatedEvent);
  192. video=0;
  193. if (!open_mp4(lastfn))
  194. {
  195. if (WaitForSingleObject(killEvent, 200) != WAIT_OBJECT_0)
  196. PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  197. return 0;
  198. }
  199. audio_output.Init(mod.outMod);
  200. if (videoOutput && video)
  201. {
  202. // TODO: this is really just a placeholder, we should do smarter stuff
  203. // like query the decoder object for a name rather than guess
  204. char set_info[256] = {0};
  205. char *audio_info = MP4PrintAudioInfo(MP4hFile, audio_track);
  206. char *video_info = 0;
  207. if (video_track != MP4_INVALID_TRACK_ID)
  208. video_info = MP4PrintVideoInfo(MP4hFile, video_track);
  209. if (video_info)
  210. {
  211. StringCchPrintfA(set_info, 256, "%s, %s %ux%u", audio_info, video_info, MP4GetTrackVideoWidth(MP4hFile, video_track), MP4GetTrackVideoHeight(MP4hFile, video_track));
  212. videoOutput->extended(VIDUSER_SET_INFOSTRING,(INT_PTR)set_info,0);
  213. MP4Free(video_info);
  214. }
  215. MP4Free(audio_info);
  216. }
  217. if (!BuildAudioBuffers())
  218. {
  219. // TODO: benski> more cleanup work has to be done here!
  220. if (WaitForSingleObject(killEvent, 200) != WAIT_OBJECT_0)
  221. PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  222. return 0;
  223. }
  224. nextSampleId = 1;
  225. nextVideoSampleId = 1;
  226. if (video)
  227. Video_Init();
  228. first = true;
  229. audio_opened = false;
  230. first_timestamp= 0;
  231. events[0]=killEvent;
  232. events[1]=seekEvent;
  233. events[2]=pauseEvent;
  234. waitTime = audio?0:INFINITE;
  235. done = false;
  236. while (!done)
  237. {
  238. int ret = WaitForMultipleObjects(2, events, FALSE, waitTime);
  239. switch (ret)
  240. {
  241. case WAIT_OBJECT_0: // kill event
  242. done = true;
  243. break;
  244. case WAIT_OBJECT_0 + 1: // seek event
  245. {
  246. bool rewind = m_needseek < GetClock();
  247. // TODO: reset pregap?
  248. MP4SampleId new_video_sample = MP4_INVALID_SAMPLE_ID;
  249. if (video)
  250. {
  251. SetEvent(video_start_flushing);
  252. WaitForSingleObject(video_flush_done, INFINITE);
  253. MP4Duration duration = MP4ConvertToTrackDuration(MP4hFile, video_track, m_needseek, MP4_MSECS_TIME_SCALE);
  254. if (duration != MP4_INVALID_DURATION)
  255. {
  256. new_video_sample = MP4GetSampleIdFromTime(MP4hFile, video_track, duration, true, rewind);
  257. if (new_video_sample == MP4_INVALID_SAMPLE_ID)
  258. new_video_sample = MP4GetSampleIdFromTime(MP4hFile, video_track, duration, false); // try again without keyframe seeking
  259. /* TODO: make sure the new seek direction is in the same as the request seek direction.
  260. e.g. make sure a seek FORWARD doesn't go BACKWARD
  261. MP4Timestamp video_timestamp = MP4GetSampleTime(MP4hFile, video_track, seek_video_sample);
  262. int new_time = MP4ConvertFromTrackTimestamp(MP4hFile, video_track, video_timestamp, MP4_MSECS_TIME_SCALE);
  263. if (m_needseek < GetClock())
  264. video_timestamp = MP4GetSampleIdFromTime(MP4hFile, video_track, duration, true); // first closest keyframe prior
  265. */
  266. if (new_video_sample != MP4_INVALID_SAMPLE_ID)
  267. {
  268. int m_old_needseek = m_needseek;
  269. MP4Timestamp video_timestamp = MP4GetSampleTime(MP4hFile, video_track, new_video_sample);
  270. m_needseek = MP4ConvertFromTrackTimestamp(MP4hFile, video_track, video_timestamp, MP4_MSECS_TIME_SCALE);
  271. if (!audio)
  272. {
  273. MP4Timestamp video_timestamp = MP4GetSampleTime(MP4hFile, video_track, new_video_sample);
  274. m_needseek = MP4ConvertFromTrackTimestamp(MP4hFile, video_track, video_timestamp, MP4_MSECS_TIME_SCALE);
  275. video_clock.Seek(m_needseek);
  276. m_needseek = -1;
  277. }
  278. else
  279. {
  280. // TODO check this will just do what is needed
  281. // aim of this is when there is 1 artwork
  282. // frame then we don't lock audio<->video
  283. // as it otherwise prevents audio seeking
  284. if (!m_needseek && m_old_needseek != m_needseek && new_video_sample == 1)
  285. {
  286. m_needseek = m_old_needseek;
  287. }
  288. }
  289. }
  290. }
  291. }
  292. if (audio)
  293. {
  294. MP4Duration duration = MP4ConvertToTrackDuration(MP4hFile, audio_track, m_needseek, MP4_MSECS_TIME_SCALE);
  295. if (duration != MP4_INVALID_DURATION)
  296. {
  297. MP4SampleId newSampleId = audio_chunk?MP4GetChunkIdFromTime(MP4hFile, audio_track, duration):MP4GetSampleIdFromTime(MP4hFile, audio_track, duration);
  298. if (newSampleId != MP4_INVALID_SAMPLE_ID)
  299. {
  300. audio->Flush();
  301. if (video)
  302. {
  303. if (new_video_sample == MP4_INVALID_SAMPLE_ID)
  304. {
  305. SetEvent(video_resume);
  306. }
  307. else
  308. {
  309. nextVideoSampleId = new_video_sample;
  310. SetEvent(video_flush);
  311. }
  312. WaitForSingleObject(video_flush_done, INFINITE);
  313. }
  314. m_needseek = MP4ConvertFromTrackTimestamp(MP4hFile, audio_track, duration, MP4_MILLISECONDS_TIME_SCALE);
  315. ResetEvent(seekEvent);
  316. audio_output.Flush(m_needseek);
  317. m_needseek = -1;
  318. nextSampleId = newSampleId;
  319. continue;
  320. }
  321. }
  322. }
  323. else
  324. {
  325. if (new_video_sample == MP4_INVALID_SAMPLE_ID)
  326. {
  327. SetEvent(video_resume);
  328. }
  329. else
  330. {
  331. nextVideoSampleId = new_video_sample;
  332. SetEvent(video_flush);
  333. }
  334. WaitForSingleObject(video_flush_done, INFINITE);
  335. ResetEvent(seekEvent);
  336. continue;
  337. }
  338. }
  339. break;
  340. case WAIT_TIMEOUT:
  341. ReadNextAudioSample();
  342. break;
  343. }
  344. }
  345. if (WaitForSingleObject(killEvent, 0) == WAIT_TIMEOUT) // if (!killed)
  346. {
  347. // tell audio decoder about end-of-stream and get remaining audio
  348. /* if (audio) {
  349. audio->EndOfStream();
  350. sample->inputValid = 0;
  351. while (DecodeAudioSample(sample)) {
  352. }
  353. }
  354. */
  355. audio_output.Write(0,0);
  356. audio_output.WaitWhilePlaying();
  357. if (WaitForSingleObject(killEvent, 0) == WAIT_TIMEOUT)
  358. PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  359. }
  360. SetEvent(killEvent);
  361. // eat the rest of the APC messages
  362. while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {}
  363. if (video)
  364. Video_Close();
  365. return 0;
  366. }