Tidy up a bit.
[dcpomatic.git] / src / lib / ffmpeg.cc
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 extern "C" {
21 #include <libavcodec/avcodec.h>
22 #include <libavformat/avformat.h>
23 #include <libswscale/swscale.h>
24 #include <libpostproc/postprocess.h>
25 }
26 #include "ffmpeg.h"
27 #include "ffmpeg_content.h"
28 #include "exceptions.h"
29
30 #include "i18n.h"
31
32 using std::string;
33 using std::cout;
34 using std::stringstream;
35 using boost::shared_ptr;
36 using boost::lexical_cast;
37
38 boost::mutex FFmpeg::_mutex;
39
40 /** @param long_probe true to do a long probe of the file looking for streams */
41 FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c, bool long_probe)
42         : _ffmpeg_content (c)
43         , _avio_buffer (0)
44         , _avio_buffer_size (4096)
45         , _avio_context (0)
46         , _format_context (0)
47         , _frame (0)
48         , _video_stream (-1)
49 {
50         setup_general (long_probe);
51         setup_video ();
52         setup_audio ();
53 }
54
55 FFmpeg::~FFmpeg ()
56 {
57         boost::mutex::scoped_lock lm (_mutex);
58
59         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
60                 AVCodecContext* context = _format_context->streams[i]->codec;
61                 if (context->codec_type == AVMEDIA_TYPE_VIDEO || context->codec_type == AVMEDIA_TYPE_AUDIO) {
62                         avcodec_close (context);
63                 }
64         }
65
66         avcodec_free_frame (&_frame);
67         
68         avformat_close_input (&_format_context);
69 }
70
71 static int
72 avio_read_wrapper (void* data, uint8_t* buffer, int amount)
73 {
74         return reinterpret_cast<FFmpeg*>(data)->avio_read (buffer, amount);
75 }
76
77 static int64_t
78 avio_seek_wrapper (void* data, int64_t offset, int whence)
79 {
80         return reinterpret_cast<FFmpeg*>(data)->avio_seek (offset, whence);
81 }
82
83 void
84 FFmpeg::setup_general (bool long_probe)
85 {
86         av_register_all ();
87
88         _file_group.set_paths (_ffmpeg_content->paths ());
89         _avio_buffer = static_cast<uint8_t*> (av_malloc (_avio_buffer_size));
90         _avio_context = avio_alloc_context (_avio_buffer, _avio_buffer_size, 0, this, avio_read_wrapper, 0, avio_seek_wrapper);
91         _format_context = avformat_alloc_context ();
92         _format_context->pb = _avio_context;
93         
94         AVDictionary* options = 0;
95         if (long_probe) {
96                 /* These durations are in microseconds, and represent how far into the content file
97                    we will look for streams.
98                 */
99                 av_dict_set (&options, "analyzeduration", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
100                 av_dict_set (&options, "probesize", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
101         }
102         
103         if (avformat_open_input (&_format_context, 0, 0, &options) < 0) {
104                 throw OpenFileError (_ffmpeg_content->path(0).string ());
105         }
106
107         if (avformat_find_stream_info (_format_context, 0) < 0) {
108                 throw DecodeError (_("could not find stream information"));
109         }
110
111         /* Find video stream */
112
113         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
114                 AVStream* s = _format_context->streams[i];
115                 if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
116                         _video_stream = i;
117                 }
118         }
119
120         if (_video_stream < 0) {
121                 throw DecodeError (N_("could not find video stream"));
122         }
123
124         _frame = avcodec_alloc_frame ();
125         if (_frame == 0) {
126                 throw DecodeError (N_("could not allocate frame"));
127         }
128 }
129
130 void
131 FFmpeg::setup_video ()
132 {
133         boost::mutex::scoped_lock lm (_mutex);
134         
135         AVCodecContext* context = _format_context->streams[_video_stream]->codec;
136         AVCodec* codec = avcodec_find_decoder (context->codec_id);
137
138         if (codec == 0) {
139                 throw DecodeError (_("could not find video decoder"));
140         }
141
142         if (avcodec_open2 (context, codec, 0) < 0) {
143                 throw DecodeError (N_("could not open video decoder"));
144         }
145 }
146
147 void
148 FFmpeg::setup_audio ()
149 {
150         boost::mutex::scoped_lock lm (_mutex);
151
152         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
153                 AVCodecContext* context = _format_context->streams[i]->codec;
154                 if (context->codec_type != AVMEDIA_TYPE_AUDIO) {
155                         continue;
156                 }
157                 
158                 AVCodec* codec = avcodec_find_decoder (context->codec_id);
159                 if (codec == 0) {
160                         throw DecodeError (_("could not find audio decoder"));
161                 }
162                 
163                 if (avcodec_open2 (context, codec, 0) < 0) {
164                         throw DecodeError (N_("could not open audio decoder"));
165                 }
166         }
167 }
168
169
170 AVCodecContext *
171 FFmpeg::video_codec_context () const
172 {
173         return _format_context->streams[_video_stream]->codec;
174 }
175
176 AVCodecContext *
177 FFmpeg::audio_codec_context () const
178 {
179         return _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec;
180 }
181
182 int
183 FFmpeg::avio_read (uint8_t* buffer, int const amount)
184 {
185         return _file_group.read (buffer, amount);
186 }
187
188 int64_t
189 FFmpeg::avio_seek (int64_t const pos, int whence)
190 {
191         if (whence == AVSEEK_SIZE) {
192                 return _file_group.length ();
193         }
194         
195         return _file_group.seek (pos, whence);
196 }