1
1
#include "decoder.h"
2
2
3
3
#include <libavformat/avformat.h>
4
+ #include <libavutil/time.h>
4
5
#include <SDL2/SDL_events.h>
5
6
#include <SDL2/SDL_mutex.h>
6
7
#include <SDL2/SDL_thread.h>
14
15
15
16
#define BUFSIZE 0x10000
16
17
18
+ static AVRational us = {1 , 1000000 };
19
+
20
+ static inline uint64_t from_be (uint8_t * b , int size )
21
+ {
22
+ uint64_t x = 0 ;
23
+ int i ;
24
+
25
+ for (i = 0 ; i < size ; i += 1 ) {
26
+ x <<= 8 ;
27
+ x |= b [i ];
28
+ }
29
+
30
+ return x ;
31
+ }
32
+
33
+ #define HEADER_SIZE 16
34
+
17
35
static int read_packet (void * opaque , uint8_t * buf , int buf_size ) {
18
36
struct decoder * decoder = opaque ;
19
- return net_recv (decoder -> video_socket , buf , buf_size );
37
+ uint8_t header [HEADER_SIZE ];
38
+ int remaining ;
39
+ int ret ;
40
+
41
+ remaining = decoder -> remaining ;
42
+ if (remaining == 0 ) {
43
+ ret = net_recv (decoder -> video_socket , header , HEADER_SIZE );
44
+ if (ret <= 0 )
45
+ return ret ;
46
+
47
+ decoder -> pts = from_be (header , 8 );
48
+ remaining = from_be (header + 12 , 4 );
49
+ }
50
+
51
+ if (buf_size > remaining )
52
+ buf_size = remaining ;
53
+
54
+ ret = net_recv (decoder -> video_socket , buf , buf_size );
55
+ if (ret <= 0 )
56
+ return ret ;
57
+
58
+ remaining -= ret ;
59
+ decoder -> remaining = remaining ;
60
+
61
+ return ret ;
20
62
}
21
63
22
64
// set the decoded frame as ready for rendering, and notify
@@ -40,6 +82,7 @@ static void notify_stopped(void) {
40
82
41
83
static int run_decoder (void * data ) {
42
84
struct decoder * decoder = data ;
85
+ int ret ;
43
86
44
87
AVCodec * codec = avcodec_find_decoder (AV_CODEC_ID_H264 );
45
88
if (!codec ) {
@@ -86,16 +129,55 @@ static int run_decoder(void *data) {
86
129
goto run_finally_free_avio_ctx ;
87
130
}
88
131
132
+ AVStream * outstream = NULL ;
133
+ AVFormatContext * output_ctx = NULL ;
134
+ if (decoder -> out_filename ) {
135
+ avformat_alloc_output_context2 (& output_ctx , NULL , NULL , decoder -> out_filename );
136
+ if (!output_ctx ) {
137
+ LOGE ("Could not allocate output format context" );
138
+ goto run_finally_free_avio_ctx ;
139
+ } else {
140
+ outstream = avformat_new_stream (output_ctx , codec );
141
+ if (!outstream ) {
142
+ LOGE ("Could not allocate output stream" );
143
+ goto run_finally_free_output_ctx ;
144
+ }
145
+ outstream -> codec = avcodec_alloc_context3 (codec );
146
+ outstream -> codec -> pix_fmt = AV_PIX_FMT_YUV420P ;
147
+ outstream -> codec -> width = decoder -> frame_size .width ;
148
+ outstream -> codec -> height = decoder -> frame_size .height ;
149
+ outstream -> time_base = (AVRational ) {1 , 60 };
150
+ outstream -> codec -> flags |= AV_CODEC_FLAG_GLOBAL_HEADER ;
151
+ ret = avio_open (& output_ctx -> pb , decoder -> out_filename , AVIO_FLAG_WRITE );
152
+ if (ret < 0 ) {
153
+ LOGE ("Failed to open output file" );
154
+ goto run_finally_free_output_ctx ;
155
+ }
156
+ ret = avformat_write_header (output_ctx , NULL );
157
+ if (ret < 0 ) {
158
+ LOGE ("Error writing output header" );
159
+ avio_closep (& output_ctx -> pb );
160
+ goto run_finally_free_output_ctx ;
161
+ }
162
+ }
163
+ }
164
+
89
165
AVPacket packet ;
90
166
av_init_packet (& packet );
91
167
packet .data = NULL ;
92
168
packet .size = 0 ;
93
169
94
170
while (!av_read_frame (format_ctx , & packet )) {
171
+
172
+ if (output_ctx ) {
173
+ packet .pts = decoder -> pts ;
174
+ av_packet_rescale_ts (& packet , us , outstream -> time_base );
175
+ ret = av_write_frame (output_ctx , & packet );
176
+ }
177
+
95
178
// the new decoding/encoding API has been introduced by:
96
179
// <http://git.videolan.org/?p=ffmpeg.git;a=commitdiff;h=7fc329e2dd6226dfecaa4a1d7adf353bf2773726>
97
180
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT (57 , 37 , 0 )
98
- int ret ;
99
181
if ((ret = avcodec_send_packet (codec_ctx , & packet )) < 0 ) {
100
182
LOGE ("Could not send video packet: %d" , ret );
101
183
goto run_quit ;
@@ -125,6 +207,7 @@ static int run_decoder(void *data) {
125
207
packet .data += len ;
126
208
}
127
209
#endif
210
+
128
211
av_packet_unref (& packet );
129
212
130
213
if (avio_ctx -> eof_reached ) {
@@ -135,7 +218,14 @@ static int run_decoder(void *data) {
135
218
LOGD ("End of frames" );
136
219
137
220
run_quit :
221
+ if (output_ctx ) {
222
+ ret = av_write_trailer (output_ctx );
223
+ avio_closep (& output_ctx -> pb );
224
+ }
138
225
avformat_close_input (& format_ctx );
226
+ run_finally_free_output_ctx :
227
+ if (output_ctx )
228
+ avformat_free_context (output_ctx );
139
229
run_finally_free_avio_ctx :
140
230
av_freep (& avio_ctx );
141
231
run_finally_free_format_ctx :
@@ -149,20 +239,21 @@ static int run_decoder(void *data) {
149
239
return 0 ;
150
240
}
151
241
152
- void decoder_init (struct decoder * decoder , struct frames * frames , socket_t video_socket ) {
242
+ void decoder_init (struct decoder * decoder , struct frames * frames , socket_t video_socket , struct size frame_size ) {
153
243
decoder -> frames = frames ;
154
244
decoder -> video_socket = video_socket ;
245
+ decoder -> frame_size = frame_size ;
155
246
}
156
247
157
- SDL_bool decoder_start (struct decoder * decoder ) {
248
+ SDL_bool decoder_start (struct decoder * decoder , const char * out_filename ) {
158
249
LOGD ("Starting decoder thread" );
159
250
251
+ decoder -> out_filename = out_filename ;
160
252
decoder -> thread = SDL_CreateThread (run_decoder , "video_decoder" , decoder );
161
253
if (!decoder -> thread ) {
162
254
LOGC ("Could not start decoder thread" );
163
255
return SDL_FALSE ;
164
256
}
165
-
166
257
return SDL_TRUE ;
167
258
}
168
259
0 commit comments