yading@11
|
1 /*
|
yading@11
|
2 * Windows Television (WTV) muxer
|
yading@11
|
3 * Copyright (c) 2011 Zhentan Feng <spyfeng at gmail dot com>
|
yading@11
|
4 * Copyright (c) 2011 Peter Ross <pross@xvid.org>
|
yading@11
|
5 * This file is part of FFmpeg.
|
yading@11
|
6 *
|
yading@11
|
7 * FFmpeg is free software; you can redistribute it and/or
|
yading@11
|
8 * modify it under the terms of the GNU Lesser General Public
|
yading@11
|
9 * License as published by the Free Software Foundation; either
|
yading@11
|
10 * version 2.1 of the License, or (at your option) any later version.
|
yading@11
|
11 *
|
yading@11
|
12 * FFmpeg is distributed in the hope that it will be useful,
|
yading@11
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
yading@11
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
yading@11
|
15 * Lesser General Public License for more details.
|
yading@11
|
16 *
|
yading@11
|
17 * You should have received a copy of the GNU Lesser General Public
|
yading@11
|
18 * License along with FFmpeg; if not, write to the Free Software
|
yading@11
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
yading@11
|
20 */
|
yading@11
|
21
|
yading@11
|
22 /**
|
yading@11
|
23 * @file
|
yading@11
|
24 * Windows Television (WTV) demuxer
|
yading@11
|
25 * @author Zhentan Feng <spyfeng at gmail dot com>
|
yading@11
|
26 */
|
yading@11
|
27
|
yading@11
|
28 #include "libavutil/intreadwrite.h"
|
yading@11
|
29 #include "libavutil/avassert.h"
|
yading@11
|
30 #include "avformat.h"
|
yading@11
|
31 #include "internal.h"
|
yading@11
|
32 #include "wtv.h"
|
yading@11
|
33 #include "asf.h"
|
yading@11
|
34
|
yading@11
|
35 #define WTV_BIGSECTOR_SIZE (1 << WTV_BIGSECTOR_BITS)
|
yading@11
|
36 #define INDEX_BASE 0x2
|
yading@11
|
37 #define MAX_NB_INDEX 10
|
yading@11
|
38
|
yading@11
|
39 /* declare utf16le strings */
|
yading@11
|
40 #define _ , 0,
|
yading@11
|
41 static const uint8_t timeline_table_0_header_events[] =
|
yading@11
|
42 {'t'_'i'_'m'_'e'_'l'_'i'_'n'_'e'_'.'_'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'E'_'v'_'e'_'n'_'t'_'s', 0};
|
yading@11
|
43 static const uint8_t table_0_header_legacy_attrib[] =
|
yading@11
|
44 {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
|
yading@11
|
45 static const uint8_t table_0_redirector_legacy_attrib[] =
|
yading@11
|
46 {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'r'_'e'_'d'_'i'_'r'_'e'_'c'_'t'_'o'_'r'_'.'_'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
|
yading@11
|
47 static const uint8_t table_0_header_time[] =
|
yading@11
|
48 {'t'_'a'_'b'_'l'_'e'_'.'_'0'_'.'_'h'_'e'_'a'_'d'_'e'_'r'_'.'_'t'_'i'_'m'_'e', 0};
|
yading@11
|
49 static const uint8_t legacy_attrib[] =
|
yading@11
|
50 {'l'_'e'_'g'_'a'_'c'_'y'_'_'_'a'_'t'_'t'_'r'_'i'_'b', 0};
|
yading@11
|
51 #undef _
|
yading@11
|
52
|
yading@11
|
53 static const ff_asf_guid sub_wtv_guid =
|
yading@11
|
54 {0x8C,0xC3,0xD2,0xC2,0x7E,0x9A,0xDA,0x11,0x8B,0xF7,0x00,0x07,0xE9,0x5E,0xAD,0x8D};
|
yading@11
|
55
|
yading@11
|
56 enum WtvFileIndex {
|
yading@11
|
57 WTV_TIMELINE_TABLE_0_HEADER_EVENTS = 0,
|
yading@11
|
58 WTV_TIMELINE_TABLE_0_ENTRIES_EVENTS,
|
yading@11
|
59 WTV_TIMELINE,
|
yading@11
|
60 WTV_TABLE_0_HEADER_LEGACY_ATTRIB,
|
yading@11
|
61 WTV_TABLE_0_ENTRIES_LEGACY_ATTRIB,
|
yading@11
|
62 WTV_TABLE_0_REDIRECTOR_LEGACY_ATTRIB,
|
yading@11
|
63 WTV_TABLE_0_HEADER_TIME,
|
yading@11
|
64 WTV_TABLE_0_ENTRIES_TIME,
|
yading@11
|
65 WTV_FILES
|
yading@11
|
66 };
|
yading@11
|
67
|
yading@11
|
68 typedef struct {
|
yading@11
|
69 int64_t length;
|
yading@11
|
70 const void *header;
|
yading@11
|
71 int depth;
|
yading@11
|
72 int first_sector;
|
yading@11
|
73 } WtvFile;
|
yading@11
|
74
|
yading@11
|
75 typedef struct {
|
yading@11
|
76 int64_t pos;
|
yading@11
|
77 int64_t serial;
|
yading@11
|
78 const ff_asf_guid * guid;
|
yading@11
|
79 int stream_id;
|
yading@11
|
80 } WtvChunkEntry;
|
yading@11
|
81
|
yading@11
|
82 typedef struct {
|
yading@11
|
83 int64_t serial;
|
yading@11
|
84 int64_t value;
|
yading@11
|
85 } WtvSyncEntry;
|
yading@11
|
86
|
yading@11
|
87 typedef struct {
|
yading@11
|
88 int64_t timeline_start_pos;
|
yading@11
|
89 WtvFile file[WTV_FILES];
|
yading@11
|
90 int64_t serial; /** chunk serial number */
|
yading@11
|
91 int64_t last_chunk_pos; /** last chunk position */
|
yading@11
|
92 int64_t last_timestamp_pos; /** last timestamp chunk position */
|
yading@11
|
93 int64_t first_index_pos; /** first index_chunk position */
|
yading@11
|
94
|
yading@11
|
95 WtvChunkEntry index[MAX_NB_INDEX];
|
yading@11
|
96 int nb_index;
|
yading@11
|
97 int first_video_flag;
|
yading@11
|
98
|
yading@11
|
99 WtvSyncEntry *st_pairs; /* (serial, timestamp) pairs */
|
yading@11
|
100 int nb_st_pairs;
|
yading@11
|
101 WtvSyncEntry *sp_pairs; /* (serial, position) pairs */
|
yading@11
|
102 int nb_sp_pairs;
|
yading@11
|
103
|
yading@11
|
104 int64_t last_pts;
|
yading@11
|
105 int64_t last_serial;
|
yading@11
|
106
|
yading@11
|
107 AVPacket thumbnail;
|
yading@11
|
108 } WtvContext;
|
yading@11
|
109
|
yading@11
|
110
|
yading@11
|
111 static void add_serial_pair(WtvSyncEntry ** list, int * count, int64_t serial, int64_t value)
|
yading@11
|
112 {
|
yading@11
|
113 int new_count = *count + 1;
|
yading@11
|
114 WtvSyncEntry *new_list = av_realloc(*list, new_count * sizeof(WtvSyncEntry));
|
yading@11
|
115 if (!new_list)
|
yading@11
|
116 return;
|
yading@11
|
117 new_list[*count] = (WtvSyncEntry){serial, value};
|
yading@11
|
118 *list = new_list;
|
yading@11
|
119 *count = new_count;
|
yading@11
|
120 }
|
yading@11
|
121
|
yading@11
|
122 typedef int WTVHeaderWriteFunc(AVIOContext *pb);
|
yading@11
|
123
|
yading@11
|
124 typedef struct {
|
yading@11
|
125 const uint8_t *header;
|
yading@11
|
126 int header_size;
|
yading@11
|
127 WTVHeaderWriteFunc *write_header;
|
yading@11
|
128 } WTVRootEntryTable;
|
yading@11
|
129
|
yading@11
|
130 static int write_pad(AVIOContext *pb, int size)
|
yading@11
|
131 {
|
yading@11
|
132 for (; size > 0; size--)
|
yading@11
|
133 avio_w8(pb, 0);
|
yading@11
|
134 return 0;
|
yading@11
|
135 }
|
yading@11
|
136
|
yading@11
|
137 static const ff_asf_guid *get_codec_guid(enum AVCodecID id, const AVCodecGuid *av_guid)
|
yading@11
|
138 {
|
yading@11
|
139 int i;
|
yading@11
|
140 for (i = 0; av_guid[i].id != AV_CODEC_ID_NONE; i++) {
|
yading@11
|
141 if (id == av_guid[i].id)
|
yading@11
|
142 return &(av_guid[i].guid);
|
yading@11
|
143 }
|
yading@11
|
144 return NULL;
|
yading@11
|
145 }
|
yading@11
|
146
|
yading@11
|
147 /**
|
yading@11
|
148 * Write chunk header. If header chunk (0x80000000 set) then add to list of header chunks
|
yading@11
|
149 */
|
yading@11
|
150 static void write_chunk_header(AVFormatContext *s, const ff_asf_guid *guid, int length, int stream_id)
|
yading@11
|
151 {
|
yading@11
|
152 WtvContext *wctx = s->priv_data;
|
yading@11
|
153 AVIOContext *pb = s->pb;
|
yading@11
|
154
|
yading@11
|
155 wctx->last_chunk_pos = avio_tell(pb) - wctx->timeline_start_pos;
|
yading@11
|
156 ff_put_guid(pb, guid);
|
yading@11
|
157 avio_wl32(pb, 32 + length);
|
yading@11
|
158 avio_wl32(pb, stream_id);
|
yading@11
|
159 avio_wl64(pb, wctx->serial);
|
yading@11
|
160
|
yading@11
|
161 if ((stream_id & 0x80000000) && guid != &ff_index_guid) {
|
yading@11
|
162 WtvChunkEntry *t = wctx->index + wctx->nb_index;
|
yading@11
|
163 av_assert0(wctx->nb_index < MAX_NB_INDEX);
|
yading@11
|
164 t->pos = wctx->last_chunk_pos;
|
yading@11
|
165 t->serial = wctx->serial;
|
yading@11
|
166 t->guid = guid;
|
yading@11
|
167 t->stream_id = stream_id & 0x3FFFFFFF;
|
yading@11
|
168 wctx->nb_index++;
|
yading@11
|
169 }
|
yading@11
|
170 }
|
yading@11
|
171
|
yading@11
|
172 static void write_chunk_header2(AVFormatContext *s, const ff_asf_guid *guid, int stream_id)
|
yading@11
|
173 {
|
yading@11
|
174 WtvContext *wctx = s->priv_data;
|
yading@11
|
175 AVIOContext *pb = s->pb;
|
yading@11
|
176
|
yading@11
|
177 int64_t last_chunk_pos = wctx->last_chunk_pos;
|
yading@11
|
178 write_chunk_header(s, guid, 0, stream_id); // length updated later
|
yading@11
|
179 avio_wl64(pb, last_chunk_pos);
|
yading@11
|
180 }
|
yading@11
|
181
|
yading@11
|
182 static void finish_chunk_noindex(AVFormatContext *s)
|
yading@11
|
183 {
|
yading@11
|
184 WtvContext *wctx = s->priv_data;
|
yading@11
|
185 AVIOContext *pb = s->pb;
|
yading@11
|
186
|
yading@11
|
187 // update the chunk_len field and pad.
|
yading@11
|
188 int64_t chunk_len = avio_tell(pb) - (wctx->last_chunk_pos + wctx->timeline_start_pos);
|
yading@11
|
189 avio_seek(pb, -(chunk_len - 16), SEEK_CUR);
|
yading@11
|
190 avio_wl32(pb, chunk_len);
|
yading@11
|
191 avio_seek(pb, chunk_len - (16 + 4), SEEK_CUR);
|
yading@11
|
192
|
yading@11
|
193 write_pad(pb, WTV_PAD8(chunk_len) - chunk_len);
|
yading@11
|
194 wctx->serial++;
|
yading@11
|
195 }
|
yading@11
|
196
|
yading@11
|
197 static void write_index(AVFormatContext *s)
|
yading@11
|
198 {
|
yading@11
|
199 AVIOContext *pb = s->pb;
|
yading@11
|
200 WtvContext *wctx = s->priv_data;
|
yading@11
|
201 int i;
|
yading@11
|
202
|
yading@11
|
203 write_chunk_header2(s, &ff_index_guid, 0x80000000);
|
yading@11
|
204 avio_wl32(pb, 0);
|
yading@11
|
205 avio_wl32(pb, 0);
|
yading@11
|
206
|
yading@11
|
207 for (i = 0; i < wctx->nb_index; i++) {
|
yading@11
|
208 WtvChunkEntry *t = wctx->index + i;
|
yading@11
|
209 ff_put_guid(pb, t->guid);
|
yading@11
|
210 avio_wl64(pb, t->pos);
|
yading@11
|
211 avio_wl32(pb, t->stream_id);
|
yading@11
|
212 avio_wl32(pb, 0); // checksum?
|
yading@11
|
213 avio_wl64(pb, t->serial);
|
yading@11
|
214 }
|
yading@11
|
215 wctx->nb_index = 0; // reset index
|
yading@11
|
216 finish_chunk_noindex(s);
|
yading@11
|
217
|
yading@11
|
218 if (!wctx->first_index_pos)
|
yading@11
|
219 wctx->first_index_pos = wctx->last_chunk_pos;
|
yading@11
|
220 }
|
yading@11
|
221
|
yading@11
|
222 static void finish_chunk(AVFormatContext *s)
|
yading@11
|
223 {
|
yading@11
|
224 WtvContext *wctx = s->priv_data;
|
yading@11
|
225 finish_chunk_noindex(s);
|
yading@11
|
226 if (wctx->nb_index == MAX_NB_INDEX)
|
yading@11
|
227 write_index(s);
|
yading@11
|
228 }
|
yading@11
|
229
|
yading@11
|
230 static int write_stream_codec_info(AVFormatContext *s, AVStream *st)
|
yading@11
|
231 {
|
yading@11
|
232 WtvContext *wctx = s->priv_data;
|
yading@11
|
233 const ff_asf_guid *g, *media_type, *format_type;
|
yading@11
|
234 AVIOContext *pb = s->pb;
|
yading@11
|
235 int64_t hdr_pos_start;
|
yading@11
|
236 int hdr_size = 0;
|
yading@11
|
237
|
yading@11
|
238 if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
yading@11
|
239 g = get_codec_guid(st->codec->codec_id, ff_video_guids);
|
yading@11
|
240 media_type = &ff_mediatype_video;
|
yading@11
|
241 format_type = &ff_format_mpeg2_video;
|
yading@11
|
242 } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
|
yading@11
|
243 g = get_codec_guid(st->codec->codec_id, ff_codec_wav_guids);
|
yading@11
|
244 media_type = &ff_mediatype_audio;
|
yading@11
|
245 format_type = &ff_format_waveformatex;
|
yading@11
|
246 } else {
|
yading@11
|
247 av_log(s, AV_LOG_ERROR, "unknown codec_type (0x%x)\n", st->codec->codec_type);
|
yading@11
|
248 return -1;
|
yading@11
|
249 }
|
yading@11
|
250
|
yading@11
|
251 if (g == NULL) {
|
yading@11
|
252 av_log(s, AV_LOG_ERROR, "can't get video codec_id (0x%x) guid.\n", st->codec->codec_id);
|
yading@11
|
253 return -1;
|
yading@11
|
254 }
|
yading@11
|
255
|
yading@11
|
256 ff_put_guid(pb, media_type); // mediatype
|
yading@11
|
257 ff_put_guid(pb, &ff_mediasubtype_cpfilters_processed); // subtype
|
yading@11
|
258 write_pad(pb, 12);
|
yading@11
|
259 ff_put_guid(pb,&ff_format_cpfilters_processed); // format type
|
yading@11
|
260 avio_wl32(pb, 0); // size
|
yading@11
|
261
|
yading@11
|
262 hdr_pos_start = avio_tell(pb);
|
yading@11
|
263 if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
yading@11
|
264 if (wctx->first_video_flag) {
|
yading@11
|
265 write_pad(pb, 216); //The size is sensitive.
|
yading@11
|
266 wctx->first_video_flag = 0;
|
yading@11
|
267 } else {
|
yading@11
|
268 write_pad(pb, 72); // aspect ratio
|
yading@11
|
269 ff_put_bmp_header(pb, st->codec, ff_codec_bmp_tags, 0);
|
yading@11
|
270 }
|
yading@11
|
271 } else {
|
yading@11
|
272 ff_put_wav_header(pb, st->codec);
|
yading@11
|
273 }
|
yading@11
|
274 hdr_size = avio_tell(pb) - hdr_pos_start;
|
yading@11
|
275
|
yading@11
|
276 // seek back write hdr_size
|
yading@11
|
277 avio_seek(pb, -(hdr_size + 4), SEEK_CUR);
|
yading@11
|
278 avio_wl32(pb, hdr_size + 32);
|
yading@11
|
279 avio_seek(pb, hdr_size, SEEK_CUR);
|
yading@11
|
280 ff_put_guid(pb, g); // actual_subtype
|
yading@11
|
281 ff_put_guid(pb, format_type); // actual_formattype
|
yading@11
|
282
|
yading@11
|
283 return 0;
|
yading@11
|
284 }
|
yading@11
|
285
|
yading@11
|
286 static int write_stream_codec(AVFormatContext *s, AVStream * st)
|
yading@11
|
287 {
|
yading@11
|
288 AVIOContext *pb = s->pb;
|
yading@11
|
289 int ret;
|
yading@11
|
290 write_chunk_header2(s, &ff_stream1_guid, 0x80000000 | 0x01);
|
yading@11
|
291
|
yading@11
|
292 avio_wl32(pb, 0x01);
|
yading@11
|
293 write_pad(pb, 4);
|
yading@11
|
294 write_pad(pb, 4);
|
yading@11
|
295
|
yading@11
|
296 ret = write_stream_codec_info(s, st);
|
yading@11
|
297 if (ret < 0) {
|
yading@11
|
298 av_log(s, AV_LOG_ERROR, "write stream codec info failed codec_type(0x%x)\n", st->codec->codec_type);
|
yading@11
|
299 return -1;
|
yading@11
|
300 }
|
yading@11
|
301
|
yading@11
|
302 finish_chunk(s);
|
yading@11
|
303 return 0;
|
yading@11
|
304 }
|
yading@11
|
305
|
yading@11
|
306 static void write_sync(AVFormatContext *s)
|
yading@11
|
307 {
|
yading@11
|
308 AVIOContext *pb = s->pb;
|
yading@11
|
309 WtvContext *wctx = s->priv_data;
|
yading@11
|
310 int64_t last_chunk_pos = wctx->last_chunk_pos;
|
yading@11
|
311
|
yading@11
|
312 write_chunk_header(s, &ff_sync_guid, 0x18, 0);
|
yading@11
|
313 avio_wl64(pb, wctx->first_index_pos);
|
yading@11
|
314 avio_wl64(pb, wctx->last_timestamp_pos);
|
yading@11
|
315 avio_wl64(pb, 0);
|
yading@11
|
316
|
yading@11
|
317 finish_chunk(s);
|
yading@11
|
318 add_serial_pair(&wctx->sp_pairs, &wctx->nb_sp_pairs, wctx->serial, wctx->last_chunk_pos);
|
yading@11
|
319
|
yading@11
|
320 wctx->last_chunk_pos = last_chunk_pos;
|
yading@11
|
321 }
|
yading@11
|
322
|
yading@11
|
323 static int write_stream_data(AVFormatContext *s, AVStream *st)
|
yading@11
|
324 {
|
yading@11
|
325 AVIOContext *pb = s->pb;
|
yading@11
|
326 int ret;
|
yading@11
|
327
|
yading@11
|
328 write_chunk_header2(s, &ff_SBE2_STREAM_DESC_EVENT, 0x80000000 | (st->index + INDEX_BASE));
|
yading@11
|
329 avio_wl32(pb, 0x00000001);
|
yading@11
|
330 avio_wl32(pb, st->index + INDEX_BASE); //stream_id
|
yading@11
|
331 avio_wl32(pb, 0x00000001);
|
yading@11
|
332 write_pad(pb, 8);
|
yading@11
|
333
|
yading@11
|
334 ret = write_stream_codec_info(s, st);
|
yading@11
|
335 if (ret < 0) {
|
yading@11
|
336 av_log(s, AV_LOG_ERROR, "write stream codec info failed codec_type(0x%x)\n", st->codec->codec_type);
|
yading@11
|
337 return -1;
|
yading@11
|
338 }
|
yading@11
|
339 finish_chunk(s);
|
yading@11
|
340
|
yading@11
|
341 avpriv_set_pts_info(st, 64, 1, 10000000);
|
yading@11
|
342
|
yading@11
|
343 return 0;
|
yading@11
|
344 }
|
yading@11
|
345
|
yading@11
|
346 static int write_header(AVFormatContext *s)
|
yading@11
|
347 {
|
yading@11
|
348 AVIOContext *pb = s->pb;
|
yading@11
|
349 WtvContext *wctx = s->priv_data;
|
yading@11
|
350 int i, pad, ret;
|
yading@11
|
351 AVStream *st;
|
yading@11
|
352
|
yading@11
|
353 wctx->last_chunk_pos = -1;
|
yading@11
|
354 wctx->last_timestamp_pos = -1;
|
yading@11
|
355
|
yading@11
|
356 ff_put_guid(pb, &ff_wtv_guid);
|
yading@11
|
357 ff_put_guid(pb, &sub_wtv_guid);
|
yading@11
|
358
|
yading@11
|
359 avio_wl32(pb, 0x01);
|
yading@11
|
360 avio_wl32(pb, 0x02);
|
yading@11
|
361 avio_wl32(pb, 1 << WTV_SECTOR_BITS);
|
yading@11
|
362 avio_wl32(pb, 1 << WTV_BIGSECTOR_BITS);
|
yading@11
|
363
|
yading@11
|
364 //write initial root fields
|
yading@11
|
365 avio_wl32(pb, 0); // root_size, update later
|
yading@11
|
366 write_pad(pb, 4);
|
yading@11
|
367 avio_wl32(pb, 0); // root_sector, update it later.
|
yading@11
|
368
|
yading@11
|
369 write_pad(pb, 32);
|
yading@11
|
370 avio_wl32(pb, 0); // file ends pointer, update it later.
|
yading@11
|
371
|
yading@11
|
372 pad = (1 << WTV_SECTOR_BITS) - avio_tell(pb);
|
yading@11
|
373 write_pad(pb, pad);
|
yading@11
|
374
|
yading@11
|
375 wctx->timeline_start_pos = avio_tell(pb);
|
yading@11
|
376
|
yading@11
|
377 wctx->serial = 1;
|
yading@11
|
378 wctx->last_chunk_pos = -1;
|
yading@11
|
379 wctx->first_video_flag = 1;
|
yading@11
|
380
|
yading@11
|
381 for (i = 0; i < s->nb_streams; i++) {
|
yading@11
|
382 st = s->streams[i];
|
yading@11
|
383 if (st->codec->codec_id == AV_CODEC_ID_MJPEG)
|
yading@11
|
384 continue;
|
yading@11
|
385 ret = write_stream_codec(s, st);
|
yading@11
|
386 if (ret < 0) {
|
yading@11
|
387 av_log(s, AV_LOG_ERROR, "write stream codec failed codec_type(0x%x)\n", st->codec->codec_type);
|
yading@11
|
388 return -1;
|
yading@11
|
389 }
|
yading@11
|
390 if (!i)
|
yading@11
|
391 write_sync(s);
|
yading@11
|
392 }
|
yading@11
|
393
|
yading@11
|
394 for (i = 0; i < s->nb_streams; i++) {
|
yading@11
|
395 st = s->streams[i];
|
yading@11
|
396 if (st->codec->codec_id == AV_CODEC_ID_MJPEG)
|
yading@11
|
397 continue;
|
yading@11
|
398 ret = write_stream_data(s, st);
|
yading@11
|
399 if (ret < 0) {
|
yading@11
|
400 av_log(s, AV_LOG_ERROR, "write stream data failed codec_type(0x%x)\n", st->codec->codec_type);
|
yading@11
|
401 return -1;
|
yading@11
|
402 }
|
yading@11
|
403 }
|
yading@11
|
404
|
yading@11
|
405 if (wctx->nb_index)
|
yading@11
|
406 write_index(s);
|
yading@11
|
407
|
yading@11
|
408 return 0;
|
yading@11
|
409 }
|
yading@11
|
410
|
yading@11
|
411 static void write_timestamp(AVFormatContext *s, AVPacket *pkt)
|
yading@11
|
412 {
|
yading@11
|
413 AVIOContext *pb = s->pb;
|
yading@11
|
414 WtvContext *wctx = s->priv_data;
|
yading@11
|
415 AVCodecContext *enc = s->streams[pkt->stream_index]->codec;
|
yading@11
|
416
|
yading@11
|
417 write_chunk_header(s, &ff_timestamp_guid, 56, 0x40000000 | (INDEX_BASE + pkt->stream_index));
|
yading@11
|
418 write_pad(pb, 8);
|
yading@11
|
419 avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts);
|
yading@11
|
420 avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts);
|
yading@11
|
421 avio_wl64(pb, pkt->pts == AV_NOPTS_VALUE ? -1 : pkt->pts);
|
yading@11
|
422 avio_wl64(pb, 0);
|
yading@11
|
423 avio_wl64(pb, enc->codec_type == AVMEDIA_TYPE_VIDEO && (pkt->flags & AV_PKT_FLAG_KEY) ? 1 : 0);
|
yading@11
|
424 avio_wl64(pb, 0);
|
yading@11
|
425
|
yading@11
|
426 wctx->last_timestamp_pos = wctx->last_chunk_pos;
|
yading@11
|
427 }
|
yading@11
|
428
|
yading@11
|
429 static int write_packet(AVFormatContext *s, AVPacket *pkt)
|
yading@11
|
430 {
|
yading@11
|
431 AVIOContext *pb = s->pb;
|
yading@11
|
432 WtvContext *wctx = s->priv_data;
|
yading@11
|
433
|
yading@11
|
434 if (s->streams[pkt->stream_index]->codec->codec_id == AV_CODEC_ID_MJPEG && !wctx->thumbnail.size) {
|
yading@11
|
435 av_copy_packet(&wctx->thumbnail, pkt);
|
yading@11
|
436 return 0;
|
yading@11
|
437 }
|
yading@11
|
438
|
yading@11
|
439 /* emit sync chunk and 'timeline.table.0.entries.Event' record every 50 frames */
|
yading@11
|
440 if (wctx->serial - (wctx->nb_sp_pairs ? wctx->sp_pairs[wctx->nb_sp_pairs - 1].serial : 0) >= 50)
|
yading@11
|
441 write_sync(s);
|
yading@11
|
442
|
yading@11
|
443 /* emit 'table.0.entries.time' record every 500ms */
|
yading@11
|
444 if (pkt->pts != AV_NOPTS_VALUE && pkt->pts - (wctx->nb_st_pairs ? wctx->st_pairs[wctx->nb_st_pairs - 1].value : 0) >= 5000000)
|
yading@11
|
445 add_serial_pair(&wctx->st_pairs, &wctx->nb_st_pairs, wctx->serial, pkt->pts);
|
yading@11
|
446
|
yading@11
|
447 if (pkt->pts != AV_NOPTS_VALUE && pkt->pts > wctx->last_pts) {
|
yading@11
|
448 wctx->last_pts = pkt->pts;
|
yading@11
|
449 wctx->last_serial = wctx->serial;
|
yading@11
|
450 }
|
yading@11
|
451
|
yading@11
|
452 // write timestamp chunk
|
yading@11
|
453 write_timestamp(s, pkt);
|
yading@11
|
454
|
yading@11
|
455 write_chunk_header(s, &ff_data_guid, pkt->size, INDEX_BASE + pkt->stream_index);
|
yading@11
|
456 avio_write(pb, pkt->data, pkt->size);
|
yading@11
|
457 write_pad(pb, WTV_PAD8(pkt->size) - pkt->size);
|
yading@11
|
458
|
yading@11
|
459 wctx->serial++;
|
yading@11
|
460 return 0;
|
yading@11
|
461 }
|
yading@11
|
462
|
yading@11
|
463 static int write_table0_header_events(AVIOContext *pb)
|
yading@11
|
464 {
|
yading@11
|
465 avio_wl32(pb, 0x10);
|
yading@11
|
466 write_pad(pb, 84);
|
yading@11
|
467 avio_wl64(pb, 0x32);
|
yading@11
|
468 return 96;
|
yading@11
|
469 }
|
yading@11
|
470
|
yading@11
|
471 static int write_table0_header_legacy_attrib(AVIOContext *pb)
|
yading@11
|
472 {
|
yading@11
|
473 int pad = 0;
|
yading@11
|
474 avio_wl32(pb, 0xFFFFFFFF);
|
yading@11
|
475 write_pad(pb, 12);
|
yading@11
|
476 avio_write(pb, legacy_attrib, sizeof(legacy_attrib));
|
yading@11
|
477 pad = WTV_PAD8(sizeof(legacy_attrib)) - sizeof(legacy_attrib);
|
yading@11
|
478 write_pad(pb, pad);
|
yading@11
|
479 write_pad(pb, 32);
|
yading@11
|
480 return 48 + WTV_PAD8(sizeof(legacy_attrib));
|
yading@11
|
481 }
|
yading@11
|
482
|
yading@11
|
483 static int write_table0_header_time(AVIOContext *pb)
|
yading@11
|
484 {
|
yading@11
|
485 avio_wl32(pb, 0x10);
|
yading@11
|
486 write_pad(pb, 76);
|
yading@11
|
487 avio_wl64(pb, 0x40);
|
yading@11
|
488 return 88;
|
yading@11
|
489 }
|
yading@11
|
490
|
yading@11
|
491 static const WTVRootEntryTable wtv_root_entry_table[] = {
|
yading@11
|
492 { timeline_table_0_header_events, sizeof(timeline_table_0_header_events), write_table0_header_events},
|
yading@11
|
493 { ff_timeline_table_0_entries_Events_le16, sizeof(ff_timeline_table_0_entries_Events_le16), NULL},
|
yading@11
|
494 { ff_timeline_le16, sizeof(ff_timeline_le16), NULL},
|
yading@11
|
495 { table_0_header_legacy_attrib, sizeof(table_0_header_legacy_attrib), write_table0_header_legacy_attrib},
|
yading@11
|
496 { ff_table_0_entries_legacy_attrib_le16, sizeof(ff_table_0_entries_legacy_attrib_le16), NULL},
|
yading@11
|
497 { table_0_redirector_legacy_attrib, sizeof(table_0_redirector_legacy_attrib), NULL},
|
yading@11
|
498 { table_0_header_time, sizeof(table_0_header_time), write_table0_header_time},
|
yading@11
|
499 { ff_table_0_entries_time_le16, sizeof(ff_table_0_entries_time_le16), NULL},
|
yading@11
|
500 };
|
yading@11
|
501
|
yading@11
|
502 static int write_root_table(AVFormatContext *s, int64_t sector_pos)
|
yading@11
|
503 {
|
yading@11
|
504 AVIOContext *pb = s->pb;
|
yading@11
|
505 WtvContext *wctx = s->priv_data;
|
yading@11
|
506 int size, pad;
|
yading@11
|
507 int i;
|
yading@11
|
508
|
yading@11
|
509 const WTVRootEntryTable *h = wtv_root_entry_table;
|
yading@11
|
510 for (i = 0; i < sizeof(wtv_root_entry_table)/sizeof(WTVRootEntryTable); i++, h++) {
|
yading@11
|
511 WtvFile *w = &wctx->file[i];
|
yading@11
|
512 int filename_padding = WTV_PAD8(h->header_size) - h->header_size;
|
yading@11
|
513 WTVHeaderWriteFunc *write = h->write_header;
|
yading@11
|
514 int len = 0;
|
yading@11
|
515 int64_t len_pos;
|
yading@11
|
516
|
yading@11
|
517 ff_put_guid(pb, &ff_dir_entry_guid);
|
yading@11
|
518 len_pos = avio_tell(pb);
|
yading@11
|
519 avio_wl16(pb, 40 + h->header_size + filename_padding + 8); // maybe updated later
|
yading@11
|
520 write_pad(pb, 6);
|
yading@11
|
521 avio_wl64(pb, write ? 0 : w->length);// maybe update later
|
yading@11
|
522 avio_wl32(pb, (h->header_size + filename_padding) >> 1);
|
yading@11
|
523 write_pad(pb, 4);
|
yading@11
|
524
|
yading@11
|
525 avio_write(pb, h->header, h->header_size);
|
yading@11
|
526 write_pad(pb, filename_padding);
|
yading@11
|
527
|
yading@11
|
528 if (write) {
|
yading@11
|
529 len = write(pb);
|
yading@11
|
530 // update length field
|
yading@11
|
531 avio_seek(pb, len_pos, SEEK_SET);
|
yading@11
|
532 avio_wl64(pb, 40 + h->header_size + filename_padding + len);
|
yading@11
|
533 avio_wl64(pb, len |(1ULL<<62) | (1ULL<<60));
|
yading@11
|
534 avio_seek(pb, 8 + h->header_size + filename_padding + len, SEEK_CUR);
|
yading@11
|
535 } else {
|
yading@11
|
536 avio_wl32(pb, w->first_sector);
|
yading@11
|
537 avio_wl32(pb, w->depth);
|
yading@11
|
538 }
|
yading@11
|
539 }
|
yading@11
|
540
|
yading@11
|
541 // caculate root table size
|
yading@11
|
542 size = avio_tell(pb) - sector_pos;
|
yading@11
|
543 pad = WTV_SECTOR_SIZE- size;
|
yading@11
|
544 write_pad(pb, pad);
|
yading@11
|
545
|
yading@11
|
546 return size;
|
yading@11
|
547 }
|
yading@11
|
548
|
yading@11
|
549 static void write_fat(AVIOContext *pb, int start_sector, int nb_sectors, int shift)
|
yading@11
|
550 {
|
yading@11
|
551 int i;
|
yading@11
|
552 for (i = 0; i < nb_sectors; i++) {
|
yading@11
|
553 avio_wl32(pb, start_sector + (i << shift));
|
yading@11
|
554 }
|
yading@11
|
555 // pad left sector pointer size
|
yading@11
|
556 write_pad(pb, WTV_SECTOR_SIZE - ((nb_sectors << 2) % WTV_SECTOR_SIZE));
|
yading@11
|
557 }
|
yading@11
|
558
|
yading@11
|
559 static int64_t write_fat_sector(AVFormatContext *s, int64_t start_pos, int nb_sectors, int sector_bits, int depth)
|
yading@11
|
560 {
|
yading@11
|
561 int64_t start_sector = start_pos >> WTV_SECTOR_BITS;
|
yading@11
|
562 int shift = sector_bits - WTV_SECTOR_BITS;
|
yading@11
|
563
|
yading@11
|
564 int64_t fat = avio_tell(s->pb);
|
yading@11
|
565 write_fat(s->pb, start_sector, nb_sectors, shift);
|
yading@11
|
566
|
yading@11
|
567 if (depth == 2) {
|
yading@11
|
568 int64_t start_sector1 = fat >> WTV_SECTOR_BITS;
|
yading@11
|
569 int nb_sectors1 = ((nb_sectors << 2) + WTV_SECTOR_SIZE - 1) / WTV_SECTOR_SIZE;
|
yading@11
|
570 int64_t fat1 = avio_tell(s->pb);
|
yading@11
|
571
|
yading@11
|
572 write_fat(s->pb, start_sector1, nb_sectors1, 0);
|
yading@11
|
573 return fat1;
|
yading@11
|
574 }
|
yading@11
|
575
|
yading@11
|
576 return fat;
|
yading@11
|
577 }
|
yading@11
|
578
|
yading@11
|
579 static void write_table_entries_events(AVFormatContext *s)
|
yading@11
|
580 {
|
yading@11
|
581 AVIOContext *pb = s->pb;
|
yading@11
|
582 WtvContext *wctx = s->priv_data;
|
yading@11
|
583 int i;
|
yading@11
|
584 for (i = 0; i < wctx->nb_sp_pairs; i++) {
|
yading@11
|
585 avio_wl64(pb, wctx->sp_pairs[i].serial);
|
yading@11
|
586 avio_wl64(pb, wctx->sp_pairs[i].value);
|
yading@11
|
587 }
|
yading@11
|
588 }
|
yading@11
|
589
|
yading@11
|
590 static void write_table_entries_time(AVFormatContext *s)
|
yading@11
|
591 {
|
yading@11
|
592 AVIOContext *pb = s->pb;
|
yading@11
|
593 WtvContext *wctx = s->priv_data;
|
yading@11
|
594 int i;
|
yading@11
|
595 for (i = 0; i < wctx->nb_st_pairs; i++) {
|
yading@11
|
596 avio_wl64(pb, wctx->st_pairs[i].value);
|
yading@11
|
597 avio_wl64(pb, wctx->st_pairs[i].serial);
|
yading@11
|
598 }
|
yading@11
|
599 avio_wl64(pb, wctx->last_pts);
|
yading@11
|
600 avio_wl64(pb, wctx->last_serial);
|
yading@11
|
601 }
|
yading@11
|
602
|
yading@11
|
603 static void write_metadata_header(AVIOContext *pb, int type, const char *key, int value_size)
|
yading@11
|
604 {
|
yading@11
|
605 ff_put_guid(pb, &ff_metadata_guid);
|
yading@11
|
606 avio_wl32(pb, type);
|
yading@11
|
607 avio_wl32(pb, value_size);
|
yading@11
|
608 avio_put_str16le(pb, key);
|
yading@11
|
609 }
|
yading@11
|
610
|
yading@11
|
611 static int metadata_header_size(const char *key)
|
yading@11
|
612 {
|
yading@11
|
613 return 16 + 4 + 4 + strlen(key)*2 + 2;
|
yading@11
|
614 }
|
yading@11
|
615
|
yading@11
|
616 static void write_tag_int32(AVIOContext *pb, const char *key, int value)
|
yading@11
|
617 {
|
yading@11
|
618 write_metadata_header(pb, 0, key, 4);
|
yading@11
|
619 avio_wl32(pb, value);
|
yading@11
|
620 }
|
yading@11
|
621
|
yading@11
|
622 static void write_tag(AVIOContext *pb, const char *key, const char *value)
|
yading@11
|
623 {
|
yading@11
|
624 write_metadata_header(pb, 1, key, strlen(value)*2 + 2);
|
yading@11
|
625 avio_put_str16le(pb, value);
|
yading@11
|
626 }
|
yading@11
|
627
|
yading@11
|
628 static int attachment_value_size(const AVPacket *pkt, const AVDictionaryEntry *e)
|
yading@11
|
629 {
|
yading@11
|
630 return strlen("image/jpeg")*2 + 2 + 1 + (e ? strlen(e->value)*2 : 0) + 2 + 4 + pkt->size;
|
yading@11
|
631 }
|
yading@11
|
632
|
yading@11
|
633 static void write_table_entries_attrib(AVFormatContext *s)
|
yading@11
|
634 {
|
yading@11
|
635 WtvContext *wctx = s->priv_data;
|
yading@11
|
636 AVIOContext *pb = s->pb;
|
yading@11
|
637 AVDictionaryEntry *tag = 0;
|
yading@11
|
638
|
yading@11
|
639 //FIXME: translate special tags (e.g. WM/Bitrate) to binary representation
|
yading@11
|
640 ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL);
|
yading@11
|
641 while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
|
yading@11
|
642 write_tag(pb, tag->key, tag->value);
|
yading@11
|
643
|
yading@11
|
644 if (wctx->thumbnail.size) {
|
yading@11
|
645 AVStream *st = s->streams[wctx->thumbnail.stream_index];
|
yading@11
|
646 tag = av_dict_get(st->metadata, "title", NULL, 0);
|
yading@11
|
647 write_metadata_header(pb, 2, "WM/Picture", attachment_value_size(&wctx->thumbnail, tag));
|
yading@11
|
648
|
yading@11
|
649 avio_put_str16le(pb, "image/jpeg");
|
yading@11
|
650 avio_w8(pb, 0x10);
|
yading@11
|
651 avio_put_str16le(pb, tag ? tag->value : "");
|
yading@11
|
652
|
yading@11
|
653 avio_wl32(pb, wctx->thumbnail.size);
|
yading@11
|
654 avio_write(pb, wctx->thumbnail.data, wctx->thumbnail.size);
|
yading@11
|
655
|
yading@11
|
656 write_tag_int32(pb, "WM/MediaThumbType", 2);
|
yading@11
|
657 }
|
yading@11
|
658 }
|
yading@11
|
659
|
yading@11
|
660 static void write_table_redirector_legacy_attrib(AVFormatContext *s)
|
yading@11
|
661 {
|
yading@11
|
662 WtvContext *wctx = s->priv_data;
|
yading@11
|
663 AVIOContext *pb = s->pb;
|
yading@11
|
664 AVDictionaryEntry *tag = 0;
|
yading@11
|
665 int64_t pos = 0;
|
yading@11
|
666
|
yading@11
|
667 //FIXME: translate special tags to binary representation
|
yading@11
|
668 while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
yading@11
|
669 avio_wl64(pb, pos);
|
yading@11
|
670 pos += metadata_header_size(tag->key) + strlen(tag->value)*2 + 2;
|
yading@11
|
671 }
|
yading@11
|
672
|
yading@11
|
673 if (wctx->thumbnail.size) {
|
yading@11
|
674 AVStream *st = s->streams[wctx->thumbnail.stream_index];
|
yading@11
|
675 avio_wl64(pb, pos);
|
yading@11
|
676 pos += metadata_header_size("WM/Picture") + attachment_value_size(&wctx->thumbnail, av_dict_get(st->metadata, "title", NULL, 0));
|
yading@11
|
677
|
yading@11
|
678 avio_wl64(pb, pos);
|
yading@11
|
679 pos += metadata_header_size("WM/MediaThumbType") + 4;
|
yading@11
|
680 }
|
yading@11
|
681 }
|
yading@11
|
682
|
yading@11
|
683 /**
|
yading@11
|
684 * Pad the remainder of a file
|
yading@11
|
685 * Write out fat table
|
yading@11
|
686 * @return <0 on error
|
yading@11
|
687 */
|
yading@11
|
688 static int finish_file(AVFormatContext *s, enum WtvFileIndex index, int64_t start_pos)
|
yading@11
|
689 {
|
yading@11
|
690 WtvContext *wctx = s->priv_data;
|
yading@11
|
691 AVIOContext *pb = s->pb;
|
yading@11
|
692 WtvFile *w = &wctx->file[index];
|
yading@11
|
693 int64_t end_pos = avio_tell(pb);
|
yading@11
|
694 int sector_bits, nb_sectors, pad;
|
yading@11
|
695
|
yading@11
|
696 av_assert0(index < WTV_FILES);
|
yading@11
|
697
|
yading@11
|
698 w->length = (end_pos - start_pos);
|
yading@11
|
699
|
yading@11
|
700 // determine optimal fat table depth, sector_bits, nb_sectors
|
yading@11
|
701 if (w->length <= WTV_SECTOR_SIZE) {
|
yading@11
|
702 w->depth = 0;
|
yading@11
|
703 sector_bits = WTV_SECTOR_BITS;
|
yading@11
|
704 } else if (w->length <= (WTV_SECTOR_SIZE / 4) * WTV_SECTOR_SIZE) {
|
yading@11
|
705 w->depth = 1;
|
yading@11
|
706 sector_bits = WTV_SECTOR_BITS;
|
yading@11
|
707 } else if (w->length <= (WTV_SECTOR_SIZE / 4) * WTV_BIGSECTOR_SIZE) {
|
yading@11
|
708 w->depth = 1;
|
yading@11
|
709 sector_bits = WTV_BIGSECTOR_BITS;
|
yading@11
|
710 } else if (w->length <= (int64_t)(WTV_SECTOR_SIZE / 4) * (WTV_SECTOR_SIZE / 4) * WTV_SECTOR_SIZE) {
|
yading@11
|
711 w->depth = 2;
|
yading@11
|
712 sector_bits = WTV_SECTOR_BITS;
|
yading@11
|
713 } else if (w->length <= (int64_t)(WTV_SECTOR_SIZE / 4) * (WTV_SECTOR_SIZE / 4) * WTV_BIGSECTOR_SIZE) {
|
yading@11
|
714 w->depth = 2;
|
yading@11
|
715 sector_bits = WTV_BIGSECTOR_BITS;
|
yading@11
|
716 } else {
|
yading@11
|
717 av_log(s, AV_LOG_ERROR, "unsupported file allocation table depth (%"PRIi64" bytes)\n", w->length);
|
yading@11
|
718 return -1;
|
yading@11
|
719 }
|
yading@11
|
720
|
yading@11
|
721 // determine the nb_sectors
|
yading@11
|
722 nb_sectors = (int)(w->length >> sector_bits);
|
yading@11
|
723
|
yading@11
|
724 // pad sector of timeline
|
yading@11
|
725 pad = (1 << sector_bits) - (w->length % (1 << sector_bits));
|
yading@11
|
726 if (pad) {
|
yading@11
|
727 nb_sectors++;
|
yading@11
|
728 write_pad(pb, pad);
|
yading@11
|
729 }
|
yading@11
|
730
|
yading@11
|
731 //write fat table
|
yading@11
|
732 if (w->depth > 0) {
|
yading@11
|
733 w->first_sector = write_fat_sector(s, start_pos, nb_sectors, sector_bits, w->depth) >> WTV_SECTOR_BITS;
|
yading@11
|
734 } else {
|
yading@11
|
735 w->first_sector = start_pos >> WTV_SECTOR_BITS;
|
yading@11
|
736 }
|
yading@11
|
737
|
yading@11
|
738 w->length |= 1ULL<<60;
|
yading@11
|
739 if (sector_bits == WTV_SECTOR_BITS)
|
yading@11
|
740 w->length |= 1ULL<<63;
|
yading@11
|
741
|
yading@11
|
742 return 0;
|
yading@11
|
743 }
|
yading@11
|
744
|
yading@11
|
745 static int write_trailer(AVFormatContext *s)
|
yading@11
|
746 {
|
yading@11
|
747 WtvContext *wctx = s->priv_data;
|
yading@11
|
748 AVIOContext *pb = s->pb;
|
yading@11
|
749 int root_size;
|
yading@11
|
750 int64_t sector_pos;
|
yading@11
|
751 int64_t start_pos, file_end_pos;
|
yading@11
|
752
|
yading@11
|
753 if (finish_file(s, WTV_TIMELINE, wctx->timeline_start_pos) < 0)
|
yading@11
|
754 return -1;
|
yading@11
|
755
|
yading@11
|
756 start_pos = avio_tell(pb);
|
yading@11
|
757 write_table_entries_events(s);
|
yading@11
|
758 if (finish_file(s, WTV_TIMELINE_TABLE_0_ENTRIES_EVENTS, start_pos) < 0)
|
yading@11
|
759 return -1;
|
yading@11
|
760
|
yading@11
|
761 start_pos = avio_tell(pb);
|
yading@11
|
762 write_table_entries_attrib(s);
|
yading@11
|
763 if (finish_file(s, WTV_TABLE_0_ENTRIES_LEGACY_ATTRIB, start_pos) < 0)
|
yading@11
|
764 return -1;
|
yading@11
|
765
|
yading@11
|
766 start_pos = avio_tell(pb);
|
yading@11
|
767 write_table_redirector_legacy_attrib(s);
|
yading@11
|
768 if (finish_file(s, WTV_TABLE_0_REDIRECTOR_LEGACY_ATTRIB, start_pos) < 0)
|
yading@11
|
769 return -1;
|
yading@11
|
770
|
yading@11
|
771 start_pos = avio_tell(pb);
|
yading@11
|
772 write_table_entries_time(s);
|
yading@11
|
773 if (finish_file(s, WTV_TABLE_0_ENTRIES_TIME, start_pos) < 0)
|
yading@11
|
774 return -1;
|
yading@11
|
775
|
yading@11
|
776 // write root table
|
yading@11
|
777 sector_pos = avio_tell(pb);
|
yading@11
|
778 root_size = write_root_table(s, sector_pos);
|
yading@11
|
779
|
yading@11
|
780 file_end_pos = avio_tell(pb);
|
yading@11
|
781 // update root value
|
yading@11
|
782 avio_seek(pb, 0x30, SEEK_SET);
|
yading@11
|
783 avio_wl32(pb, root_size);
|
yading@11
|
784 avio_seek(pb, 4, SEEK_CUR);
|
yading@11
|
785 avio_wl32(pb, sector_pos >> WTV_SECTOR_BITS);
|
yading@11
|
786 avio_seek(pb, 0x5c, SEEK_SET);
|
yading@11
|
787 avio_wl32(pb, file_end_pos >> WTV_SECTOR_BITS);
|
yading@11
|
788
|
yading@11
|
789 avio_flush(pb);
|
yading@11
|
790
|
yading@11
|
791 av_free(wctx->sp_pairs);
|
yading@11
|
792 av_free(wctx->st_pairs);
|
yading@11
|
793 av_free_packet(&wctx->thumbnail);
|
yading@11
|
794 return 0;
|
yading@11
|
795 }
|
yading@11
|
796
|
yading@11
|
797 AVOutputFormat ff_wtv_muxer = {
|
yading@11
|
798 .name = "wtv",
|
yading@11
|
799 .long_name = NULL_IF_CONFIG_SMALL("Windows Television (WTV)"),
|
yading@11
|
800 .extensions = "wtv",
|
yading@11
|
801 .priv_data_size = sizeof(WtvContext),
|
yading@11
|
802 .audio_codec = AV_CODEC_ID_AC3,
|
yading@11
|
803 .video_codec = AV_CODEC_ID_MPEG2VIDEO,
|
yading@11
|
804 .write_header = write_header,
|
yading@11
|
805 .write_packet = write_packet,
|
yading@11
|
806 .write_trailer = write_trailer,
|
yading@11
|
807 .codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags,
|
yading@11
|
808 ff_codec_wav_tags, 0 },
|
yading@11
|
809 };
|