Fix backward compatibility.
[vuplus_transtreamproxy] / src / Mpeg.cpp
1 /*
2  * Mpeg.cpp
3  *
4  *  Created on: 2014. 6. 18.
5  *      Author: oskwon
6  */
7
8 #include "Mpeg.h"
9 #include "Http.h"
10 #include "Util.h"
11 #include "Logger.h"
12 //----------------------------------------------------------------------
13
14 void Mpeg::seek(HttpHeader &header)
15 {
16         try {
17                 off_t byte_offset = 0;
18                 std::string position = header.page_params["position"];
19                 std::string relative = header.page_params["relative"];
20                 if (position.empty() && relative.empty()) {
21                         std::string range = header.params["Range"];
22                         DEBUG("Range : %s", range.c_str());
23                         if((range.length() > 7) && (range.substr(0, 6) == "bytes=")) {
24                                 range = range.substr(6);
25                                 if(range.find('-') == (range.length() - 1)) {
26                                         byte_offset = Util::strtollu(range);
27                                         DEBUG("Range To : %s -> %llu", range.c_str(), byte_offset);
28                                 }
29                         }
30                 }
31                 else {
32                         off_t position_offset;
33                         if (!relative.empty()) {
34                                 int dur = calc_length();
35                                 DEBUG("duration : %d", dur);
36                                 position_offset = (dur * Util::strtollu(relative)) / 100;
37                         }
38                         else {
39                                 position_offset = Util::strtollu(position);
40                         }
41                         position_offset *= 90000;
42                         get_offset(byte_offset, position_offset, -1);
43                 }
44
45                 DEBUG("seek to byte_offset %llu", byte_offset);
46                 if (byte_offset > 0) {
47                         seek_absolute(byte_offset);
48                         DEBUG("seek ok");
49                 }
50         }
51         catch (...) {
52                 WARNING("seek fail.");
53         }
54 }
55 //----------------------------------------------------------------------
56
57 off_t Mpeg::seek_internal(off_t offset, int whence)
58 {
59         if (m_nrfiles < 2)
60                 return ::lseek(get_fd(), offset, whence);
61
62         switch (whence) {
63         case SEEK_SET: m_current_offset  = offset; break;
64         case SEEK_CUR: m_current_offset += offset; break;
65         case SEEK_END: m_current_offset  = m_totallength + offset; break;
66         }
67
68         if (m_current_offset < 0)
69                 m_current_offset = 0;
70         return m_current_offset;
71 }
72 //----------------------------------------------------------------------
73
74 ssize_t Mpeg::read_internal(off_t offset, void *buf, size_t count)
75 {
76         if (offset != m_current_offset) {
77                 m_current_offset = seek_internal(offset, SEEK_SET);
78                 if (m_current_offset < 0) {
79                         return m_current_offset;
80                 }
81         }
82         switch_offset(m_current_offset);
83
84         if (m_nrfiles >= 2) {
85                 if ((m_current_offset + count) > m_totallength)
86                         count = m_totallength - m_current_offset;
87                 if (count < 0) {
88                         return 0;
89                 }
90         }
91
92         int ret = ::read(get_fd(), buf, count);
93         if (ret > 0)
94                 m_current_offset = m_last_offset += ret;
95         return ret;
96 }
97 //----------------------------------------------------------------------
98
99 int Mpeg::switch_offset(off_t off)
100 {
101         if (m_splitsize) {
102                 int filenr = off / m_splitsize;
103                 if (filenr >= m_nrfiles)
104                         filenr = m_nrfiles - 1;
105 #if 0
106                 if (filenr != m_current_file) {
107                         close();
108                         m_fd = open(filenr);
109                         m_last_offset = m_base_offset = m_splitsize * filenr;
110                         m_current_file = filenr;
111                 }
112 #endif
113         }
114         else m_base_offset = 0;
115
116         return (off != m_last_offset) ? (m_last_offset = ::lseek(get_fd(), off - m_base_offset, SEEK_SET) + m_base_offset) : m_last_offset;
117 }
118 //----------------------------------------------------------------------
119
120 void Mpeg::calc_begin()
121 {
122         if (!(m_begin_valid || m_futile)) {
123                 m_offset_begin = 0;
124                 if (!get_pts(m_offset_begin, m_pts_begin, 0))
125                         m_begin_valid = 1;
126                 else m_futile = 1;
127         }
128         if (m_begin_valid) {
129                 m_end_valid = 0;
130         }
131 }
132 //----------------------------------------------------------------------
133
134 void Mpeg::calc_end()
135 {
136         off_t end = seek_internal(0, SEEK_END);
137
138         if (llabs(end - m_last_filelength) > 1*1024*1024) {
139                 m_last_filelength = end;
140                 m_end_valid = 0;
141
142                 m_futile = 0;
143         }
144
145         int maxiter = 10;
146
147         m_offset_end = m_last_filelength;
148
149         while (!(m_end_valid || m_futile)) {
150                 if (!--maxiter) {
151                         m_futile = 1;
152                         return;
153                 }
154
155                 m_offset_end -= 256*1024;
156                 if (m_offset_end < 0)
157                         m_offset_end = 0;
158
159                 off_t off = m_offset_end;
160
161                 if (!get_pts(m_offset_end, m_pts_end, 0))
162                         m_end_valid = 1;
163                 else m_offset_end = off;
164
165                 if (!m_offset_end) {
166                         m_futile = 1;
167                         break;
168                 }
169         }
170 }
171 //----------------------------------------------------------------------
172
173 int Mpeg::fix_pts(const off_t &offset, pts_t &now)
174 {
175         /* for the simple case, we assume one epoch, with up to one wrap around in the middle. */
176         calc_begin();
177         if (!m_begin_valid) {
178                 return -1;
179         }
180
181         pts_t pos = m_pts_begin;
182         if ((now < pos) && ((pos - now) < 90000 * 10)) {
183                 pos = 0;
184                 return 0;
185         }
186
187         if (now < pos) /* wrap around */
188                 now = now + 0x200000000LL - pos;
189         else now -= pos;
190
191         return 0;
192 }
193 //----------------------------------------------------------------------
194
195 void Mpeg::take_samples()
196 {
197         m_samples_taken = 1;
198         m_samples.clear();
199         int retries=2;
200         pts_t dummy = calc_length();
201
202         if (dummy <= 0)
203                 return;
204
205         int nr_samples = 30;
206         off_t bytes_per_sample = (m_offset_end - m_offset_begin) / (long long)nr_samples;
207         if (bytes_per_sample < 40*1024*1024)
208                 bytes_per_sample = 40*1024*1024;
209
210         bytes_per_sample -= bytes_per_sample % 188;
211
212         DEBUG("samples step %lld, pts begin %llx, pts end %llx, offs begin %lld, offs end %lld:",
213                         bytes_per_sample, m_pts_begin, m_pts_end, m_offset_begin, m_offset_end);
214
215         for (off_t offset = m_offset_begin; offset < m_offset_end;) {
216                 pts_t p;
217                 if (take_sample(offset, p) && retries--)
218                         continue;
219                 retries = 2;
220                 offset += bytes_per_sample;
221         }
222         m_samples[0] = m_offset_begin;
223         m_samples[m_pts_end - m_pts_begin] = m_offset_end;
224 }
225 //----------------------------------------------------------------------
226
227 /* returns 0 when a sample was taken. */
228 int Mpeg::take_sample(off_t off, pts_t &p)
229 {
230         off_t offset_org = off;
231
232         if (!get_pts(off, p, 1)) {
233                 /* as we are happily mixing PTS and PCR values (no comment, please), we might
234                    end up with some "negative" segments.
235                    so check if this new sample is between the previous and the next field*/
236                 std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(p);
237                 std::map<pts_t, off_t>::const_iterator u = l;
238
239                 if (l != m_samples.begin()) {
240                         --l;
241                         if (u != m_samples.end()) {
242                                 if ((l->second > off) || (u->second < off)) {
243                                         DEBUG("ignoring sample %lld %lld %lld (%llx %llx %llx)", l->second, off, u->second, l->first, p, u->first);
244                                         return 1;
245                                 }
246                         }
247                 }
248
249                 DEBUG("adding sample %lld: pts 0x%llx -> pos %lld (diff %lld bytes)", offset_org, p, off, off-offset_org);
250                 m_samples[p] = off;
251                 return 0;
252         }
253         return -1;
254 }
255 //----------------------------------------------------------------------
256
257 int Mpeg::calc_bitrate()
258 {
259         calc_length();
260         if (!m_begin_valid || !m_end_valid) {
261                 return -1;
262         }
263
264         pts_t len_in_pts = m_pts_end - m_pts_begin;
265
266         /* wrap around? */
267         if (len_in_pts < 0) {
268                 len_in_pts += 0x200000000LL;
269         }
270         off_t len_in_bytes = m_offset_end - m_offset_begin;
271         if (!len_in_pts) return -1;
272
273         unsigned long long bitrate = len_in_bytes * 90000 * 8 / len_in_pts;
274         if ((bitrate < 10000) || (bitrate > 100000000)) {
275                 return -1;
276         }
277         return bitrate;
278 }
279 //----------------------------------------------------------------------
280
281 int Mpeg::get_offset(off_t &offset, pts_t &pts, int marg)
282 {
283         calc_length();
284         if (!m_begin_valid || !m_end_valid) {
285                 return -1;
286         }
287
288         if (!m_samples_taken) {
289                 take_samples();
290         }
291
292         if (!m_samples.empty()) {
293                 int maxtries = 5;
294                 pts_t p = -1;
295
296                 while (maxtries--) {
297                         /* search entry before and after */
298                         std::map<pts_t, off_t>::const_iterator l = m_samples.lower_bound(pts);
299                         std::map<pts_t, off_t>::const_iterator u = l;
300
301                         if (l != m_samples.begin())
302                                 --l;
303
304                         /* we could have seeked beyond the end */
305                         if (u == m_samples.end()) {
306                                 /* use last segment for interpolation. */
307                                 if (l != m_samples.begin()) {
308                                         --u;
309                                         --l;
310                                 }
311                         }
312
313                         /* if we don't have enough points */
314                         if (u == m_samples.end())
315                                 break;
316
317                         pts_t pts_diff = u->first - l->first;
318                         off_t offset_diff = u->second - l->second;
319
320                         if (offset_diff < 0) {
321                                 DEBUG("something went wrong when taking samples.");
322                                 m_samples.clear();
323                                 take_samples();
324                                 continue;
325                         }
326                         DEBUG("using: %llx:%llx -> %llx:%llx", l->first, u->first, l->second, u->second);
327
328                         int bitrate = (pts_diff) ? (offset_diff * 90000 * 8 / pts_diff) : 0;
329                         offset = l->second;
330                         offset += ((pts - l->first) * (pts_t)bitrate) / 8ULL / 90000ULL;
331                         offset -= offset % 188;
332                         p = pts;
333
334                         if (!take_sample(offset, p)) {
335                                 int diff = (p - pts) / 90;
336                                 DEBUG("calculated diff %d ms", diff);
337
338                                 if (::abs(diff) > 300) {
339                                         DEBUG("diff to big, refining");
340                                         continue;
341                                 }
342                         }
343                         else DEBUG("no sample taken, refinement not possible.");
344                         break;
345                 }
346
347                 if (p != -1) {
348                         pts = p;
349                         DEBUG("aborting. Taking %llx as offset for %lld", offset, pts);
350                         return 0;
351                 }
352         }
353
354         int bitrate = calc_bitrate();
355         offset = pts * (pts_t)bitrate / 8ULL / 90000ULL;
356         DEBUG("fallback, bitrate=%d, results in %016llx", bitrate, offset);
357         offset -= offset % 188;
358
359         return 0;
360 }
361 //----------------------------------------------------------------------
362
363 int Mpeg::get_pts(off_t &offset, pts_t &pts, int fixed)
364 {
365         int left = 256*1024;
366
367         offset -= offset % 188;
368
369         while (left >= 188) {
370                 unsigned char packet[188];
371                 if (read_internal(offset, packet, 188) != 188) {
372                         //break;
373                         return -1;
374                 }
375                 left   -= 188;
376                 offset += 188;
377
378                 if (packet[0] != 0x47) {
379                         int i = 0;
380                         while (i < 188) {
381                                 if (packet[i] == 0x47)
382                                         break;
383                                 --offset; ++i;
384                         }
385                         continue;
386                 }
387
388                 unsigned char *payload;
389                 int pusi = !!(packet[1] & 0x40);
390
391                 /* check for adaption field */
392                 if (packet[3] & 0x20) {
393                         if (packet[4] >= 183)
394                                 continue;
395                         if (packet[4]) {
396                                 if (packet[5] & 0x10) { /* PCR present */
397                                         pts  = ((unsigned long long)(packet[ 6]&0xFF)) << 25;
398                                         pts |= ((unsigned long long)(packet[ 7]&0xFF)) << 17;
399                                         pts |= ((unsigned long long)(packet[ 8]&0xFE)) << 9;
400                                         pts |= ((unsigned long long)(packet[ 9]&0xFF)) << 1;
401                                         pts |= ((unsigned long long)(packet[10]&0x80)) >> 7;
402                                         offset -= 188;
403                                         if (fixed && fix_pts(offset, pts))
404                                                 return -1;
405                                         return 0;
406                                 }
407                         }
408                         payload = packet + packet[4] + 4 + 1;
409                 }
410                 else payload = packet + 4;
411
412                 if (!pusi) continue;
413
414                 /* somehow not a startcode. (this is invalid, since pusi was set.) ignore it. */
415                 if (payload[0] || payload[1] || (payload[2] != 1))
416                         continue;
417
418                 if (payload[3] == 0xFD) { // stream use extension mechanism defined in ISO 13818-1 Amendment 2
419                         if (payload[7] & 1) { // PES extension flag
420                                 int offs = 0;
421                                 if (payload[7] & 0x80) // pts avail
422                                         offs += 5;
423                                 if (payload[7] & 0x40) // dts avail
424                                         offs += 5;
425                                 if (payload[7] & 0x20) // escr avail
426                                         offs += 6;
427                                 if (payload[7] & 0x10) // es rate
428                                         offs += 3;
429                                 if (payload[7] & 0x8)  // dsm trickmode
430                                         offs += 1;
431                                 if (payload[7] & 0x4)  // additional copy info
432                                         offs += 1;
433                                 if (payload[7] & 0x2)  // crc
434                                         offs += 2;
435                                 if (payload[8] < offs)
436                                         continue;
437                                 uint8_t pef = payload[9+offs++]; // pes extension field
438                                 if (pef & 1) { // pes extension flag 2
439                                         if (pef & 0x80) // private data flag
440                                                 offs += 16;
441                                         if (pef & 0x40) // pack header field flag
442                                                 offs += 1;
443                                         if (pef & 0x20) // program packet sequence counter flag
444                                                 offs += 2;
445                                         if (pef & 0x10) // P-STD buffer flag
446                                                 offs += 2;
447                                         if (payload[8] < offs)
448                                                 continue;
449                                         uint8_t stream_id_extension_len = payload[9+offs++] & 0x7F;
450                                         if (stream_id_extension_len >= 1) {
451                                                 if (payload[8] < (offs + stream_id_extension_len) )
452                                                         continue;
453                                                 if (payload[9+offs] & 0x80) // stream_id_extension_bit (should not set)
454                                                         continue;
455                                                 switch (payload[9+offs]) {
456                                                 case 0x55 ... 0x5f: // VC-1
457                                                         break;
458                                                 case 0x71: // AC3 / DTS
459                                                         break;
460                                                 case 0x72: // DTS - HD
461                                                         break;
462                                                 default:
463                                                         continue;
464                                                 }
465                                         }
466                                         else continue;
467                                 }
468                                 else continue;
469                         }
470                         else continue;
471                 }
472                 /* drop non-audio, non-video packets because other streams can be non-compliant.*/
473                 else if (((payload[3] & 0xE0) != 0xC0) &&  // audio
474                              ((payload[3] & 0xF0) != 0xE0)) {  // video
475                         continue;
476                 }
477
478                 if (payload[7] & 0x80) { /* PTS */
479                         pts  = ((unsigned long long)(payload[ 9]&0xE))  << 29;
480                         pts |= ((unsigned long long)(payload[10]&0xFF)) << 22;
481                         pts |= ((unsigned long long)(payload[11]&0xFE)) << 14;
482                         pts |= ((unsigned long long)(payload[12]&0xFF)) << 7;
483                         pts |= ((unsigned long long)(payload[13]&0xFE)) >> 1;
484                         offset -= 188;
485
486                         return (fixed && fix_pts(offset, pts)) ? -1 : 0;
487                 }
488         }
489         return -1;
490 }
491 //----------------------------------------------------------------------
492
493 int Mpeg::calc_length()
494 {
495         if (m_duration <= 0) {
496                 calc_begin(); calc_end();
497                 if (!(m_begin_valid && m_end_valid))
498                         return -1;
499                 pts_t len = m_pts_end - m_pts_begin;
500
501                 if (len < 0)
502                         len += 0x200000000LL;
503
504                 len = len / 90000;
505
506                 m_duration = int(len);
507         }
508         return m_duration;
509 }
510 //----------------------------------------------------------------------