Result (boost::shared_ptr<Image> image_, int log2_scaling_)
: image (image_)
, log2_scaling (log2_scaling_)
+ , error (false)
+ {}
+
+ Result (boost::shared_ptr<Image> image_, int log2_scaling_, bool error_)
+ : image (image_)
+ , log2_scaling (log2_scaling_)
+ , error (error_)
{}
/** Image (which will be aligned) */
* will be 1.
*/
int log2_scaling;
+ /** true if there was an error during image decoding, otherwise false */
+ bool error;
};
/** @param log Log to write to, or 0.
: _data (path)
, _size (size)
, _pixel_format (pixel_format)
+ , _error (false)
{
/* ::image assumes 16bpp */
DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
, _size (size)
, _pixel_format (pixel_format)
, _forced_reduction (forced_reduction)
+ , _error (false)
{
/* ::image assumes 16bpp */
DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
, _eye (eye)
, _pixel_format (pixel_format)
, _forced_reduction (forced_reduction)
+ , _error (false)
{
/* ::image assumes 16bpp */
DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
}
J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
+ : _error (false)
{
_size = dcp::Size (xml->number_child<int> ("Width"), xml->number_child<int> ("Height"));
if (xml->optional_number_child<int> ("Eye")) {
reduce = max (0, reduce);
}
- shared_ptr<dcp::OpenJPEGImage> decompressed = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
- _image.reset (new Image (_pixel_format, decompressed->size(), true));
-
- int const shift = 16 - decompressed->precision (0);
-
- /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
- the data is 12-bit either way.
- */
-
- int const width = decompressed->size().width;
-
- int p = 0;
- int* decomp_0 = decompressed->data (0);
- int* decomp_1 = decompressed->data (1);
- int* decomp_2 = decompressed->data (2);
- for (int y = 0; y < decompressed->size().height; ++y) {
- uint16_t* q = (uint16_t *) (_image->data()[0] + y * _image->stride()[0]);
- for (int x = 0; x < width; ++x) {
- *q++ = decomp_0[p] << shift;
- *q++ = decomp_1[p] << shift;
- *q++ = decomp_2[p] << shift;
- ++p;
+ try {
+ shared_ptr<dcp::OpenJPEGImage> decompressed = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
+ _image.reset (new Image (_pixel_format, decompressed->size(), true));
+
+ int const shift = 16 - decompressed->precision (0);
+
+ /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
+ the data is 12-bit either way.
+ */
+
+ int const width = decompressed->size().width;
+
+ int p = 0;
+ int* decomp_0 = decompressed->data (0);
+ int* decomp_1 = decompressed->data (1);
+ int* decomp_2 = decompressed->data (2);
+ for (int y = 0; y < decompressed->size().height; ++y) {
+ uint16_t* q = (uint16_t *) (_image->data()[0] + y * _image->stride()[0]);
+ for (int x = 0; x < width; ++x) {
+ *q++ = decomp_0[p] << shift;
+ *q++ = decomp_1[p] << shift;
+ *q++ = decomp_2[p] << shift;
+ ++p;
+ }
}
+ } catch (dcp::J2KDecompressionError& e) {
+ _image.reset (new Image (_pixel_format, _size, true));
+ _image->make_black ();
+ _error = true;
}
_target_size = target_size;
J2KImageProxy::image (optional<dcp::Size> target_size) const
{
int const r = prepare (target_size);
+
/* I think this is safe without a lock on mutex. _image is guaranteed to be
set up when prepare() has happened.
*/
- return Result (_image, r);
+ return Result (_image, r, _error);
}
+
void
J2KImageProxy::add_metadata (xmlpp::Node* node) const
{
AVPixelFormat _pixel_format;
mutable boost::mutex _mutex;
boost::optional<int> _forced_reduction;
+ /** true if an error occurred while decoding the JPEG2000 data, false if not */
+ mutable bool _error;
};
PresetColourConversion::all().front().conversion,
VIDEO_RANGE_FULL,
boost::weak_ptr<Content>(),
- boost::optional<Frame>()
+ boost::optional<Frame>(),
+ false
)
);
}
piece->content->video->colour_conversion(),
piece->content->video->range(),
piece->content,
- video.frame
+ video.frame,
+ false
)
);
optional<ColourConversion> colour_conversion,
VideoRange video_range,
weak_ptr<Content> content,
- optional<Frame> video_frame
+ optional<Frame> video_frame,
+ bool error
)
: _in (in)
, _crop (crop)
, _video_range (video_range)
, _content (content)
, _video_frame (video_frame)
+ , _error (error)
{
}
_eyes = (Eyes) node->number_child<int> ("Eyes");
_part = (Part) node->number_child<int> ("Part");
_video_range = (VideoRange) node->number_child<int>("VideoRange");
+ _error = node->optional_bool_child("Error").get_value_or (false);
/* Assume that the ColourConversion uses the current state version */
_colour_conversion = ColourConversion::from_xml (node, Film::current_state_version);
_image_fade = _fade;
ImageProxy::Result prox = _in->image (_inter_size);
+ _error = prox.error;
Crop total_crop = _crop;
switch (_part) {
node->add_child("Eyes")->add_child_text (raw_convert<string> (static_cast<int> (_eyes)));
node->add_child("Part")->add_child_text (raw_convert<string> (static_cast<int> (_part)));
node->add_child("VideoRange")->add_child_text(raw_convert<string>(static_cast<int>(_video_range)));
+ node->add_child("Error")->add_child_text(_error ? "1" : "0");
if (_colour_conversion) {
_colour_conversion.get().as_xml (node);
}
_colour_conversion,
_video_range,
_content,
- _video_frame
+ _video_frame,
+ _error
)
);
}
boost::optional<ColourConversion>,
VideoRange video_range,
boost::weak_ptr<Content>,
- boost::optional<Frame>
+ boost::optional<Frame>,
+ bool error
);
PlayerVideo (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>);
return _content;
}
+ bool error () const {
+ return _error;
+ }
+
private:
void make_image (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
mutable dcp::Size _image_out_size;
/** _fade that was used to make _image */
mutable boost::optional<double> _image_fade;
+ /** true if there was an error when decoding our image */
+ mutable bool _error;
};
#endif
return _video_view->dropped ();
}
+
+int
+FilmViewer::errored () const
+{
+ return _video_view->errored ();
+}
+
+
int
FilmViewer::gets () const
{
boost::optional<dcpomatic::DCPTime> audio_time () const;
int dropped () const;
+ int errored () const;
int gets () const;
int audio_callback (void* out, unsigned int frames);
{
shared_ptr<FilmViewer> fv = _viewer.lock ();
if (fv) {
- checked_set (_dropped, wxString::Format(_("Dropped frames: %d"), fv->dropped()));
+ wxString s = wxString::Format(_("Dropped frames: %d"), fv->dropped() + fv->errored());
+ if (fv->errored() == 1) {
+ s += wxString::Format(_(" (%d error)"), fv->errored());
+ } else if (fv->errored() > 1) {
+ s += wxString::Format(_(" (%d errors)"), fv->errored());
+ }
+ checked_set (_dropped, s);
}
}
, _eyes (EYES_LEFT)
, _three_d (false)
, _dropped (0)
+ , _errored (0)
, _gets (0)
{
_player_video.first->eyes() != EYES_BOTH
);
+ if (_player_video.first && _player_video.first->error()) {
+ ++_errored;
+ }
+
return true;
}
{
boost::mutex::scoped_lock lm (_mutex);
_dropped = 0;
+ _errored = 0;
}
bool
return _dropped;
}
+ int errored () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _errored;
+ }
+
int gets () const {
boost::mutex::scoped_lock lm (_mutex);
return _gets;
bool _three_d;
int _dropped;
+ int _errored;
int _gets;
};
ColourConversion(),
VIDEO_RANGE_FULL,
weak_ptr<Content>(),
- optional<Frame>()
+ optional<Frame>(),
+ false
)
);
ColourConversion(),
VIDEO_RANGE_FULL,
weak_ptr<Content>(),
- optional<Frame>()
+ optional<Frame>(),
+ false
)
);
ColourConversion(),
VIDEO_RANGE_FULL,
weak_ptr<Content>(),
- optional<Frame>()
+ optional<Frame>(),
+ false
)
);
PresetColourConversion::all().front().conversion,
VIDEO_RANGE_FULL,
weak_ptr<Content>(),
- optional<Frame>()
+ optional<Frame>(),
+ false
)
);