1 
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6 
7 #include <SDL2/SDL.h>
8 #ifndef _POSIX_SOURCE
9 #define __USE_POSIX199309  (1)   // make posix GCC workable
10 #define __USE_XOPEN2K
11 #endif
12 #include <pthread.h>
13 #ifdef _TIMESPEC_DEFINED         // make MinGW workable
14 #include <pthread_time.h>
15 #endif
16 
17 #include "tiny_codec.h"
18 #include "stream_codec.h"
19 
stream_codec_init(stream_codec_p s)20 void stream_codec_init(stream_codec_p s)
21 {
22     s->state = VIDEO_STATE_STOPPED;
23     s->stop = 0;
24     s->update_audio = 1;
25     s->thread = 0;
26     pthread_mutex_init(&s->timer_mutex, NULL);
27     pthread_mutex_init(&s->video_buffer_mutex, NULL);
28     pthread_mutex_init(&s->audio_buffer_mutex, NULL);
29     codec_init(&s->codec, NULL);
30 }
31 
32 
stream_codec_clear(stream_codec_p s)33 void stream_codec_clear(stream_codec_p s)
34 {
35     s->stop = 1;
36     if(s->thread)
37     {
38         pthread_join(s->thread, NULL);
39         s->thread = 0;
40     }
41 
42     pthread_mutex_destroy(&s->timer_mutex);
43     pthread_mutex_destroy(&s->video_buffer_mutex);
44     pthread_mutex_destroy(&s->audio_buffer_mutex);
45 }
46 
47 
stream_codec_check_end(stream_codec_p s)48 int stream_codec_check_end(stream_codec_p s)
49 {
50     if(s->state == VIDEO_STATE_STOPPED)
51     {
52         if(s->thread)
53         {
54             pthread_join(s->thread, NULL);
55             s->thread = 0;
56             return 1;
57         }
58         return 0;
59     }
60     return -1;
61 }
62 
63 
stream_codec_stop(stream_codec_p s,int wait)64 void stream_codec_stop(stream_codec_p s, int wait)
65 {
66     s->stop = 1;
67     if(wait && s->thread)
68     {
69         pthread_join(s->thread, NULL);
70         s->thread = 0;
71     }
72 }
73 
74 
stream_codec_thread_func(void * data)75 static void *stream_codec_thread_func(void *data)
76 {
77     stream_codec_p s = (stream_codec_p)data;
78     if(s)
79     {
80         uint64_t frame = 0;
81         uint64_t ns = 0;
82         struct timespec time_start = { 0 };
83         struct timespec vid_time;
84         int can_continue = 1;
85 
86         clock_gettime(CLOCK_REALTIME, &time_start);
87 
88         while(!s->stop && can_continue)
89         {
90             frame++;
91             can_continue = 0;
92             ns = (frame * s->codec.fps_denum) % s->codec.fps_num;
93             ns = ns * 1000000000 / s->codec.fps_num;
94             vid_time.tv_sec = time_start.tv_sec + frame * s->codec.fps_denum / s->codec.fps_num;
95             vid_time.tv_nsec = time_start.tv_nsec + ns;
96             if(vid_time.tv_nsec >= 1000000000)
97             {
98                 vid_time.tv_nsec -= 1000000000;
99                 vid_time.tv_sec++;
100             }
101 
102             if(s->update_audio && s->codec.audio.decode && (s->codec.packet(&s->codec, &s->codec.audio.pkt) >= 0))
103             {
104                 pthread_mutex_lock(&s->audio_buffer_mutex);
105                 s->codec.audio.decode(&s->codec, &s->codec.audio.pkt);
106                 s->update_audio = 0;
107                 pthread_mutex_unlock(&s->audio_buffer_mutex);
108             }
109 
110             if(s->codec.video.decode && (s->codec.packet(&s->codec, &s->codec.video.pkt) >= 0))
111             {
112                 pthread_mutex_lock(&s->video_buffer_mutex);
113                 s->codec.video.decode(&s->codec, &s->codec.video.pkt);
114                 pthread_mutex_unlock(&s->video_buffer_mutex);
115                 can_continue++;
116             }
117 
118             s->state = VIDEO_STATE_RUNNING;
119             pthread_mutex_timedlock(&s->timer_mutex, &vid_time);
120         }
121         s->state = VIDEO_STATE_QEUED;
122 
123         pthread_mutex_lock(&s->video_buffer_mutex);
124         pthread_mutex_lock(&s->audio_buffer_mutex);
125         codec_clear(&s->codec);
126         pthread_mutex_unlock(&s->audio_buffer_mutex);
127         pthread_mutex_unlock(&s->video_buffer_mutex);
128 
129         SDL_RWclose(s->codec.input);
130         s->codec.input = NULL;
131     }
132     s->state = VIDEO_STATE_STOPPED;
133 
134     return NULL;
135 }
136 
137 
stream_codec_video_lock(stream_codec_p s)138 void stream_codec_video_lock(stream_codec_p s)
139 {
140     pthread_mutex_lock(&s->video_buffer_mutex);
141 }
142 
143 
stream_codec_video_unlock(stream_codec_p s)144 void stream_codec_video_unlock(stream_codec_p s)
145 {
146     pthread_mutex_unlock(&s->video_buffer_mutex);
147 }
148 
149 
stream_codec_audio_lock(stream_codec_p s)150 void stream_codec_audio_lock(stream_codec_p s)
151 {
152     pthread_mutex_lock(&s->audio_buffer_mutex);
153 }
154 
155 
stream_codec_audio_unlock(stream_codec_p s)156 void stream_codec_audio_unlock(stream_codec_p s)
157 {
158     pthread_mutex_unlock(&s->audio_buffer_mutex);
159 }
160 
161 
stream_codec_play(stream_codec_p s)162 int stream_codec_play(stream_codec_p s)
163 {
164     if((s->state == VIDEO_STATE_STOPPED) && s->codec.input)
165     {
166         s->state = VIDEO_STATE_QEUED;
167         s->stop = 0;
168         return 0 != pthread_create(&s->thread, NULL, stream_codec_thread_func, s);
169     }
170     return 0;
171 }