demux: adaptive: merge toChunk/getChunk
[vlc.git] / modules / demux / smooth / playlist / ForgedInitSegment.cpp
1 /*
2  * ForgedInitSegment.cpp
3  *****************************************************************************
4  * Copyright (C) 2015 - VideoLAN Authors
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "ForgedInitSegment.hpp"
25 #include "MemoryChunk.hpp"
26 #include "../adaptive/playlist/SegmentChunk.hpp"
27
28 #include <vlc_common.h>
29
30 #include <cstdlib>
31
32 extern "C"
33 {
34     #include "../../mux/mp4/libmp4mux.h"
35     #include "../../demux/mp4/libmp4.h" /* majors */
36 }
37
38 using namespace adaptive::playlist;
39 using namespace smooth::playlist;
40 using namespace smooth::http;
41
42 ForgedInitSegment::ForgedInitSegment(ICanonicalUrl *parent,
43                                      const std::string &type_,
44                                      uint64_t timescale_,
45                                      uint64_t duration_) :
46     InitSegment(parent), TimescaleAble()
47 {
48     type = type_;
49     duration.Set(duration_);
50     extradata = NULL;
51     i_extradata = 0;
52     setTimescale(timescale_);
53     formatex.cbSize = 0;
54     formatex.nAvgBytesPerSec = 0;
55     formatex.nBlockAlign = 0;
56     formatex.nChannels = 0;
57     formatex.nSamplesPerSec = 0;
58     formatex.wBitsPerSample = 0;
59     formatex.wFormatTag = 0;
60     width = height = 0;
61     fourcc = 0;
62     es_type = 0;
63     track_id = 1;
64 }
65
66 ForgedInitSegment::~ForgedInitSegment()
67 {
68     free(extradata);
69 }
70
71 static uint8_t *HexDecode(const std::string &s, size_t *decoded_size)
72 {
73     *decoded_size = s.size() / 2;
74     uint8_t *data = (uint8_t *) malloc(*decoded_size);
75     if(data)
76     {
77         for(size_t i=0; i<*decoded_size; i++)
78             data[i] = std::strtoul(s.substr(i*2, 2).c_str(), NULL, 16);
79     }
80     return data;
81 }
82
83 void ForgedInitSegment::fromWaveFormatEx(const uint8_t *p_data, size_t i_data)
84 {
85     if(i_data >= sizeof(WAVEFORMATEX))
86     {
87         formatex.wFormatTag = GetWLE(p_data);
88         wf_tag_to_fourcc(formatex.wFormatTag, &fourcc, NULL);
89         formatex.nChannels = GetWLE(&p_data[2]);
90         formatex.nSamplesPerSec = GetDWLE(&p_data[4]);
91         formatex.nAvgBytesPerSec = GetDWLE(&p_data[8]);
92         formatex.nBlockAlign = GetWLE(&p_data[12]);
93         formatex.wBitsPerSample = GetWLE(&p_data[14]);
94         formatex.cbSize = GetWLE(&p_data[16]);
95         if(i_data > sizeof(WAVEFORMATEX))
96         {
97             if(extradata)
98             {
99                 free(extradata);
100                 extradata = NULL;
101                 i_extradata = 0;
102             }
103             formatex.cbSize = __MIN(i_data - sizeof(WAVEFORMATEX), formatex.cbSize);
104             extradata = (uint8_t*)malloc(formatex.cbSize);
105             if(extradata)
106             {
107                 memcpy(extradata, &p_data[sizeof(WAVEFORMATEX)], formatex.cbSize);
108                 i_extradata = formatex.cbSize;
109             }
110         }
111         es_type = AUDIO_ES;
112     }
113 }
114
115 void ForgedInitSegment::fromVideoInfoHeader(const uint8_t *, size_t)
116 {
117 //    VIDEOINFOHEADER
118     es_type = VIDEO_ES;
119 }
120
121 void ForgedInitSegment::setWaveFormatEx(const std::string &waveformat)
122 {
123     size_t i_data;
124     uint8_t *p_data = HexDecode(waveformat, &i_data);
125     fromWaveFormatEx(p_data, i_data);
126     free(p_data);
127 }
128
129 void ForgedInitSegment::setCodecPrivateData(const std::string &extra)
130 {
131     if(extradata)
132     {
133         free(extradata);
134         extradata = NULL;
135         i_extradata = 0;
136     }
137     extradata = HexDecode(extra, &i_extradata);
138     if(fourcc == VLC_CODEC_WMAP)
139     {
140         //fromWaveFormatEx(const std::string &extra);
141     }
142 }
143
144 void ForgedInitSegment::setChannels(uint16_t i)
145 {
146     formatex.nChannels = i;
147 }
148
149 void ForgedInitSegment::setPacketSize(uint16_t i)
150 {
151     formatex.nBlockAlign = i;
152 }
153
154 void ForgedInitSegment::setSamplingRate(uint32_t i)
155 {
156     formatex.nSamplesPerSec = i;
157 }
158
159 void ForgedInitSegment::setBitsPerSample(uint16_t i)
160 {
161     formatex.wBitsPerSample = i;
162 }
163
164 void ForgedInitSegment::setVideoSize(unsigned w, unsigned h)
165 {
166     width = w;
167     height = h;
168 }
169
170 void ForgedInitSegment::setTrackID(unsigned i)
171 {
172     track_id = i;
173 }
174
175 void ForgedInitSegment::setAudioTag(uint16_t i)
176 {
177     wf_tag_to_fourcc(i, &fourcc, NULL);
178 }
179
180 void ForgedInitSegment::setFourCC(const std::string &fcc)
181 {
182     if(fcc.size() == 4)
183     {
184         fourcc = VLC_FOURCC(fcc[0], fcc[1], fcc[2], fcc[3]);
185         switch(fourcc)
186         {
187             case VLC_FOURCC( 'A', 'V', 'C', '1' ):
188             case VLC_FOURCC( 'A', 'V', 'C', 'B' ):
189             case VLC_FOURCC( 'H', '2', '6', '4' ):
190             case VLC_FOURCC( 'W', 'V', 'C', '1' ):
191                 es_type = VIDEO_ES;
192                 break;
193             case VLC_FOURCC( 'A', 'A', 'C', 'L' ):
194             case VLC_FOURCC( 'W', 'M', 'A', 'P' ):
195             default:
196                 es_type = AUDIO_ES;
197                 break;
198         }
199     }
200 }
201
202 void ForgedInitSegment::setLanguage(const std::string &lang)
203 {
204     language = lang;
205 }
206
207 block_t * ForgedInitSegment::buildMoovBox()
208 {
209     mp4mux_trackinfo_t trackinfo;
210     mp4mux_trackinfo_Init(&trackinfo);
211
212     trackinfo.i_track_id = 0x01; /* Will always be 1st and unique track; tfhd patched on block read */
213     trackinfo.i_timescale = inheritTimescale();
214     trackinfo.i_read_duration = duration.Get();
215     trackinfo.i_trex_default_length = 1;
216     trackinfo.i_trex_default_size = 1;
217
218     trackinfo.fmt.i_cat = es_type;
219     trackinfo.fmt.i_original_fourcc = fourcc;
220     trackinfo.fmt.i_codec = vlc_fourcc_GetCodec(es_type, fourcc);
221     switch(es_type)
222     {
223         case VIDEO_ES:
224             if( fourcc == VLC_FOURCC( 'A', 'V', 'C', '1' ) ||
225                 fourcc == VLC_FOURCC( 'A', 'V', 'C', 'B' ) ||
226                 fourcc == VLC_FOURCC( 'H', '2', '6', '4' ) )
227             {
228                 trackinfo.fmt.i_codec = VLC_CODEC_H264;
229             }
230             else if( fourcc == VLC_FOURCC( 'W', 'V', 'C', '1' ) )
231             {
232                 trackinfo.fmt.i_codec = VLC_CODEC_VC1;
233 //                trackinfo.fmt.video.i_bits_per_pixel = 0x18; // No clue why this was set in smooth streamfilter
234             }
235
236             trackinfo.fmt.video.i_width = width;
237             trackinfo.fmt.video.i_height = height;
238             trackinfo.fmt.video.i_visible_width = width;
239             trackinfo.fmt.video.i_visible_height = height;
240
241             if(i_extradata && extradata)
242             {
243                 trackinfo.fmt.p_extra = malloc(i_extradata);
244                 if(trackinfo.fmt.p_extra)
245                 {
246                     memcpy(trackinfo.fmt.p_extra, extradata, i_extradata);
247                     trackinfo.fmt.i_extra = i_extradata;
248                 }
249             }
250             break;
251
252         case AUDIO_ES:
253             trackinfo.fmt.audio.i_channels = formatex.nChannels;
254             trackinfo.fmt.audio.i_rate = formatex.nSamplesPerSec;
255             trackinfo.fmt.audio.i_bitspersample = formatex.wBitsPerSample;
256             trackinfo.fmt.audio.i_blockalign = formatex.nBlockAlign;
257             trackinfo.fmt.i_bitrate = formatex.nAvgBytesPerSec * 8; // FIXME (use bitrate) ?
258
259             if(i_extradata && extradata)
260             {
261                 trackinfo.fmt.p_extra = malloc(i_extradata);
262                 if(trackinfo.fmt.p_extra)
263                 {
264                     memcpy(trackinfo.fmt.p_extra, extradata, i_extradata);
265                     trackinfo.fmt.i_extra = i_extradata;
266                 }
267             }
268         default:
269             break;
270     }
271
272     if(!language.empty())
273         trackinfo.fmt.psz_language = strdup(language.c_str());
274
275     mp4mux_trackinfo_t *p_tracks = &trackinfo;
276     bo_t *box = GetMoovBox(NULL, &p_tracks, 1, true, false, false, false);
277     mp4mux_trackinfo_Clear(&trackinfo);
278
279     block_t *moov = NULL;
280     if(box)
281     {
282         moov = box->b;
283         free(box);
284     }
285
286     if(!moov)
287         return NULL;
288
289     vlc_fourcc_t extra[] = {MAJOR_isom, VLC_FOURCC('p','i','f','f'), VLC_FOURCC('i','s','o','2'), VLC_FOURCC('s','m','o','o')};
290     box = GetFtyp(VLC_FOURCC('i','s','m','l'), 1, extra, ARRAY_SIZE(extra));
291
292     if(box)
293     {
294         block_ChainAppend(&box->b, moov);
295         moov = block_ChainGather(box->b);
296         free(box);
297     }
298
299     return moov;
300 }
301
302 SegmentChunk* ForgedInitSegment::toChunk(size_t, BaseRepresentation *rep, HTTPConnectionManager *)
303 {
304     block_t *moov = buildMoovBox();
305     if(moov)
306     {
307         MemoryChunkSource *source = new (std::nothrow) MemoryChunkSource(moov);
308         if( source )
309         {
310             SegmentChunk *chunk = new (std::nothrow) SegmentChunk(this, source, rep);
311             if( chunk )
312                 return chunk;
313             else
314                 delete source;
315         }
316     }
317     return NULL;
318 }