+string
+FFmpegDecoder::stream_name (AVStream* s) const
+{
+ stringstream n;
+
+ AVDictionaryEntry const * lang = av_dict_get (s->metadata, N_("language"), 0, 0);
+ if (lang) {
+ n << lang->value;
+ }
+
+ AVDictionaryEntry const * title = av_dict_get (s->metadata, N_("title"), 0, 0);
+ if (title) {
+ if (!n.str().empty()) {
+ n << N_(" ");
+ }
+ n << title->value;
+ }
+
+ if (n.str().empty()) {
+ n << N_("unknown");
+ }
+
+ return n.str ();
+}
+
+int
+FFmpegDecoder::bytes_per_audio_sample () const
+{
+ return av_get_bytes_per_sample (audio_sample_format ());
+}
+
+void
+FFmpegDecoder::set_audio_stream (shared_ptr<AudioStream> s)
+{
+ AudioDecoder::set_audio_stream (s);
+ setup_audio ();
+}
+
+void
+FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
+{
+ VideoDecoder::set_subtitle_stream (s);
+ setup_subtitle ();
+ OutputChanged ();
+}
+
+void
+FFmpegDecoder::filter_and_emit_video (AVFrame* frame)
+{
+ boost::mutex::scoped_lock lm (_filter_graphs_mutex);
+
+ shared_ptr<FilterGraph> graph;
+
+ list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
+ while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format)) {
+ ++i;
+ }
+
+ if (i == _filter_graphs.end ()) {
+ graph.reset (new FilterGraph (_film, this, libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format));
+ _filter_graphs.push_back (graph);
+ _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), frame->width, frame->height, frame->format));
+ } else {
+ graph = *i;
+ }
+
+ list<shared_ptr<Image> > images = graph->process (frame);
+
+ for (list<shared_ptr<Image> >::iterator i = images.begin(); i != images.end(); ++i) {
+ emit_video (*i, frame_time ());
+ }
+}
+
+bool
+FFmpegDecoder::seek (double p)
+{
+ return do_seek (p, false);
+}
+