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