X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fffmpeg_image_proxy.cc;h=e7d5b424dc0da3bdb052eab77f14c90f3fd0716b;hb=6cac49dc50dae6b173135df101d532f20031ca70;hp=e38b80be1942085d9160891a29edff428fd3425a;hpb=e5b744922fb6aed65ec13f22a9de0c86dd1bd561;p=dcpomatic.git diff --git a/src/lib/ffmpeg_image_proxy.cc b/src/lib/ffmpeg_image_proxy.cc index e38b80be1..e7d5b424d 100644 --- a/src/lib/ffmpeg_image_proxy.cc +++ b/src/lib/ffmpeg_image_proxy.cc @@ -18,33 +18,41 @@ */ -#include "ffmpeg_image_proxy.h" + +#include "compose.hpp" #include "cross.h" -#include "exceptions.h" #include "dcpomatic_socket.h" +#include "exceptions.h" +#include "ffmpeg_image_proxy.h" #include "image.h" -#include "compose.hpp" #include "util.h" +#include "warnings.h" #include +DCPOMATIC_DISABLE_WARNINGS extern "C" { #include #include +#include } #include +DCPOMATIC_ENABLE_WARNINGS #include #include "i18n.h" -using std::string; + using std::cout; -using std::pair; -using std::min; using std::make_pair; -using boost::shared_ptr; +using std::make_shared; +using std::min; +using std::pair; +using std::shared_ptr; +using std::string; using boost::optional; -using boost::dynamic_pointer_cast; +using std::dynamic_pointer_cast; using dcp::raw_convert; + FFmpegImageProxy::FFmpegImageProxy (boost::filesystem::path path) : _data (path) , _pos (0) @@ -53,19 +61,19 @@ FFmpegImageProxy::FFmpegImageProxy (boost::filesystem::path path) } -FFmpegImageProxy::FFmpegImageProxy (dcp::Data data) +FFmpegImageProxy::FFmpegImageProxy (dcp::ArrayData data) : _data (data) , _pos (0) { } -FFmpegImageProxy::FFmpegImageProxy (shared_ptr, shared_ptr socket) +FFmpegImageProxy::FFmpegImageProxy (shared_ptr socket) : _pos (0) { uint32_t const size = socket->read_uint32 (); - _data = dcp::Data (size); - socket->read (_data.data().get(), size); + _data = dcp::ArrayData (size); + socket->read (_data.data(), size); } static int @@ -83,8 +91,11 @@ avio_seek_wrapper (void* data, int64_t offset, int whence) int FFmpegImageProxy::avio_read (uint8_t* buffer, int const amount) { - int const to_do = min(int64_t(amount), _data.size() - _pos); - memcpy (buffer, _data.data().get() + _pos, to_do); + int const to_do = min(static_cast(amount), static_cast(_data.size()) - _pos); + if (to_do == 0) { + return AVERROR_EOF; + } + memcpy (buffer, _data.data() + _pos, to_do); _pos += to_do; return to_do; } @@ -109,21 +120,24 @@ FFmpegImageProxy::avio_seek (int64_t const pos, int whence) return _pos; } -pair, int> + +ImageProxy::Result FFmpegImageProxy::image (optional) const { + auto constexpr name_for_errors = "FFmpegImageProxy::image"; + boost::mutex::scoped_lock lm (_mutex); if (_image) { - return make_pair (_image, 0); + return Result (_image, 0); } uint8_t* avio_buffer = static_cast (wrapped_av_malloc(4096)); - AVIOContext* avio_context = avio_alloc_context (avio_buffer, 4096, 0, const_cast(this), avio_read_wrapper, 0, avio_seek_wrapper); + auto avio_context = avio_alloc_context (avio_buffer, 4096, 0, const_cast(this), avio_read_wrapper, 0, avio_seek_wrapper); AVFormatContext* format_context = avformat_alloc_context (); format_context->pb = avio_context; - AVDictionary* options = 0; + AVDictionary* options = nullptr; /* These durations are in microseconds, and represent how far into the content file we will look for streams. */ @@ -131,50 +145,79 @@ FFmpegImageProxy::image (optional) const av_dict_set (&options, "probesize", raw_convert(5 * 60 * 1000000).c_str(), 0); int e = avformat_open_input (&format_context, 0, 0, &options); + if ((e < 0 && e == AVERROR_INVALIDDATA) || (e >= 0 && format_context->probe_score <= 25)) { + /* Hack to fix loading of .tga files through AVIOContexts (rather then + directly from the file). This code just does enough to allow the + probe code to take a hint from "foo.tga" and so try targa format. + */ + auto f = av_find_input_format ("image2"); + format_context = avformat_alloc_context (); + format_context->pb = avio_context; + format_context->iformat = f; + e = avformat_open_input (&format_context, "foo.tga", f, &options); + } if (e < 0) { - throw OpenFileError (_path->string(), e, true); + if (_path) { + throw OpenFileError (_path->string(), e, OpenFileError::READ); + } else { + boost::throw_exception(DecodeError(String::compose(_("Could not decode image (%1)"), e))); + } } - if (avformat_find_stream_info(format_context, 0) < 0) { - throw DecodeError (_("could not find stream information")); + int r = avformat_find_stream_info(format_context, 0); + if (r < 0) { + throw DecodeError (N_("avcodec_find_stream_info"), name_for_errors, r, *_path); } DCPOMATIC_ASSERT (format_context->nb_streams == 1); - AVFrame* frame = av_frame_alloc (); + auto frame = av_frame_alloc (); if (!frame) { - throw DecodeError (N_("could not allocate frame")); + std::bad_alloc (); } - AVCodecContext* codec_context = format_context->streams[0]->codec; - AVCodec* codec = avcodec_find_decoder (codec_context->codec_id); + auto codec = avcodec_find_decoder (format_context->streams[0]->codecpar->codec_id); DCPOMATIC_ASSERT (codec); - if (avcodec_open2 (codec_context, codec, 0) < 0) { - throw DecodeError (N_("could not open decoder")); + auto context = avcodec_alloc_context3 (codec); + if (!context) { + throw DecodeError (N_("avcodec_alloc_context3"), name_for_errors, *_path); + } + + r = avcodec_open2 (context, codec, 0); + if (r < 0) { + throw DecodeError (N_("avcodec_open2"), name_for_errors, r, *_path); } AVPacket packet; - int r = av_read_frame (format_context, &packet); + r = av_read_frame (format_context, &packet); if (r < 0) { - throw DecodeError (N_("could not read frame")); + throw DecodeError (N_("av_read_frame"), name_for_errors, r, *_path); } - int frame_finished; - if (avcodec_decode_video2(codec_context, frame, &frame_finished, &packet) < 0 || !frame_finished) { - throw DecodeError (N_("could not decode video")); + r = avcodec_send_packet (context, &packet); + if (r < 0) { + throw DecodeError (N_("avcodec_send_packet"), name_for_errors, r, *_path); } - _image.reset (new Image (frame)); + r = avcodec_receive_frame (context, frame); + if (r < 0) { + throw DecodeError (N_("avcodec_receive_frame"), name_for_errors, r, *_path); + } + _image = make_shared(frame); + + av_packet_unref (&packet); av_frame_free (&frame); + avcodec_free_context (&context); avformat_close_input (&format_context); av_free (avio_context->buffer); av_free (avio_context); - return make_pair (_image, 0); + return Result (_image, 0); } + void FFmpegImageProxy::add_metadata (xmlpp::Node* node) const { @@ -182,25 +225,21 @@ FFmpegImageProxy::add_metadata (xmlpp::Node* node) const } void -FFmpegImageProxy::send_binary (shared_ptr socket) const +FFmpegImageProxy::write_to_socket (shared_ptr socket) const { socket->write (_data.size()); - socket->write (_data.data().get(), _data.size()); + socket->write (_data.data(), _data.size()); } bool FFmpegImageProxy::same (shared_ptr other) const { - shared_ptr mp = dynamic_pointer_cast (other); + auto mp = dynamic_pointer_cast(other); if (!mp) { return false; } - if (_data.size() != mp->_data.size()) { - return false; - } - - return memcmp (_data.data().get(), mp->_data.data().get(), _data.size()) == 0; + return _data == mp->_data; } size_t