1 /*
2 Copyright (c) 2012 Stephen Baker
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
23 #include "th_movie.h"
24
25 #include "config.h"
26
27 #include "lua_sdl.h"
28 #if (defined(CORSIX_TH_USE_FFMPEG) || defined(CORSIX_TH_USE_LIBAV)) && \
29 defined(CORSIX_TH_USE_SDL_MIXER)
30
31 #include "th_gfx.h"
32 extern "C" {
33 #include <libavcodec/avcodec.h>
34 #include <libavutil/avutil.h>
35 #include <libavutil/mathematics.h>
36 #include <libavutil/opt.h>
37 #include <libswscale/swscale.h>
38 #if (defined(CORSIX_TH_USE_LIBAV) && \
39 LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \
40 (defined(CORSIX_TH_USE_FFMPEG) && \
41 LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100))
42 #include <libavutil/imgutils.h>
43 #endif
44 }
45 #include <SDL_mixer.h>
46
47 #include <chrono>
48 #include <cstring>
49 #include <iostream>
50
51 #if (defined(CORSIX_TH_USE_LIBAV) && \
52 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 7, 0)) || \
53 (defined(CORSIX_TH_USE_FFMPEG) && \
54 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 12, 100))
55 #define av_packet_unref av_free_packet
56 #endif
57
58 #if (defined(CORSIX_TH_USE_LIBAV) && \
59 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1)) || \
60 (defined(CORSIX_TH_USE_FFMPEG) && \
61 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 45, 101))
62 #define av_frame_alloc avcodec_alloc_frame
63 #define av_frame_unref avcodec_get_frame_defaults
64 #define av_frame_free avcodec_free_frame
65 #endif
66
67 #if (defined(CORSIX_TH_USE_LIBAV) && \
68 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 52, 0)) || \
69 (defined(CORSIX_TH_USE_FFMPEG) && \
70 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 63, 100))
avcodec_free_context(AVCodecContext ** ctx)71 void avcodec_free_context(AVCodecContext** ctx) {
72 avcodec_close(*ctx);
73 av_free(*ctx);
74 }
75 #endif
76
77 namespace {
78
th_movie_audio_callback(int iChannel,void * pStream,int iStreamSize,void * pUserData)79 void th_movie_audio_callback(int iChannel, void* pStream, int iStreamSize,
80 void* pUserData) {
81 movie_player* pMovie = (movie_player*)pUserData;
82 pMovie->copy_audio_to_stream((uint8_t*)pStream, iStreamSize);
83 }
84
85 } // namespace
86
movie_picture()87 movie_picture::movie_picture()
88 : buffer(nullptr), pixel_format(AV_PIX_FMT_RGB24), mutex{} {}
89
~movie_picture()90 movie_picture::~movie_picture() { av_freep(&buffer); }
91
allocate(int iWidth,int iHeight)92 void movie_picture::allocate(int iWidth, int iHeight) {
93 width = iWidth;
94 height = iHeight;
95 av_freep(&buffer);
96 #if (defined(CORSIX_TH_USE_LIBAV) && \
97 LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \
98 (defined(CORSIX_TH_USE_FFMPEG) && \
99 LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100))
100 int numBytes = av_image_get_buffer_size(pixel_format, width, height, 1);
101 #else
102 int numBytes = avpicture_get_size(pixel_format, width, height);
103 #endif
104 buffer = static_cast<uint8_t*>(av_mallocz(numBytes));
105 }
106
deallocate()107 void movie_picture::deallocate() { av_freep(&buffer); }
108
movie_picture_buffer()109 movie_picture_buffer::movie_picture_buffer()
110 : aborting(false),
111 allocated(false),
112 picture_count(0),
113 read_index(0),
114 write_index(0),
115 sws_context(nullptr),
116 texture(nullptr),
117 mutex{},
118 cond{} {}
119
~movie_picture_buffer()120 movie_picture_buffer::~movie_picture_buffer() {
121 sws_freeContext(sws_context);
122 if (texture) {
123 SDL_DestroyTexture(texture);
124 texture = nullptr;
125 }
126 }
127
abort()128 void movie_picture_buffer::abort() {
129 aborting = true;
130 std::lock_guard<std::mutex> lock(mutex);
131 cond.notify_all();
132 }
133
reset()134 void movie_picture_buffer::reset() { aborting = false; }
135
allocate(SDL_Renderer * pRenderer,int iWidth,int iHeight)136 void movie_picture_buffer::allocate(SDL_Renderer* pRenderer, int iWidth,
137 int iHeight) {
138 if (texture) {
139 SDL_DestroyTexture(texture);
140 std::cerr << "movie_player overlay should be deallocated before being "
141 "allocated!\n";
142 }
143 texture = SDL_CreateTexture(pRenderer, SDL_PIXELFORMAT_RGB24,
144 SDL_TEXTUREACCESS_STREAMING, iWidth, iHeight);
145 if (texture == nullptr) {
146 std::cerr << "Problem creating overlay: " << SDL_GetError() << "\n";
147 return;
148 }
149 for (int i = 0; i < picture_buffer_size; i++) {
150 picture_queue[i].allocate(iWidth, iHeight);
151 }
152 // Do not change write_index, it's used by the other thread.
153 // read_index is only used in this thread.
154 read_index = write_index;
155
156 std::lock_guard<std::mutex> lock(mutex);
157 picture_count = 0;
158 allocated = true;
159 cond.notify_one();
160 }
161
deallocate()162 void movie_picture_buffer::deallocate() {
163 {
164 std::lock_guard<std::mutex> lock(mutex);
165 allocated = false;
166 }
167
168 for (int i = 0; i < picture_buffer_size; i++) {
169 std::lock_guard<std::mutex> pictureLock(picture_queue[i].mutex);
170 picture_queue[i].deallocate();
171 }
172
173 if (texture) {
174 SDL_DestroyTexture(texture);
175 texture = nullptr;
176 }
177 }
178
advance()179 bool movie_picture_buffer::advance() {
180 if (empty()) {
181 return false;
182 }
183
184 read_index++;
185 if (read_index == picture_buffer_size) {
186 read_index = 0;
187 }
188
189 std::lock_guard<std::mutex> lock(mutex);
190 picture_count--;
191 cond.notify_one();
192
193 return true;
194 }
195
draw(SDL_Renderer * pRenderer,const SDL_Rect & dstrect)196 void movie_picture_buffer::draw(SDL_Renderer* pRenderer,
197 const SDL_Rect& dstrect) {
198 if (!empty()) {
199 auto cur_pic = &(picture_queue[read_index]);
200
201 std::lock_guard<std::mutex> pictureLock(cur_pic->mutex);
202 if (cur_pic->buffer) {
203 SDL_UpdateTexture(texture, nullptr, cur_pic->buffer, cur_pic->width * 3);
204 int iError = SDL_RenderCopy(pRenderer, texture, nullptr, &dstrect);
205 if (iError < 0) {
206 std::cerr << "Error displaying movie frame: " << SDL_GetError() << "\n";
207 }
208 }
209 }
210 }
211
get_next_pts()212 double movie_picture_buffer::get_next_pts() {
213 double nextPts;
214 std::lock_guard<std::mutex> lock(mutex);
215 if (!allocated || picture_count < 2) {
216 nextPts = 0;
217 } else {
218 nextPts = picture_queue[(read_index + 1) % picture_buffer_size].pts;
219 }
220 return nextPts;
221 }
222
empty()223 bool movie_picture_buffer::empty() {
224 std::lock_guard<std::mutex> lock(mutex);
225 return (!allocated || picture_count == 0);
226 }
227
full()228 bool movie_picture_buffer::full() {
229 std::lock_guard<std::mutex> lock(mutex);
230 return unsafe_full();
231 }
232
unsafe_full()233 bool movie_picture_buffer::unsafe_full() {
234 return (!allocated || picture_count == picture_buffer_size);
235 }
236
write(AVFrame * pFrame,double dPts)237 int movie_picture_buffer::write(AVFrame* pFrame, double dPts) {
238 movie_picture* pMoviePicture = nullptr;
239 std::unique_lock<std::mutex> picBufLock(mutex);
240 while (unsafe_full() && !aborting) {
241 cond.wait(picBufLock);
242 }
243 picBufLock.unlock();
244
245 if (aborting) {
246 return -1;
247 }
248
249 pMoviePicture = &picture_queue[write_index];
250 std::unique_lock<std::mutex> pictureLock(pMoviePicture->mutex);
251
252 if (pMoviePicture->buffer) {
253 sws_context = sws_getCachedContext(
254 sws_context, pFrame->width, pFrame->height,
255 (AVPixelFormat)pFrame->format, pMoviePicture->width,
256 pMoviePicture->height, pMoviePicture->pixel_format, SWS_BICUBIC,
257 nullptr, nullptr, nullptr);
258 if (sws_context == nullptr) {
259 std::cerr << "Failed to initialize SwsContext\n";
260 return 1;
261 }
262
263 /* Allocate a new frame and buffer for the destination RGB24 data. */
264 AVFrame* pFrameRGB = av_frame_alloc();
265 #if (defined(CORSIX_TH_USE_LIBAV) && \
266 LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(54, 6, 0)) || \
267 (defined(CORSIX_TH_USE_FFMPEG) && \
268 LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 63, 100))
269 av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize,
270 pMoviePicture->buffer, pMoviePicture->pixel_format,
271 pMoviePicture->width, pMoviePicture->height, 1);
272 #else
273 avpicture_fill((AVPicture*)pFrameRGB, pMoviePicture->buffer,
274 pMoviePicture->pixel_format, pMoviePicture->width,
275 pMoviePicture->height);
276 #endif
277
278 /* Rescale the frame data and convert it to RGB24. */
279 sws_scale(sws_context, pFrame->data, pFrame->linesize, 0, pFrame->height,
280 pFrameRGB->data, pFrameRGB->linesize);
281
282 av_frame_free(&pFrameRGB);
283
284 pMoviePicture->pts = dPts;
285
286 pictureLock.unlock();
287 write_index++;
288 if (write_index == picture_buffer_size) {
289 write_index = 0;
290 }
291 picBufLock.lock();
292 picture_count++;
293 picBufLock.unlock();
294 }
295
296 return 0;
297 }
298
av_packet_queue()299 av_packet_queue::av_packet_queue()
300 : first_packet(nullptr), last_packet(nullptr), count(0), mutex{}, cond{} {}
301
get_count() const302 int av_packet_queue::get_count() const { return count; }
303
push(AVPacket * pPacket)304 void av_packet_queue::push(AVPacket* pPacket) {
305 #if (defined(CORSIX_TH_USE_LIBAV) && \
306 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 8, 0)) || \
307 (defined(CORSIX_TH_USE_FFMPEG) && \
308 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 12, 100))
309 if (av_dup_packet(pPacket) < 0) {
310 throw std::runtime_error("Failed to duplicate AV packet");
311 }
312 #endif
313
314 th_packet_list* pNode = (th_packet_list*)av_malloc(sizeof(th_packet_list));
315 pNode->pkt = *pPacket;
316 pNode->next = nullptr;
317
318 std::lock_guard<std::mutex> lock(mutex);
319
320 if (last_packet == nullptr) {
321 first_packet = pNode;
322 } else {
323 last_packet->next = pNode;
324 }
325 last_packet = pNode;
326 count++;
327
328 cond.notify_one();
329 }
330
pull(bool fBlock)331 AVPacket* av_packet_queue::pull(bool fBlock) {
332 std::unique_lock<std::mutex> lock(mutex);
333
334 th_packet_list* pNode = first_packet;
335 if (pNode == nullptr && fBlock) {
336 cond.wait(lock);
337 pNode = first_packet;
338 }
339
340 AVPacket* pPacket;
341 if (pNode == nullptr) {
342 pPacket = nullptr;
343 } else {
344 first_packet = pNode->next;
345 if (first_packet == nullptr) {
346 last_packet = nullptr;
347 }
348 count--;
349 pPacket = (AVPacket*)av_malloc(sizeof(AVPacket));
350 *pPacket = pNode->pkt;
351 av_free(pNode);
352 }
353
354 return pPacket;
355 }
356
release()357 void av_packet_queue::release() {
358 std::lock_guard<std::mutex> lock(mutex);
359 cond.notify_all();
360 }
361
movie_player()362 movie_player::movie_player()
363 : renderer(nullptr),
364 last_error(),
365 decoding_audio_mutex{},
366 format_context(nullptr),
367 video_codec_context(nullptr),
368 audio_codec_context(nullptr),
369 video_queue(nullptr),
370 audio_queue(nullptr),
371 movie_picture_buffer(new ::movie_picture_buffer()),
372 audio_resample_context(nullptr),
373 audio_buffer_size(0),
374 audio_buffer_max_size(0),
375 audio_packet(nullptr),
376 audio_frame(nullptr),
377 empty_audio_chunk(nullptr),
378 audio_channel(-1),
379 stream_thread{},
380 video_thread{} {
381 #if defined(CORSIX_TH_USE_LIBAV) || \
382 (defined(CORSIX_TH_USE_FFMPEG) && \
383 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100))
384 av_register_all();
385 #endif
386
387 #ifndef CORSIX_TH_MOVIE_USE_SEND_PACKET_API
388 flush_packet = (AVPacket*)av_malloc(sizeof(AVPacket));
389 av_init_packet(flush_packet);
390 flush_packet->data = (uint8_t*)"FLUSH";
391 flush_packet->size = 5;
392 #endif
393
394 audio_chunk_buffer =
395 (uint8_t*)std::calloc(audio_chunk_buffer_capacity, sizeof(uint8_t));
396 }
397
~movie_player()398 movie_player::~movie_player() {
399 unload();
400
401 #ifndef CORSIX_TH_MOVIE_USE_SEND_PACKET_API
402 av_packet_unref(flush_packet);
403 av_free(flush_packet);
404 #endif
405 free(audio_chunk_buffer);
406 delete movie_picture_buffer;
407 }
408
set_renderer(SDL_Renderer * pRenderer)409 void movie_player::set_renderer(SDL_Renderer* pRenderer) {
410 renderer = pRenderer;
411 }
412
movies_enabled() const413 bool movie_player::movies_enabled() const { return true; }
414
load(const char * szFilepath)415 bool movie_player::load(const char* szFilepath) {
416 int iError = 0;
417 AVCodec* m_pVideoCodec;
418 AVCodec* m_pAudioCodec;
419
420 unload(); // Unload any currently loaded video to free memory
421 aborting = false;
422
423 iError = avformat_open_input(&format_context, szFilepath, nullptr, nullptr);
424 if (iError < 0) {
425 av_strerror(iError, error_buffer, movie_error_buffer_capacity);
426 last_error = std::string(error_buffer);
427 return false;
428 }
429
430 iError = avformat_find_stream_info(format_context, nullptr);
431 if (iError < 0) {
432 av_strerror(iError, error_buffer, movie_error_buffer_capacity);
433 last_error = std::string(error_buffer);
434 return false;
435 }
436
437 video_stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_VIDEO,
438 -1, -1, &m_pVideoCodec, 0);
439 if (video_stream_index < 0) {
440 av_strerror(video_stream_index, error_buffer, movie_error_buffer_capacity);
441 last_error = std::string(error_buffer);
442 return false;
443 }
444 video_codec_context = get_codec_context_for_stream(
445 m_pVideoCodec, format_context->streams[video_stream_index]);
446 avcodec_open2(video_codec_context, m_pVideoCodec, nullptr);
447
448 audio_stream_index = av_find_best_stream(format_context, AVMEDIA_TYPE_AUDIO,
449 -1, -1, &m_pAudioCodec, 0);
450 if (audio_stream_index >= 0) {
451 audio_codec_context = get_codec_context_for_stream(
452 m_pAudioCodec, format_context->streams[audio_stream_index]);
453 avcodec_open2(audio_codec_context, m_pAudioCodec, nullptr);
454 }
455
456 return true;
457 }
458
get_codec_context_for_stream(AVCodec * codec,AVStream * stream) const459 AVCodecContext* movie_player::get_codec_context_for_stream(
460 AVCodec* codec, AVStream* stream) const {
461 #if (defined(CORSIX_TH_USE_LIBAV) && \
462 LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \
463 (defined(CORSIX_TH_USE_FFMPEG) && \
464 LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100))
465 AVCodecContext* ctx = avcodec_alloc_context3(codec);
466 avcodec_parameters_to_context(ctx, stream->codecpar);
467 return ctx;
468 #else
469 return stream->codec;
470 #endif
471 }
472
unload()473 void movie_player::unload() {
474 aborting = true;
475
476 if (audio_queue) {
477 audio_queue->release();
478 }
479 if (video_queue) {
480 video_queue->release();
481 }
482 movie_picture_buffer->abort();
483
484 if (stream_thread.joinable()) {
485 stream_thread.join();
486 }
487 if (video_thread.joinable()) {
488 video_thread.join();
489 }
490
491 // wait until after other threads are closed to clear the packet queues
492 // so we don't free something being used.
493 if (audio_queue) {
494 while (audio_queue->get_count() > 0) {
495 AVPacket* p = audio_queue->pull(false);
496 av_packet_unref(p);
497 av_free(p);
498 }
499 delete audio_queue;
500 audio_queue = nullptr;
501 }
502 if (video_queue) {
503 while (video_queue->get_count() > 0) {
504 AVPacket* p = video_queue->pull(false);
505 av_packet_unref(p);
506 av_free(p);
507 }
508 delete video_queue;
509 video_queue = nullptr;
510 }
511 movie_picture_buffer->deallocate();
512 #if (defined(CORSIX_TH_USE_LIBAV) && \
513 LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \
514 (defined(CORSIX_TH_USE_FFMPEG) && \
515 LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100))
516 if (video_codec_context) {
517 avcodec_free_context(&video_codec_context);
518 video_codec_context = nullptr;
519 }
520 #endif
521
522 if (audio_channel >= 0) {
523 Mix_UnregisterAllEffects(audio_channel);
524 Mix_HaltChannel(audio_channel);
525 Mix_FreeChunk(empty_audio_chunk);
526 audio_channel = -1;
527 }
528
529 std::lock_guard<std::mutex> audioLock(decoding_audio_mutex);
530
531 if (audio_buffer_max_size > 0) {
532 av_free(audio_buffer);
533 audio_buffer_max_size = 0;
534 }
535 #if (defined(CORSIX_TH_USE_LIBAV) && \
536 LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 14, 0)) || \
537 (defined(CORSIX_TH_USE_FFMPEG) && \
538 LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 33, 100))
539 if (audio_codec_context) {
540 avcodec_free_context(&audio_codec_context);
541 audio_codec_context = nullptr;
542 }
543 #endif
544 av_frame_free(&audio_frame);
545
546 #ifdef CORSIX_TH_USE_FFMPEG
547 swr_free(&audio_resample_context);
548 #elif defined(CORSIX_TH_USE_LIBAV)
549 // avresample_free doesn't skip nullptr on it's own.
550 if (audio_resample_context != nullptr) {
551 avresample_free(&audio_resample_context);
552 audio_resample_context = nullptr;
553 }
554 #endif
555
556 if (audio_packet) {
557 audio_packet->data = audio_packet_data;
558 audio_packet->size = audio_packet_size;
559 av_packet_unref(audio_packet);
560 av_free(audio_packet);
561 audio_packet = nullptr;
562 audio_packet_data = nullptr;
563 audio_packet_size = 0;
564 }
565
566 if (format_context) {
567 avformat_close_input(&format_context);
568 }
569 }
570
play(int iChannel)571 void movie_player::play(int iChannel) {
572 if (!renderer) {
573 last_error = std::string("Cannot play before setting the renderer");
574 return;
575 }
576
577 video_queue = new av_packet_queue();
578 movie_picture_buffer->reset();
579 movie_picture_buffer->allocate(renderer, video_codec_context->width,
580 video_codec_context->height);
581
582 audio_packet = nullptr;
583 audio_packet_size = 0;
584 audio_packet_data = nullptr;
585
586 audio_buffer_size = 0;
587 audio_buffer_index = 0;
588 audio_buffer_max_size = 0;
589
590 audio_queue = new av_packet_queue();
591 current_sync_pts = 0;
592 current_sync_pts_system_time = SDL_GetTicks();
593
594 if (audio_stream_index >= 0) {
595 Mix_QuerySpec(&mixer_frequency, nullptr, &mixer_channels);
596 #ifdef CORSIX_TH_USE_FFMPEG
597 audio_resample_context = swr_alloc_set_opts(
598 audio_resample_context,
599 mixer_channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO,
600 AV_SAMPLE_FMT_S16, mixer_frequency, audio_codec_context->channel_layout,
601 audio_codec_context->sample_fmt, audio_codec_context->sample_rate, 0,
602 nullptr);
603 swr_init(audio_resample_context);
604 #elif defined(CORSIX_TH_USE_LIBAV)
605 audio_resample_context = avresample_alloc_context();
606 av_opt_set_int(audio_resample_context, "in_channel_layout",
607 audio_codec_context->channel_layout, 0);
608 av_opt_set_int(
609 audio_resample_context, "out_channel_layout",
610 mixer_channels == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO, 0);
611 av_opt_set_int(audio_resample_context, "in_sample_rate",
612 audio_codec_context->sample_rate, 0);
613 av_opt_set_int(audio_resample_context, "out_sample_rate", mixer_frequency,
614 0);
615 av_opt_set_int(audio_resample_context, "in_sample_fmt",
616 audio_codec_context->sample_fmt, 0);
617 av_opt_set_int(audio_resample_context, "out_sample_fmt", AV_SAMPLE_FMT_S16,
618 0);
619 avresample_open(audio_resample_context);
620 #endif
621 empty_audio_chunk =
622 Mix_QuickLoad_RAW(audio_chunk_buffer, audio_chunk_buffer_capacity);
623
624 audio_channel = Mix_PlayChannel(iChannel, empty_audio_chunk, -1);
625 if (audio_channel < 0) {
626 audio_channel = -1;
627 last_error = std::string(Mix_GetError());
628 Mix_FreeChunk(empty_audio_chunk);
629 } else {
630 Mix_RegisterEffect(audio_channel, th_movie_audio_callback, nullptr, this);
631 }
632 }
633
634 stream_thread = std::thread(&movie_player::read_streams, this);
635 video_thread = std::thread(&movie_player::run_video, this);
636 }
637
stop()638 void movie_player::stop() { aborting = true; }
639
get_native_height() const640 int movie_player::get_native_height() const {
641 int iHeight = 0;
642
643 if (video_codec_context) {
644 iHeight = video_codec_context->height;
645 }
646 return iHeight;
647 }
648
get_native_width() const649 int movie_player::get_native_width() const {
650 int iWidth = 0;
651
652 if (video_codec_context) {
653 iWidth = video_codec_context->width;
654 }
655 return iWidth;
656 }
657
has_audio_track() const658 bool movie_player::has_audio_track() const { return (audio_stream_index >= 0); }
659
get_last_error() const660 const char* movie_player::get_last_error() const { return last_error.c_str(); }
661
clear_last_error()662 void movie_player::clear_last_error() { last_error.clear(); }
663
refresh(const SDL_Rect & destination_rect)664 void movie_player::refresh(const SDL_Rect& destination_rect) {
665 SDL_Rect dest_rect;
666
667 dest_rect = SDL_Rect{destination_rect.x, destination_rect.y,
668 destination_rect.w, destination_rect.h};
669
670 if (!movie_picture_buffer->empty()) {
671 double dCurTime = SDL_GetTicks() - current_sync_pts_system_time +
672 current_sync_pts * 1000.0;
673 double dNextPts = movie_picture_buffer->get_next_pts();
674
675 if (dNextPts > 0 && dNextPts * 1000.0 <= dCurTime) {
676 movie_picture_buffer->advance();
677 }
678
679 movie_picture_buffer->draw(renderer, dest_rect);
680 }
681 }
682
allocate_picture_buffer()683 void movie_player::allocate_picture_buffer() {
684 if (!video_codec_context) {
685 return;
686 }
687 movie_picture_buffer->allocate(renderer, get_native_width(),
688 get_native_height());
689 }
690
deallocate_picture_buffer()691 void movie_player::deallocate_picture_buffer() {
692 movie_picture_buffer->deallocate();
693 }
694
read_streams()695 void movie_player::read_streams() {
696 AVPacket packet;
697 int iError;
698
699 while (!aborting) {
700 iError = av_read_frame(format_context, &packet);
701 if (iError < 0) {
702 if (iError == AVERROR_EOF || format_context->pb->error ||
703 format_context->pb->eof_reached) {
704 break;
705 }
706 } else {
707 if (packet.stream_index == video_stream_index) {
708 video_queue->push(&packet);
709 } else if (packet.stream_index == audio_stream_index) {
710 audio_queue->push(&packet);
711 } else {
712 av_packet_unref(&packet);
713 }
714 }
715 }
716
717 while (!aborting) {
718 if (video_queue->get_count() == 0 && audio_queue->get_count() == 0 &&
719 movie_picture_buffer->get_next_pts() == 0) {
720 break;
721 }
722 std::this_thread::sleep_for(std::chrono::milliseconds(10));
723 }
724
725 SDL_Event endEvent;
726 endEvent.type = SDL_USEREVENT_MOVIE_OVER;
727 SDL_PushEvent(&endEvent);
728 aborting = true;
729 }
730
run_video()731 void movie_player::run_video() {
732 AVFrame* pFrame = av_frame_alloc();
733 double dClockPts;
734 int iError;
735
736 while (!aborting) {
737 av_frame_unref(pFrame);
738
739 #ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API
740 iError = get_frame(video_stream_index, pFrame);
741
742 if (iError == AVERROR_EOF) {
743 break;
744 } else if (iError < 0) {
745 std::cerr << "Unexpected error " << iError
746 << " while decoding video packet" << std::endl;
747 break;
748 }
749 #else
750 iError = get_video_frame(pFrame);
751 if (iError < 0) {
752 break;
753 } else if (iError == 0) {
754 continue;
755 }
756 #endif
757
758 dClockPts = get_presentation_time_for_frame(pFrame, video_stream_index);
759 iError = movie_picture_buffer->write(pFrame, dClockPts);
760
761 if (iError < 0) {
762 break;
763 }
764 }
765
766 avcodec_flush_buffers(video_codec_context);
767 av_frame_free(&pFrame);
768 }
769
get_presentation_time_for_frame(AVFrame * frame,int streamIndex) const770 double movie_player::get_presentation_time_for_frame(AVFrame* frame,
771 int streamIndex) const {
772 int64_t pts;
773 #ifdef CORSIX_TH_USE_LIBAV
774 pts = frame->pts;
775 if (pts == AV_NOPTS_VALUE) {
776 pts = frame->pkt_dts;
777 }
778 #else
779 #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 18, 100)
780 pts = *(int64_t*)av_opt_ptr(avcodec_get_frame_class(), frame,
781 "best_effort_timestamp");
782 #elif LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 18, 100)
783 pts = av_frame_get_best_effort_timestamp(frame);
784 #else
785 pts = frame->best_effort_timestamp;
786 #endif // LIBAVCODEC_VERSION_INT
787 #endif // CORSIX_T_USE_LIBAV
788
789 if (pts == AV_NOPTS_VALUE) {
790 pts = 0;
791 }
792
793 return pts * av_q2d(format_context->streams[streamIndex]->time_base);
794 }
795
796 #ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API
get_frame(int stream,AVFrame * pFrame)797 int movie_player::get_frame(int stream, AVFrame* pFrame) {
798 int iError = AVERROR(EAGAIN);
799 AVCodecContext* ctx;
800 av_packet_queue* pq;
801
802 if (stream == video_stream_index) {
803 ctx = video_codec_context;
804 pq = video_queue;
805 } else if (stream == audio_stream_index) {
806 ctx = audio_codec_context;
807 pq = audio_queue;
808 } else {
809 throw std::invalid_argument("Invalid value provided for stream");
810 }
811
812 while (iError == AVERROR(EAGAIN)) {
813 iError = avcodec_receive_frame(ctx, pFrame);
814
815 if (iError == AVERROR(EAGAIN)) {
816 AVPacket* pkt = pq->pull(true);
817 int res = avcodec_send_packet(ctx, pkt);
818 if (pkt != nullptr) {
819 av_packet_unref(pkt);
820 av_free(pkt);
821 }
822
823 if (res == AVERROR(EAGAIN)) {
824 throw std::runtime_error(
825 "avcodec_receive_frame and avcodec_send_packet should "
826 "not return EAGAIN at the same time");
827 }
828 }
829 }
830
831 return iError;
832 }
833
834 #else
get_video_frame(AVFrame * pFrame)835 int movie_player::get_video_frame(AVFrame* pFrame) {
836 int iGotPicture = 0;
837 int iError;
838
839 AVPacket* pPacket = video_queue->pull(true);
840 if (pPacket == nullptr) {
841 return -1;
842 }
843
844 if (pPacket->data == flush_packet->data) {
845 // TODO: Flush
846
847 return 0;
848 }
849
850 iError =
851 avcodec_decode_video2(video_codec_context, pFrame, &iGotPicture, pPacket);
852 av_packet_unref(pPacket);
853 av_free(pPacket);
854
855 if (iError < 0) {
856 return 0;
857 }
858
859 if (iGotPicture) {
860 iError = 1;
861 return iError;
862 }
863
864 return 0;
865 }
866 #endif
867
copy_audio_to_stream(uint8_t * pbStream,int iStreamSize)868 void movie_player::copy_audio_to_stream(uint8_t* pbStream, int iStreamSize) {
869 std::lock_guard<std::mutex> audioLock(decoding_audio_mutex);
870
871 bool fFirst = true;
872 while (iStreamSize > 0 && !aborting) {
873 if (audio_buffer_index >= audio_buffer_size) {
874 int iAudioSize = decode_audio_frame(fFirst);
875 fFirst = false;
876
877 if (iAudioSize <= 0) {
878 std::memset(audio_buffer, 0, audio_buffer_size);
879 } else {
880 audio_buffer_size = iAudioSize;
881 }
882 audio_buffer_index = 0;
883 }
884
885 int iCopyLength = audio_buffer_size - audio_buffer_index;
886 if (iCopyLength > iStreamSize) {
887 iCopyLength = iStreamSize;
888 }
889 std::memcpy(pbStream, (uint8_t*)audio_buffer + audio_buffer_index,
890 iCopyLength);
891 iStreamSize -= iCopyLength;
892 pbStream += iCopyLength;
893 audio_buffer_index += iCopyLength;
894 }
895 }
896
decode_audio_frame(bool fFirst)897 int movie_player::decode_audio_frame(bool fFirst) {
898 #ifdef CORSIX_TH_MOVIE_USE_SEND_PACKET_API
899 if (!audio_frame) {
900 audio_frame = av_frame_alloc();
901 } else {
902 av_frame_unref(audio_frame);
903 }
904
905 int iError = get_frame(audio_stream_index, audio_frame);
906
907 if (iError == AVERROR_EOF) {
908 return 0;
909 } else if (iError < 0) {
910 std::cerr << "Unexpected error " << iError << " while decoding audio packet"
911 << std::endl;
912 return 0;
913 }
914
915 double dClockPts =
916 get_presentation_time_for_frame(audio_frame, audio_stream_index);
917 current_sync_pts = dClockPts;
918 current_sync_pts_system_time = SDL_GetTicks();
919 #else
920 int iGotFrame = 0;
921 bool fNewPacket = false;
922 bool fFlushComplete = false;
923
924 while (!iGotFrame && !aborting) {
925 if (!audio_packet || audio_packet->size == 0) {
926 if (audio_packet) {
927 audio_packet->data = audio_packet_data;
928 audio_packet->size = audio_packet_size;
929 av_packet_unref(audio_packet);
930 av_free(audio_packet);
931 audio_packet = nullptr;
932 }
933 audio_packet = audio_queue->pull(true);
934 if (aborting) {
935 break;
936 }
937
938 audio_packet_data = audio_packet->data;
939 audio_packet_size = audio_packet->size;
940
941 if (audio_packet == nullptr) {
942 return -1;
943 }
944 fNewPacket = true;
945
946 if (audio_packet->data == flush_packet->data) {
947 avcodec_flush_buffers(audio_codec_context);
948 fFlushComplete = false;
949 }
950 }
951
952 if (fFirst) {
953 int64_t iStreamPts = audio_packet->pts;
954 if (iStreamPts != AV_NOPTS_VALUE) {
955 // There is a time_base in audio_codec_context too, but that one
956 // is wrong.
957 double dClockPts =
958 iStreamPts *
959 av_q2d(format_context->streams[audio_stream_index]->time_base);
960 current_sync_pts = dClockPts;
961 current_sync_pts_system_time = SDL_GetTicks();
962 }
963 fFirst = false;
964 }
965
966 while (audio_packet->size > 0 || (!audio_packet->data && fNewPacket)) {
967 if (!audio_frame) {
968 audio_frame = av_frame_alloc();
969 } else {
970 av_frame_unref(audio_frame);
971 }
972
973 if (fFlushComplete) {
974 break;
975 }
976
977 fNewPacket = false;
978
979 int iBytesConsumed = avcodec_decode_audio4(
980 audio_codec_context, audio_frame, &iGotFrame, audio_packet);
981
982 if (iBytesConsumed < 0) {
983 audio_packet->size = 0;
984 break;
985 }
986 audio_packet->data += iBytesConsumed;
987 audio_packet->size -= iBytesConsumed;
988
989 if (!iGotFrame) {
990 if (audio_packet->data &&
991 (audio_codec_context->codec->capabilities & CODEC_CAP_DELAY)) {
992 fFlushComplete = true;
993 }
994 }
995 }
996 }
997 #endif
998 // over-estimate output samples
999 int iOutSamples =
1000 (int)av_rescale_rnd(audio_frame->nb_samples, mixer_frequency,
1001 audio_codec_context->sample_rate, AV_ROUND_UP);
1002 int iSampleSize =
1003 av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * iOutSamples * mixer_channels;
1004
1005 if (iSampleSize > audio_buffer_max_size) {
1006 if (audio_buffer_max_size > 0) {
1007 av_free(audio_buffer);
1008 }
1009 audio_buffer = (uint8_t*)av_malloc(iSampleSize);
1010 audio_buffer_max_size = iSampleSize;
1011 }
1012
1013 #ifdef CORSIX_TH_USE_FFMPEG
1014 swr_convert(audio_resample_context, &audio_buffer, iOutSamples,
1015 (const uint8_t**)&audio_frame->data[0], audio_frame->nb_samples);
1016 #elif defined(CORSIX_TH_USE_LIBAV)
1017 avresample_convert(audio_resample_context, &audio_buffer, 0, iOutSamples,
1018 (uint8_t**)&audio_frame->data[0], 0,
1019 audio_frame->nb_samples);
1020 #endif
1021 return iSampleSize;
1022 }
1023 #else // CORSIX_TH_USE_FFMPEG || CORSIX_TH_USE_LIBAV
movie_player()1024 movie_player::movie_player() {}
~movie_player()1025 movie_player::~movie_player() {}
set_renderer(SDL_Renderer * renderer)1026 void movie_player::set_renderer(SDL_Renderer* renderer) {}
movies_enabled() const1027 bool movie_player::movies_enabled() const { return false; }
load(const char * file_path)1028 bool movie_player::load(const char* file_path) { return true; }
unload()1029 void movie_player::unload() {}
play(int iChannel)1030 void movie_player::play(int iChannel) {
1031 SDL_Event endEvent;
1032 endEvent.type = SDL_USEREVENT_MOVIE_OVER;
1033 SDL_PushEvent(&endEvent);
1034 }
stop()1035 void movie_player::stop() {}
get_native_height() const1036 int movie_player::get_native_height() const { return 0; }
get_native_width() const1037 int movie_player::get_native_width() const { return 0; }
has_audio_track() const1038 bool movie_player::has_audio_track() const { return false; }
get_last_error() const1039 const char* movie_player::get_last_error() const { return nullptr; }
clear_last_error()1040 void movie_player::clear_last_error() {}
refresh(const SDL_Rect & destination_rect)1041 void movie_player::refresh(const SDL_Rect& destination_rect) {}
allocate_picture_buffer()1042 void movie_player::allocate_picture_buffer() {}
deallocate_picture_buffer()1043 void movie_player::deallocate_picture_buffer() {}
read_streams()1044 void movie_player::read_streams() {}
run_video()1045 void movie_player::run_video() {}
copy_audio_to_stream(uint8_t * stream,int length)1046 void movie_player::copy_audio_to_stream(uint8_t* stream, int length) {}
1047 #endif // CORSIX_TH_USE_FFMPEG
1048