1 /*
2  * This file is part of bino, a 3D video player.
3  *
4  * Copyright (C) 2010, 2011, 2012, 2015
5  * Martin Lambers <marlam@marlam.de>
6  * Frédéric Devernay <frederic.devernay@inrialpes.fr>
7  * Joe <cuchac@email.cz>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "config.h"
24 
25 #include <limits>
26 
27 #include "base/dbg.h"
28 #include "base/exc.h"
29 #include "base/msg.h"
30 #include "base/str.h"
31 
32 #include "base/gettext.h"
33 #define _(string) gettext(string)
34 
35 #include "media_input.h"
36 
37 
media_input()38 media_input::media_input() :
39     _active_video_stream(-1), _active_audio_stream(-1), _active_subtitle_stream(-1),
40     _have_active_video_read(false), _have_active_audio_read(false), _have_active_subtitle_read(false),
41     _last_audio_data_size(0), _initial_skip(0), _duration(-1)
42 {
43 }
44 
~media_input()45 media_input::~media_input()
46 {
47 }
48 
get_video_stream(int stream,int & media_object,int & media_object_video_stream) const49 void media_input::get_video_stream(int stream, int &media_object, int &media_object_video_stream) const
50 {
51     assert(stream < video_streams());
52 
53     size_t i = 0;
54     while (_media_objects[i].video_streams() < stream + 1)
55     {
56         stream -= _media_objects[i].video_streams();
57         i++;
58     }
59     media_object = i;
60     media_object_video_stream = stream;
61 }
62 
get_audio_stream(int stream,int & media_object,int & media_object_audio_stream) const63 void media_input::get_audio_stream(int stream, int &media_object, int &media_object_audio_stream) const
64 {
65     assert(stream < audio_streams());
66 
67     size_t i = 0;
68     while (_media_objects[i].audio_streams() < stream + 1)
69     {
70         stream -= _media_objects[i].audio_streams();
71         i++;
72     }
73     media_object = i;
74     media_object_audio_stream = stream;
75 }
76 
get_subtitle_stream(int stream,int & media_object,int & media_object_subtitle_stream) const77 void media_input::get_subtitle_stream(int stream, int &media_object, int &media_object_subtitle_stream) const
78 {
79     assert(stream < subtitle_streams());
80 
81     size_t i = 0;
82     while (_media_objects[i].subtitle_streams() < stream + 1)
83     {
84         stream -= _media_objects[i].subtitle_streams();
85         i++;
86     }
87     media_object = i;
88     media_object_subtitle_stream = stream;
89 }
90 
91 // Get the basename of an URL (just the file name, without leading paths)
basename(const std::string & url)92 static std::string basename(const std::string &url)
93 {
94     size_t last_slash = url.find_last_of('/');
95     size_t last_backslash = url.find_last_of('\\');
96     size_t i = std::min(last_slash, last_backslash);
97     if (last_slash != std::string::npos && last_backslash != std::string::npos)
98     {
99         i = std::max(last_slash, last_backslash);
100     }
101     if (i == std::string::npos)
102     {
103         return url;
104     }
105     else
106     {
107         return url.substr(i + 1);
108     }
109 }
110 
open(const std::vector<std::string> & urls,const device_request & dev_request)111 void media_input::open(const std::vector<std::string> &urls, const device_request &dev_request)
112 {
113     assert(urls.size() > 0);
114 
115     // Open media objects
116     _is_device = dev_request.is_device();
117     _media_objects.resize(urls.size());
118     for (size_t i = 0; i < urls.size(); i++)
119     {
120         _media_objects[i].open(urls[i], dev_request);
121     }
122 
123     // Construct id for this input
124     _id = basename(_media_objects[0].url());
125     for (size_t i = 1; i < _media_objects.size(); i++)
126     {
127         _id += '/';
128         _id += basename(_media_objects[i].url());
129     }
130 
131     // Gather metadata
132     for (size_t i = 0; i < _media_objects.size(); i++)
133     {
134         // Note that we may have multiple identical tag names in our metadata
135         for (size_t j = 0; j < _media_objects[i].tags(); j++)
136         {
137             _tag_names.push_back(_media_objects[i].tag_name(j));
138             _tag_values.push_back(_media_objects[i].tag_value(j));
139         }
140     }
141 
142     // Gather streams and stream names
143     for (size_t i = 0; i < _media_objects.size(); i++)
144     {
145         for (int j = 0; j < _media_objects[i].video_streams(); j++)
146         {
147             _video_stream_names.push_back(_media_objects[i].video_frame_template(j).format_info());
148         }
149     }
150     if (_video_stream_names.size() > 1)
151     {
152         for (size_t i = 0; i < _video_stream_names.size(); i++)
153         {
154             _video_stream_names[i].insert(0,
155                     std::string(1, '#') + str::from(i + 1) + '/'
156                     + str::from(_video_stream_names.size()) + ": ");
157         }
158     }
159     for (size_t i = 0; i < _media_objects.size(); i++)
160     {
161         for (int j = 0; j < _media_objects[i].audio_streams(); j++)
162         {
163             _audio_stream_names.push_back(_media_objects[i].audio_blob_template(j).format_info());
164         }
165     }
166     if (_audio_stream_names.size() > 1)
167     {
168         for (size_t i = 0; i < _audio_stream_names.size(); i++)
169         {
170             _audio_stream_names[i].insert(0,
171                     std::string(1, '#') + str::from(i + 1) + '/'
172                     + str::from(_audio_stream_names.size()) + ": ");
173         }
174     }
175     for (size_t i = 0; i < _media_objects.size(); i++)
176     {
177         for (int j = 0; j < _media_objects[i].subtitle_streams(); j++)
178         {
179             _subtitle_stream_names.push_back(_media_objects[i].subtitle_box_template(j).format_info());
180         }
181     }
182     if (_subtitle_stream_names.size() > 1)
183     {
184         for (size_t i = 0; i < _subtitle_stream_names.size(); i++)
185         {
186             _subtitle_stream_names[i].insert(0,
187                     std::string(1, '#') + str::from(i + 1) + '/'
188                     + str::from(_subtitle_stream_names.size()) + ": ");
189         }
190     }
191 
192     // Set duration information
193     _duration = std::numeric_limits<int64_t>::max();
194     for (size_t i = 0; i < _media_objects.size(); i++)
195     {
196         for (int j = 0; j < _media_objects[i].video_streams(); j++)
197         {
198             int64_t d = _media_objects[i].video_duration(j);
199             if (d < _duration)
200             {
201                 _duration = d;
202             }
203         }
204         for (int j = 0; j < _media_objects[i].audio_streams(); j++)
205         {
206             int64_t d = _media_objects[i].audio_duration(j);
207             if (d < _duration)
208             {
209                 _duration = d;
210             }
211         }
212         // Ignore subtitle stream duration; it seems unreliable and is not important anyway.
213     }
214 
215     // Skip advertisement in 3dtv.at movies. Only works for single media objects.
216     try { _initial_skip = str::to<int64_t>(tag_value("StereoscopicSkip")); } catch (...) { }
217 
218     // Find stereo layout and set active video stream(s)
219     _supports_stereo_layout_separate = false;
220     if (video_streams() == 2)
221     {
222         int o0, o1, v0, v1;
223         get_video_stream(0, o0, v0);
224         get_video_stream(1, o1, v1);
225         video_frame t0 = _media_objects[o0].video_frame_template(v0);
226         video_frame t1 = _media_objects[o1].video_frame_template(v1);
227         if (t0.width == t1.width
228                 && t0.height == t1.height
229                 && (t0.aspect_ratio <= t1.aspect_ratio && t0.aspect_ratio >= t1.aspect_ratio)
230                 && t0.layout == t1.layout
231                 && t0.color_space == t1.color_space
232                 && t0.value_range == t1.value_range
233                 && t0.chroma_location == t1.chroma_location)
234         {
235             _supports_stereo_layout_separate = true;
236         }
237     }
238     if (_supports_stereo_layout_separate)
239     {
240         _active_video_stream = 0;
241         int o, s;
242         get_video_stream(_active_video_stream, o, s);
243         _video_frame = _media_objects[o].video_frame_template(s);
244         _video_frame.stereo_layout = parameters::layout_separate;
245     }
246     else if (video_streams() > 0)
247     {
248         _active_video_stream = 0;
249         int o, s;
250         get_video_stream(_active_video_stream, o, s);
251         _video_frame = _media_objects[o].video_frame_template(s);
252     }
253     else
254     {
255         _active_video_stream = -1;
256     }
257     if (_active_video_stream >= 0)
258     {
259         select_video_stream(_active_video_stream);
260     }
261 
262     // Set active audio stream
263     _active_audio_stream = (audio_streams() > 0 ? 0 : -1);
264     if (_active_audio_stream >= 0)
265     {
266         int o, s;
267         get_audio_stream(_active_audio_stream, o, s);
268         _audio_blob = _media_objects[o].audio_blob_template(s);
269         select_audio_stream(_active_audio_stream);
270     }
271 
272     // Set active subtitle stream
273     _active_subtitle_stream = -1;       // no subtitles by default
274 
275     // Print summary
276     msg::inf(_("Input:"));
277     for (int i = 0; i < video_streams(); i++)
278     {
279         int o, s;
280         get_video_stream(i, o, s);
281         msg::inf(4, _("Video %s: %s"), video_stream_name(i).c_str(),
282                 _media_objects[o].video_frame_template(s).format_name().c_str());
283     }
284     if (video_streams() == 0)
285     {
286         msg::inf(4, _("No video."));
287     }
288     for (int i = 0; i < audio_streams(); i++)
289     {
290         int o, s;
291         get_audio_stream(i, o, s);
292         msg::inf(4, _("Audio %s: %s"), audio_stream_name(i).c_str(),
293                 _media_objects[o].audio_blob_template(s).format_name().c_str());
294     }
295     if (audio_streams() == 0)
296     {
297         msg::inf(4, _("No audio."));
298     }
299     for (int i = 0; i < subtitle_streams(); i++)
300     {
301         int o, s;
302         get_subtitle_stream(i, o, s);
303         msg::inf(4, _("Subtitle %s: %s"), subtitle_stream_name(i).c_str(),
304                 _media_objects[o].subtitle_box_template(s).format_name().c_str());
305     }
306     if (subtitle_streams() == 0)
307     {
308         msg::inf(4, _("No subtitle."));
309     }
310     msg::inf(4, _("Duration: %g seconds"), duration() / 1e6f);
311     if (video_streams() > 0)
312     {
313         msg::inf(4, _("Stereo layout: %s"), parameters::stereo_layout_to_string(
314                     video_frame_template().stereo_layout, video_frame_template().stereo_layout_swap).c_str());
315     }
316 }
317 
urls() const318 size_t media_input::urls() const
319 {
320     return _media_objects.size();
321 }
322 
url(size_t i) const323 const std::string &media_input::url(size_t i) const
324 {
325     return _media_objects[i].url();
326 }
327 
id() const328 const std::string &media_input::id() const
329 {
330     return _id;
331 }
332 
is_device() const333 bool media_input::is_device() const
334 {
335     return _is_device;
336 }
337 
tags() const338 size_t media_input::tags() const
339 {
340     return _tag_names.size();
341 }
342 
tag_name(size_t i) const343 const std::string &media_input::tag_name(size_t i) const
344 {
345     assert(_tag_names.size() > i);
346     return _tag_names[i];
347 }
348 
tag_value(size_t i) const349 const std::string &media_input::tag_value(size_t i) const
350 {
351     assert(_tag_values.size() > i);
352     return _tag_values[i];
353 }
354 
tag_value(const std::string & tag_name) const355 const std::string &media_input::tag_value(const std::string &tag_name) const
356 {
357     static std::string empty;
358     for (size_t i = 0; i < _tag_names.size(); i++)
359     {
360         if (std::string(tag_name) == _tag_names[i])
361         {
362             return _tag_values[i];
363         }
364     }
365     return empty;
366 }
367 
video_frame_template() const368 const video_frame &media_input::video_frame_template() const
369 {
370     assert(_active_video_stream >= 0);
371     return _video_frame;
372 }
373 
video_frame_rate_numerator() const374 int media_input::video_frame_rate_numerator() const
375 {
376     assert(_active_video_stream >= 0);
377     int o, s;
378     get_video_stream(_active_video_stream, o, s);
379     return _media_objects[o].video_frame_rate_numerator(s);
380 }
381 
video_frame_rate_denominator() const382 int media_input::video_frame_rate_denominator() const
383 {
384     assert(_active_video_stream >= 0);
385     int o, s;
386     get_video_stream(_active_video_stream, o, s);
387     return _media_objects[o].video_frame_rate_denominator(s);
388 }
389 
video_frame_duration() const390 int64_t media_input::video_frame_duration() const
391 {
392     assert(_active_video_stream >= 0);
393     return static_cast<int64_t>(video_frame_rate_denominator()) * 1000000 / video_frame_rate_numerator();
394 }
395 
audio_blob_template() const396 const audio_blob &media_input::audio_blob_template() const
397 {
398     assert(_active_audio_stream >= 0);
399     return _audio_blob;
400 }
401 
subtitle_box_template() const402 const subtitle_box &media_input::subtitle_box_template() const
403 {
404     assert(_active_subtitle_stream >= 0);
405     return _subtitle_box;
406 }
407 
stereo_layout_is_supported(parameters::stereo_layout_t layout,bool) const408 bool media_input::stereo_layout_is_supported(parameters::stereo_layout_t layout, bool) const
409 {
410     if (video_streams() < 1)
411     {
412         return false;
413     }
414     assert(_active_video_stream >= 0);
415     assert(_active_video_stream < video_streams());
416     int o, s;
417     get_video_stream(_active_video_stream, o, s);
418     const video_frame &t = _media_objects[o].video_frame_template(s);
419     bool supported = true;
420     if (((layout == parameters::layout_left_right || layout == parameters::layout_left_right_half) && t.raw_width % 2 != 0)
421             || ((layout == parameters::layout_top_bottom || layout == parameters::layout_top_bottom_half) && t.raw_height % 2 != 0)
422             || (layout == parameters::layout_even_odd_rows && t.raw_height % 2 != 0)
423             || (layout == parameters::layout_separate && !_supports_stereo_layout_separate))
424     {
425         supported = false;
426     }
427     return supported;
428 }
429 
set_stereo_layout(parameters::stereo_layout_t layout,bool swap)430 void media_input::set_stereo_layout(parameters::stereo_layout_t layout, bool swap)
431 {
432     assert(stereo_layout_is_supported(layout, swap));
433     if (_have_active_video_read)
434     {
435         (void)finish_video_frame_read();
436     }
437     if (_have_active_audio_read)
438     {
439         (void)finish_audio_blob_read();
440     }
441     if (_have_active_subtitle_read)
442     {
443         (void)finish_subtitle_box_read();
444     }
445     int o, s;
446     get_video_stream(_active_video_stream, o, s);
447     const video_frame &t = _media_objects[o].video_frame_template(s);
448     _video_frame = t;
449     _video_frame.stereo_layout = layout;
450     _video_frame.stereo_layout_swap = swap;
451     _video_frame.set_view_dimensions();
452     // Reset active stream in case we switched to or from 'separate'.
453     select_video_stream(_active_video_stream);
454     if (layout == parameters::layout_separate)
455     {
456         // If we switched the layout to 'separate', then we have to seek to the
457         // position of the first video stream, or else the second video stream
458         // is out of sync.
459         int64_t pos = _media_objects[o].tell();
460         if (pos > std::numeric_limits<int64_t>::min())
461         {
462             seek(pos);
463         }
464     }
465 }
466 
select_video_stream(int video_stream)467 void media_input::select_video_stream(int video_stream)
468 {
469     if (_have_active_video_read)
470     {
471         (void)finish_video_frame_read();
472     }
473     if (_have_active_audio_read)
474     {
475         (void)finish_audio_blob_read();
476     }
477     if (_have_active_subtitle_read)
478     {
479         (void)finish_subtitle_box_read();
480     }
481     assert(video_stream >= 0);
482     assert(video_stream < video_streams());
483     if (_video_frame.stereo_layout == parameters::layout_separate)
484     {
485         _active_video_stream = 0;
486         for (size_t i = 0; i < _media_objects.size(); i++)
487         {
488             for (int j = 0; j < _media_objects[i].video_streams(); j++)
489             {
490                 _media_objects[i].video_stream_set_active(j, true);
491             }
492         }
493     }
494     else
495     {
496         _active_video_stream = video_stream;
497         int o, s;
498         get_video_stream(_active_video_stream, o, s);
499         for (size_t i = 0; i < _media_objects.size(); i++)
500         {
501             for (int j = 0; j < _media_objects[i].video_streams(); j++)
502             {
503                 _media_objects[i].video_stream_set_active(j, (i == static_cast<size_t>(o) && j == s));
504             }
505         }
506     }
507     // Re-set video frame template
508     parameters::stereo_layout_t stereo_layout_bak = _video_frame.stereo_layout;
509     bool stereo_layout_swap_bak = _video_frame.stereo_layout_swap;
510     int o, s;
511     get_video_stream(_active_video_stream, o, s);
512     _video_frame = _media_objects[o].video_frame_template(s);
513     _video_frame.stereo_layout = stereo_layout_bak;
514     _video_frame.stereo_layout_swap = stereo_layout_swap_bak;
515     _video_frame.set_view_dimensions();
516 }
517 
select_audio_stream(int audio_stream)518 void media_input::select_audio_stream(int audio_stream)
519 {
520     if (_have_active_video_read)
521     {
522         (void)finish_video_frame_read();
523     }
524     if (_have_active_audio_read)
525     {
526         (void)finish_audio_blob_read();
527     }
528     if (_have_active_subtitle_read)
529     {
530         (void)finish_subtitle_box_read();
531     }
532     assert(audio_stream >= 0);
533     assert(audio_stream < audio_streams());
534     _active_audio_stream = audio_stream;
535     int o, s;
536     get_audio_stream(_active_audio_stream, o, s);
537     for (size_t i = 0; i < _media_objects.size(); i++)
538     {
539         for (int j = 0; j < _media_objects[i].audio_streams(); j++)
540         {
541             _media_objects[i].audio_stream_set_active(j, (i == static_cast<size_t>(o) && j == s));
542         }
543     }
544     // Re-set audio blob template
545     _audio_blob = _media_objects[o].audio_blob_template(s);
546 }
547 
select_subtitle_stream(int subtitle_stream)548 void media_input::select_subtitle_stream(int subtitle_stream)
549 {
550     if (_have_active_video_read)
551     {
552         (void)finish_video_frame_read();
553     }
554     if (_have_active_audio_read)
555     {
556         (void)finish_audio_blob_read();
557     }
558     if (_have_active_subtitle_read)
559     {
560         (void)finish_subtitle_box_read();
561     }
562     assert(subtitle_stream >= -1);
563     assert(subtitle_stream < subtitle_streams());
564     _active_subtitle_stream = subtitle_stream;
565     int o = -1, s = -1;
566     if (_active_subtitle_stream >= 0)
567     {
568         get_subtitle_stream(_active_subtitle_stream, o, s);
569     }
570     for (size_t i = 0; i < _media_objects.size(); i++)
571     {
572         for (int j = 0; j < _media_objects[i].subtitle_streams(); j++)
573         {
574             _media_objects[i].subtitle_stream_set_active(j, (i == static_cast<size_t>(o) && j == s));
575         }
576     }
577     // Re-set subtitle box template
578     if (_active_subtitle_stream >= 0)
579         _subtitle_box = _media_objects[o].subtitle_box_template(s);
580     else
581         _subtitle_box = subtitle_box();
582 }
583 
start_video_frame_read()584 void media_input::start_video_frame_read()
585 {
586     assert(_active_video_stream >= 0);
587     if (_have_active_video_read)
588     {
589         return;
590     }
591     if (_video_frame.stereo_layout == parameters::layout_separate)
592     {
593         int o0, s0, o1, s1;
594         get_video_stream(0, o0, s0);
595         get_video_stream(1, o1, s1);
596         _media_objects[o0].start_video_frame_read(s0, 1);
597         _media_objects[o1].start_video_frame_read(s1, 1);
598     }
599     else
600     {
601         int o, s;
602         get_video_stream(_active_video_stream, o, s);
603         _media_objects[o].start_video_frame_read(s,
604                 _video_frame.stereo_layout == parameters::layout_alternating ? 2 : 1);
605     }
606     _have_active_video_read = true;
607 }
608 
finish_video_frame_read()609 video_frame media_input::finish_video_frame_read()
610 {
611     assert(_active_video_stream >= 0);
612     if (!_have_active_video_read)
613     {
614         start_video_frame_read();
615     }
616     video_frame frame;
617     if (_video_frame.stereo_layout == parameters::layout_separate)
618     {
619         int o0, s0, o1, s1;
620         get_video_stream(0, o0, s0);
621         get_video_stream(1, o1, s1);
622         video_frame f0 = _media_objects[o0].finish_video_frame_read(s0);
623         video_frame f1 = _media_objects[o1].finish_video_frame_read(s1);
624         if (f0.is_valid() && f1.is_valid())
625         {
626             frame = _video_frame;
627             for (int p = 0; p < 3; p++)
628             {
629                 frame.data[0][p] = f0.data[0][p];
630                 frame.data[1][p] = f1.data[0][p];
631                 frame.line_size[0][p] = f0.line_size[0][p];
632                 frame.line_size[1][p] = f1.line_size[0][p];
633             }
634             frame.presentation_time = f0.presentation_time;
635         }
636     }
637     else
638     {
639         int o, s;
640         get_video_stream(_active_video_stream, o, s);
641         video_frame f = _media_objects[o].finish_video_frame_read(s);
642         if (f.is_valid())
643         {
644             frame = _video_frame;
645             for (int v = 0; v < 2; v++)
646             {
647                 for (int p = 0; p < 3; p++)
648                 {
649                     frame.data[v][p] = f.data[v][p];
650                     frame.line_size[v][p] = f.line_size[v][p];
651                 }
652             }
653             frame.presentation_time = f.presentation_time;
654         }
655     }
656     _have_active_video_read = false;
657     return frame;
658 }
659 
start_audio_blob_read(size_t size)660 void media_input::start_audio_blob_read(size_t size)
661 {
662     assert(_active_audio_stream >= 0);
663     if (_have_active_audio_read)
664     {
665         return;
666     }
667     int o, s;
668     get_audio_stream(_active_audio_stream, o, s);
669     _media_objects[o].start_audio_blob_read(s, size);
670     _last_audio_data_size = size;
671     _have_active_audio_read = true;
672 }
673 
finish_audio_blob_read()674 audio_blob media_input::finish_audio_blob_read()
675 {
676     assert(_active_audio_stream >= 0);
677     int o, s;
678     get_audio_stream(_active_audio_stream, o, s);
679     if (!_have_active_audio_read)
680     {
681         start_audio_blob_read(_last_audio_data_size);
682     }
683     _have_active_audio_read = false;
684     return _media_objects[o].finish_audio_blob_read(s);
685 }
686 
start_subtitle_box_read()687 void media_input::start_subtitle_box_read()
688 {
689     assert(_active_subtitle_stream >= 0);
690     if (_have_active_subtitle_read)
691     {
692         return;
693     }
694     int o, s;
695     get_subtitle_stream(_active_subtitle_stream, o, s);
696     _media_objects[o].start_subtitle_box_read(s);
697     _have_active_subtitle_read = true;
698 }
699 
finish_subtitle_box_read()700 subtitle_box media_input::finish_subtitle_box_read()
701 {
702     assert(_active_subtitle_stream >= 0);
703     int o, s;
704     get_subtitle_stream(_active_subtitle_stream, o, s);
705     if (!_have_active_subtitle_read)
706     {
707         start_subtitle_box_read();
708     }
709     _have_active_subtitle_read = false;
710     return _media_objects[o].finish_subtitle_box_read(s);
711 }
712 
tell()713 int64_t media_input::tell()
714 {
715     int64_t pos = std::numeric_limits<int64_t>::min();
716     int o, s;
717     if (_active_audio_stream >= 0)
718     {
719         get_audio_stream(_active_audio_stream, o, s);
720         pos = _media_objects[o].tell();
721     }
722     else if (_active_video_stream >= 0)
723     {
724         get_video_stream(_active_video_stream, o, s);
725         pos = _media_objects[o].tell();
726     }
727     return pos;
728 }
729 
seek(int64_t pos)730 void media_input::seek(int64_t pos)
731 {
732     if (_have_active_video_read)
733     {
734         (void)finish_video_frame_read();
735     }
736     if (_have_active_audio_read)
737     {
738         (void)finish_audio_blob_read();
739     }
740     if (_have_active_subtitle_read)
741     {
742         (void)finish_subtitle_box_read();
743     }
744     for (size_t i = 0; i < _media_objects.size(); i++)
745     {
746         _media_objects[i].seek(pos);
747     }
748 }
749 
close()750 void media_input::close()
751 {
752     try
753     {
754         if (_have_active_video_read)
755         {
756             (void)finish_video_frame_read();
757         }
758         if (_have_active_audio_read)
759         {
760             (void)finish_audio_blob_read();
761         }
762         if (_have_active_subtitle_read)
763         {
764             (void)finish_subtitle_box_read();
765         }
766         for (size_t i = 0; i < _media_objects.size(); i++)
767         {
768             _media_objects[i].close();
769         }
770     }
771     catch (...)
772     {
773     }
774     _is_device = false;
775     _id = "";
776     _media_objects.clear();
777     _tag_names.clear();
778     _tag_values.clear();
779     _video_stream_names.clear();
780     _audio_stream_names.clear();
781     _subtitle_stream_names.clear();
782     _active_video_stream = -1;
783     _active_audio_stream = -1;
784     _active_subtitle_stream = -1;
785     _initial_skip = 0;
786     _duration = -1;
787     _video_frame = video_frame();
788     _audio_blob = audio_blob();
789     _subtitle_box = subtitle_box();
790 }
791