Hack around crash on exist of tests due to race between ~FFmpeg and __cxa_finalize.
[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 }
25 #include "ffmpeg.h"
26 #include "ffmpeg_content.h"
27 #include "exceptions.h"
28 #include "util.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 /* This should not really be a pointer, but I find that __cxa_finalize tries
39  * to destroy the mutex while a call to ~FFmpeg is in progress; this crashes
40  * with a failure of assert (!posix::pthread_mutex_destroy(&m));
41  *
42  * The hacky work-around is never to destroy the mutex...
43  */
44 boost::mutex* FFmpeg::_mutex;
45
46 FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
47         : _ffmpeg_content (c)
48         , _avio_buffer (0)
49         , _avio_buffer_size (4096)
50         , _avio_context (0)
51         , _format_context (0)
52         , _frame (0)
53         , _video_stream (-1)
54 {
55         if (!_mutex) {
56                 _mutex = new boost::mutex ();
57         }
58         
59         setup_general ();
60         setup_video ();
61         setup_audio ();
62 }
63
64 FFmpeg::~FFmpeg ()
65 {
66         boost::mutex::scoped_lock lm (*_mutex);
67
68         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
69                 AVCodecContext* context = _format_context->streams[i]->codec;
70                 if (context->codec_type == AVMEDIA_TYPE_VIDEO || context->codec_type == AVMEDIA_TYPE_AUDIO) {
71                         avcodec_close (context);
72                 }
73         }
74
75         av_frame_free (&_frame);
76         
77         avformat_close_input (&_format_context);
78 }
79
80 static int
81 avio_read_wrapper (void* data, uint8_t* buffer, int amount)
82 {
83         return reinterpret_cast<FFmpeg*>(data)->avio_read (buffer, amount);
84 }
85
86 static int64_t
87 avio_seek_wrapper (void* data, int64_t offset, int whence)
88 {
89         return reinterpret_cast<FFmpeg*>(data)->avio_seek (offset, whence);
90 }
91
92 void
93 FFmpeg::setup_general ()
94 {
95         av_register_all ();
96
97         _file_group.set_paths (_ffmpeg_content->paths ());
98         _avio_buffer = static_cast<uint8_t*> (wrapped_av_malloc (_avio_buffer_size));
99         _avio_context = avio_alloc_context (_avio_buffer, _avio_buffer_size, 0, this, avio_read_wrapper, 0, avio_seek_wrapper);
100         _format_context = avformat_alloc_context ();
101         _format_context->pb = _avio_context;
102         
103         AVDictionary* options = 0;
104         /* These durations are in microseconds, and represent how far into the content file
105            we will look for streams.
106         */
107         av_dict_set (&options, "analyzeduration", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
108         av_dict_set (&options, "probesize", lexical_cast<string> (5 * 60 * 1e6).c_str(), 0);
109         
110         if (avformat_open_input (&_format_context, 0, 0, &options) < 0) {
111                 throw OpenFileError (_ffmpeg_content->path(0).string ());
112         }
113
114         if (avformat_find_stream_info (_format_context, 0) < 0) {
115                 throw DecodeError (_("could not find stream information"));
116         }
117
118         /* Find video stream */
119
120         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
121                 AVStream* s = _format_context->streams[i];
122                 if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
123                         _video_stream = i;
124                 }
125         }
126
127         if (_video_stream < 0) {
128                 throw DecodeError (N_("could not find video stream"));
129         }
130
131         /* Hack: if the AVStreams have zero IDs, put some in.  We
132            use the IDs so that we can cope with VOBs, in which streams
133            move about in index but remain with the same ID in different
134            VOBs.  However, some files have all-zero IDs, hence this hack.
135         */
136            
137         uint32_t i = 0;
138         while (i < _format_context->nb_streams && _format_context->streams[i]->id == 0) {
139                 ++i;
140         }
141
142         if (i == _format_context->nb_streams) {
143                 /* Put in our own IDs */
144                 for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
145                         _format_context->streams[i]->id = i;
146                 }
147         }
148
149         _frame = av_frame_alloc ();
150         if (_frame == 0) {
151                 throw DecodeError (N_("could not allocate frame"));
152         }
153 }
154
155 void
156 FFmpeg::setup_video ()
157 {
158         boost::mutex::scoped_lock lm (*_mutex);
159
160         assert (_video_stream >= 0);
161         AVCodecContext* context = _format_context->streams[_video_stream]->codec;
162         AVCodec* codec = avcodec_find_decoder (context->codec_id);
163
164         if (codec == 0) {
165                 throw DecodeError (_("could not find video decoder"));
166         }
167
168         if (avcodec_open2 (context, codec, 0) < 0) {
169                 throw DecodeError (N_("could not open video decoder"));
170         }
171 }
172
173 void
174 FFmpeg::setup_audio ()
175 {
176         boost::mutex::scoped_lock lm (*_mutex);
177
178         for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
179                 AVCodecContext* context = _format_context->streams[i]->codec;
180                 if (context->codec_type != AVMEDIA_TYPE_AUDIO) {
181                         continue;
182                 }
183                 
184                 AVCodec* codec = avcodec_find_decoder (context->codec_id);
185                 if (codec == 0) {
186                         throw DecodeError (_("could not find audio decoder"));
187                 }
188                 
189                 if (avcodec_open2 (context, codec, 0) < 0) {
190                         throw DecodeError (N_("could not open audio decoder"));
191                 }
192         }
193 }
194
195
196 AVCodecContext *
197 FFmpeg::video_codec_context () const
198 {
199         return _format_context->streams[_video_stream]->codec;
200 }
201
202 AVCodecContext *
203 FFmpeg::audio_codec_context () const
204 {
205         if (!_ffmpeg_content->audio_stream ()) {
206                 return 0;
207         }
208         
209         return _ffmpeg_content->audio_stream()->stream(_format_context)->codec;
210 }
211
212 int
213 FFmpeg::avio_read (uint8_t* buffer, int const amount)
214 {
215         return _file_group.read (buffer, amount);
216 }
217
218 int64_t
219 FFmpeg::avio_seek (int64_t const pos, int whence)
220 {
221         if (whence == AVSEEK_SIZE) {
222                 return _file_group.length ();
223         }
224         
225         return _file_group.seek (pos, whence);
226 }