19861
|
1 #include <stdio.h>
|
|
2
|
|
3 #include "config.h"
|
|
4 #include "mp_msg.h"
|
|
5
|
|
6 #include "stream.h"
|
|
7 #include "demuxer.h"
|
|
8 #include "stheader.h"
|
|
9
|
|
10 #define USE_LIBNUT
|
|
11 #ifdef USE_LIBNUT
|
|
12
|
|
13 #include "nut.h"
|
|
14
|
|
15 typedef struct {
|
|
16 int last_pts; // FIXME
|
|
17 nut_context_t * nut;
|
|
18 nut_stream_header_t * s;
|
|
19 } nut_priv_t;
|
|
20
|
|
21 static size_t mp_read(void * h, size_t len, uint8_t * buf) {
|
|
22 stream_t * stream = (stream_t*)h;
|
|
23
|
|
24 if(stream_eof(stream)) return 0;
|
|
25
|
|
26 return stream_read(stream, buf, len);
|
|
27 }
|
|
28
|
|
29 static off_t mp_seek(void * h, long long pos, int whence) {
|
|
30 stream_t * stream = (stream_t*)h;
|
|
31
|
|
32 if (stream->end_pos < stream_tell(stream))
|
|
33 stream->end_pos = stream_tell(stream);
|
|
34
|
|
35 if (whence == SEEK_CUR) pos += stream_tell(stream);
|
|
36 else if (whence == SEEK_END) pos += stream->end_pos;
|
|
37 else if (whence != SEEK_SET) return -1;
|
|
38
|
|
39 if (pos < stream->end_pos && stream->eof) stream_reset(stream);
|
|
40 if (stream_seek(stream, pos) == 0) return -1;
|
|
41
|
|
42 return pos;
|
|
43 }
|
|
44
|
|
45 #define ID_STRING "nut/multimedia container"
|
|
46 #define ID_LENGTH (strlen(ID_STRING) + 1)
|
|
47
|
|
48 static int nut_check_file(demuxer_t * demuxer) {
|
|
49 uint8_t buf[ID_LENGTH];
|
|
50 nut_priv_t * priv = demuxer->priv = calloc(1, sizeof(nut_priv_t));
|
|
51
|
|
52 if (stream_read(demuxer->stream, buf, ID_LENGTH) != ID_LENGTH) return 0;
|
|
53
|
|
54 if (memcmp(buf, ID_STRING, ID_LENGTH)) return 0;
|
|
55
|
|
56 stream_seek(demuxer->stream, 0);
|
|
57 return DEMUXER_TYPE_NUT;
|
|
58 }
|
|
59
|
|
60 static demuxer_t * demux_open_nut(demuxer_t * demuxer) {
|
|
61 extern int index_mode;
|
|
62 nut_demuxer_opts_t dopts = {
|
|
63 .input = {
|
|
64 .priv = demuxer->stream,
|
|
65 .seek = mp_seek,
|
|
66 .read = mp_read,
|
|
67 .eof = NULL,
|
|
68 .file_pos = stream_tell(demuxer->stream),
|
|
69 },
|
|
70 .read_index = index_mode
|
|
71 };
|
|
72 nut_priv_t * priv = demuxer->priv;
|
|
73 nut_context_t * nut = priv->nut = nut_demuxer_init(&dopts);
|
|
74 nut_stream_header_t * s;
|
|
75 int ret;
|
|
76 int i;
|
|
77
|
|
78 if ((ret = nut_read_headers(nut, &s))) {
|
|
79 if (ret < 0) mp_msg(MSGT_HEADER, MSGL_ERR, "NUT error: %s\n",
|
|
80 nut_error(-ret));
|
|
81 nut_demuxer_uninit(nut);
|
|
82 free(priv);
|
|
83 return NULL;
|
|
84 }
|
|
85
|
|
86 priv->s = s;
|
|
87
|
|
88 for (i = 0; s[i].type != -1 && i < 2; i++) switch(s[i].type) {
|
|
89 case NUT_AUDIO_CLASS: {
|
|
90 WAVEFORMATEX *wf =
|
|
91 calloc(sizeof(WAVEFORMATEX) +
|
|
92 s[i].codec_specific_len, 1);
|
|
93 sh_audio_t* sh_audio = new_sh_audio(demuxer, i);
|
|
94 int j;
|
|
95
|
|
96 sh_audio->wf= wf; sh_audio->ds = demuxer->audio;
|
|
97 sh_audio->audio.dwSampleSize = 0; // FIXME
|
|
98 sh_audio->audio.dwScale = s[i].time_base.nom;
|
|
99 sh_audio->audio.dwRate = s[i].time_base.den;
|
|
100 sh_audio->format = 0;
|
|
101 for (j = 0; j < s[i].fourcc_len && j < 4; j++)
|
|
102 sh_audio->format |= s[i].fourcc[j]<<(j*8);
|
|
103 sh_audio->channels = s[i].channel_count;
|
|
104 sh_audio->samplerate =
|
|
105 s[i].samplerate_nom / s[i].samplerate_denom;
|
|
106 sh_audio->i_bps = 0; // FIXME
|
|
107
|
|
108 wf->wFormatTag = sh_audio->format;
|
|
109 wf->nChannels = s[i].channel_count;
|
|
110 wf->nSamplesPerSec =
|
|
111 s[i].samplerate_nom / s[i].samplerate_denom;
|
|
112 wf->nAvgBytesPerSec = 0; // FIXME
|
|
113 wf->nBlockAlign = 0; // FIXME
|
|
114 wf->wBitsPerSample = 0; // FIXME
|
|
115 wf->cbSize = s[i].codec_specific_len;
|
|
116 if (s[i].codec_specific_len)
|
|
117 memcpy(wf + 1, s[i].codec_specific,
|
|
118 s[i].codec_specific_len);
|
|
119
|
|
120 demuxer->audio->id = i;
|
|
121 demuxer->audio->sh= demuxer->a_streams[i];
|
|
122 break;
|
|
123 }
|
|
124 case NUT_VIDEO_CLASS: {
|
|
125 BITMAPINFOHEADER * bih =
|
|
126 calloc(sizeof(BITMAPINFOHEADER) +
|
|
127 s[i].codec_specific_len, 1);
|
|
128 sh_video_t * sh_video = new_sh_video(demuxer, i);
|
|
129 int j;
|
|
130
|
|
131 sh_video->bih = bih;
|
|
132 sh_video->ds = demuxer->video;
|
|
133 sh_video->disp_w = s[i].width;
|
|
134 sh_video->disp_h = s[i].height;
|
|
135 sh_video->video.dwScale = s[i].time_base.nom;
|
|
136 sh_video->video.dwRate = s[i].time_base.den;
|
|
137
|
|
138 sh_video->fps = sh_video->video.dwRate/
|
|
139 (float)sh_video->video.dwScale;
|
|
140 sh_video->frametime = 1./sh_video->fps;
|
|
141 sh_video->format = 0;
|
|
142 for (j = 0; j < s[i].fourcc_len && j < 4; j++)
|
|
143 sh_video->format |= s[i].fourcc[j]<<(j*8);
|
|
144 if (!s[i].sample_height) sh_video->aspect = 0;
|
|
145 else sh_video->aspect =
|
|
146 s[i].sample_width / (float)s[i].sample_height;
|
|
147 sh_video->i_bps = 0; // FIXME
|
|
148
|
|
149 bih->biSize = sizeof(BITMAPINFOHEADER) +
|
|
150 s[i].codec_specific_len;
|
|
151 bih->biWidth = s[i].width;
|
|
152 bih->biHeight = s[i].height;
|
|
153 bih->biBitCount = 0; // FIXME
|
|
154 bih->biSizeImage = 0; // FIXME
|
|
155 bih->biCompression = sh_video->format;
|
|
156
|
|
157 if (s[i].codec_specific_len)
|
|
158 memcpy(bih + 1, s[i].codec_specific,
|
|
159 s[i].codec_specific_len);
|
|
160
|
|
161 demuxer->video->id = i;
|
|
162 demuxer->video->sh = demuxer->v_streams[i];
|
|
163 break;
|
|
164 }
|
|
165 }
|
|
166
|
|
167 return demuxer;
|
|
168 }
|
|
169
|
|
170 static int demux_nut_fill_buffer(demuxer_t * demuxer, demux_stream_t * dsds) {
|
|
171 nut_priv_t * priv = demuxer->priv;
|
|
172 nut_context_t * nut = priv->nut;
|
|
173 demux_packet_t *dp;
|
|
174 demux_stream_t *ds;
|
|
175 nut_packet_t pd;
|
|
176 int ret;
|
|
177 double pts;
|
|
178
|
|
179 demuxer->filepos = stream_tell(demuxer->stream);
|
|
180 if (stream_eof(demuxer->stream)) return 0;
|
|
181
|
|
182 while (1) {
|
|
183 ret = nut_read_next_packet(nut, &pd);
|
|
184 if (ret < 0) {
|
|
185 mp_msg(MSGT_HEADER, MSGL_ERR, "NUT error: %s\n",
|
|
186 nut_error(-ret));
|
|
187 continue;
|
|
188 }
|
|
189 if (ret == 1) return 0; // EOF
|
|
190 if (pd.type == e_frame) break;
|
|
191 // else, skip this packet
|
|
192 while ((ret = nut_skip_packet(nut, &pd.len))) {
|
|
193 if (ret < 0) {
|
|
194 mp_msg(MSGT_HEADER, MSGL_ERR,
|
|
195 "NUT error: %s\n", nut_error(-ret));
|
|
196 break;
|
|
197 }
|
|
198 if (ret == 1) return 0; // EOF
|
|
199 }
|
|
200 }
|
|
201
|
|
202 pts = (double)pd.pts * priv->s[pd.stream].time_base.nom /
|
|
203 priv->s[pd.stream].time_base.den;
|
|
204
|
|
205 if (pd.stream == demuxer->audio->id) {
|
|
206 ds = demuxer->audio;
|
|
207 if (!demuxer->video->sh) {
|
|
208 ((sh_audio_t*)ds->sh)->delay = pts;
|
|
209 }
|
|
210 }
|
|
211 else if (pd.stream == demuxer->video->id) {
|
|
212 ds = demuxer->video;
|
|
213 }
|
|
214 else {
|
|
215 while ((ret = nut_skip_packet(nut, &pd.len))) {
|
|
216 if (ret < 0) {
|
|
217 mp_msg(MSGT_HEADER, MSGL_ERR,
|
|
218 "NUT error: %s\n", nut_error(-ret));
|
|
219 break;
|
|
220 }
|
|
221 if (ret == 1) return 0; // EOF
|
|
222 }
|
|
223 return 1;
|
|
224 }
|
|
225
|
|
226 if (pd.stream == 0) priv->last_pts = pd.pts;
|
|
227
|
|
228 dp = new_demux_packet(pd.len);
|
|
229
|
|
230 dp->pts = pts;
|
|
231
|
|
232 dp->pos = demuxer->filepos;
|
|
233 dp->flags= (pd.flags & NUT_FLAG_KEY) ? 0x10 : 0;
|
|
234
|
|
235 while ((ret = nut_read_frame(nut, &pd.len, dp->buffer))) {
|
|
236 if (ret < 0) {
|
|
237 mp_msg(MSGT_HEADER, MSGL_ERR,
|
|
238 "NUT error: %s\n", nut_error(-ret));
|
|
239 break;
|
|
240 }
|
|
241 if (ret == 1) return 0; // EOF
|
|
242 }
|
|
243 ds_add_packet(ds, dp); // append packet to DS stream
|
|
244 return 1;
|
|
245 }
|
|
246
|
|
247 static void demux_seek_nut(demuxer_t * demuxer, float time_pos, float audio_delay, int flags) {
|
|
248 nut_context_t * nut = ((nut_priv_t*)demuxer->priv)->nut;
|
|
249 nut_priv_t * priv = demuxer->priv;
|
|
250 sh_audio_t * sh_audio = demuxer->audio->sh;
|
|
251 int nutflags = 0;
|
|
252 int ret;
|
|
253 const int tmp[] = { 0, -1 };
|
|
254
|
|
255 if (!(flags & 1)) {
|
|
256 nutflags |= 1; // relative
|
|
257 if (time_pos > 0) nutflags |= 2; // forwards
|
|
258 }
|
|
259
|
|
260 if (flags & 2) // percent
|
|
261 time_pos *= priv->s[0].max_pts *
|
|
262 (double)priv->s[0].time_base.nom /
|
|
263 priv->s[0].time_base.den;
|
|
264
|
|
265 ret = nut_seek(nut, time_pos, nutflags, tmp);
|
|
266 if (ret < 0)
|
|
267 mp_msg(MSGT_HEADER, MSGL_ERR, "NUT error: %s\n", nut_error(-ret));
|
|
268 if (sh_audio) resync_audio_stream(sh_audio);
|
|
269 }
|
|
270
|
|
271 static int demux_control_nut(demuxer_t * demuxer, int cmd, void * arg) {
|
|
272 nut_priv_t * priv = demuxer->priv;
|
|
273 switch (cmd) {
|
|
274 case DEMUXER_CTRL_GET_TIME_LENGTH:
|
|
275 *((double *)arg) = priv->s[0].max_pts *
|
|
276 (double)priv->s[0].time_base.nom /
|
|
277 priv->s[0].time_base.den;
|
|
278 return DEMUXER_CTRL_OK;
|
|
279 case DEMUXER_CTRL_GET_PERCENT_POS:
|
|
280 if (priv->s[0].max_pts == 0)
|
|
281 return DEMUXER_CTRL_DONTKNOW;
|
|
282 *((int *)arg) = priv->last_pts * 100 /
|
|
283 (double)priv->s[0].max_pts;
|
|
284 return DEMUXER_CTRL_OK;
|
|
285 default:
|
|
286 return DEMUXER_CTRL_NOTIMPL;
|
|
287 }
|
|
288 }
|
|
289
|
|
290 static void demux_close_nut(demuxer_t *demuxer) {
|
|
291 nut_context_t * nut = ((nut_priv_t*)demuxer->priv)->nut;
|
|
292 nut_demuxer_uninit(nut);
|
|
293 free(((nut_priv_t*)demuxer->priv)->s);
|
|
294 free(demuxer->priv);
|
|
295 demuxer->priv = NULL;
|
|
296 }
|
|
297
|
|
298
|
|
299 demuxer_desc_t demuxer_desc_nut = {
|
|
300 "NUT demuxer",
|
|
301 "nut",
|
|
302 "libnut",
|
|
303 "Oded Shimon (ods15)",
|
|
304 "NUT demuxer, requires libnut",
|
|
305 DEMUXER_TYPE_NUT,
|
|
306 1, // safe check demuxer
|
|
307 nut_check_file,
|
|
308 demux_nut_fill_buffer,
|
|
309 demux_open_nut,
|
|
310 demux_close_nut,
|
|
311 demux_seek_nut,
|
|
312 demux_control_nut
|
|
313 };
|
|
314
|
|
315 #endif // USE_LIBNUT
|
|
316
|