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