video_frame media_input::finish_video_frame_read() { assert(_active_video_stream >= 0); if (!_have_active_video_read) { start_video_frame_read(); } video_frame frame; if (_video_frame.stereo_layout == parameters::layout_separate) { int o0, s0, o1, s1; get_video_stream(0, o0, s0); get_video_stream(1, o1, s1); video_frame f0 = _media_objects[o0].finish_video_frame_read(s0); video_frame f1 = _media_objects[o1].finish_video_frame_read(s1); if (f0.is_valid() && f1.is_valid()) { frame = _video_frame; for (int p = 0; p < 3; p++) { frame.data[0][p] = f0.data[0][p]; frame.data[1][p] = f1.data[0][p]; frame.line_size[0][p] = f0.line_size[0][p]; frame.line_size[1][p] = f1.line_size[0][p]; } frame.presentation_time = f0.presentation_time; } } else { int o, s; get_video_stream(_active_video_stream, o, s); video_frame f = _media_objects[o].finish_video_frame_read(s); if (f.is_valid()) { frame = _video_frame; for (int v = 0; v < 2; v++) { for (int p = 0; p < 3; p++) { frame.data[v][p] = f.data[v][p]; frame.line_size[v][p] = f.line_size[v][p]; } } frame.presentation_time = f.presentation_time; } } _have_active_video_read = false; return frame; }
void media_input::select_video_stream(int video_stream) { if (_have_active_video_read) { (void)finish_video_frame_read(); } if (_have_active_audio_read) { (void)finish_audio_blob_read(); } if (_have_active_subtitle_read) { (void)finish_subtitle_box_read(); } assert(video_stream >= 0); assert(video_stream < video_streams()); if (_video_frame.stereo_layout == parameters::layout_separate) { _active_video_stream = 0; for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { _media_objects[i].video_stream_set_active(j, true); } } } else { _active_video_stream = video_stream; int o, s; get_video_stream(_active_video_stream, o, s); for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { _media_objects[i].video_stream_set_active(j, (i == static_cast<size_t>(o) && j == s)); } } } // Re-set video frame template parameters::stereo_layout_t stereo_layout_bak = _video_frame.stereo_layout; bool stereo_layout_swap_bak = _video_frame.stereo_layout_swap; int o, s; get_video_stream(_active_video_stream, o, s); _video_frame = _media_objects[o].video_frame_template(s); _video_frame.stereo_layout = stereo_layout_bak; _video_frame.stereo_layout_swap = stereo_layout_swap_bak; _video_frame.set_view_dimensions(); }
int media_input::video_frame_rate_denominator() const { assert(_active_video_stream >= 0); int o, s; get_video_stream(_active_video_stream, o, s); return _media_objects[o].video_frame_rate_denominator(s); }
/* * picture_to_frame reads and decodes * the picture file * * side effects: allocates an AVFrame which * must be freed with av_free_frame */ FFMPEG_tmp * picture_to_frame(char *filepath) { AVFormatContext *fctx = NULL; int stream_no; AVCodecContext *cctx = NULL; AVCodec *c = NULL; AVFrame *frame = NULL; FFMPEG_tmp *tmp = malloc(sizeof(FFMPEG_tmp)); fctx = get_fcontext(filepath); if (fctx == NULL) { fprintf(stderr, "Fatal: could not open %s\n", filepath); avformat_close_input(&fctx); exit(1); } stream_no = get_video_stream(fctx); if (stream_no == -1) { fprintf(stderr, "Fatal: could not find video stream\n"); avformat_close_input(&fctx); exit(1); } cctx = get_ccontext(fctx, stream_no); if (cctx == NULL) { fprintf(stderr, "Fatal: no codec context initialized\n"); avcodec_close(cctx); avformat_close_input(&fctx); exit(1); } c = get_codec(cctx); if (c == NULL) { fprintf(stderr, "Fatal: could not open codec\n"); avcodec_close(cctx); avformat_close_input(&fctx); exit(1); } frame = decode_picture(fctx, stream_no, cctx); if (frame == NULL) { avcodec_close(cctx); avformat_close_input(&fctx); fprintf(stderr, "Fatal: could not decode image\n"); exit(1); } /* clean up successful run */ tmp->frame = frame; tmp->fctx = fctx; tmp->cctx = cctx; tmp->c = c; return tmp; }
void media_input::start_video_frame_read() { assert(_active_video_stream >= 0); if (_have_active_video_read) { return; } if (_video_frame.stereo_layout == video_frame::separate) { int o0, s0, o1, s1; get_video_stream(0, o0, s0); get_video_stream(1, o1, s1); _media_objects[o0].start_video_frame_read(s0); _media_objects[o1].start_video_frame_read(s1); } else { int o, s; get_video_stream(_active_video_stream, o, s); _media_objects[o].start_video_frame_read(s); } _have_active_video_read = true; }
int64_t media_input::tell() { int64_t pos = std::numeric_limits<int64_t>::min(); int o, s; if (_active_audio_stream >= 0) { get_audio_stream(_active_audio_stream, o, s); pos = _media_objects[o].tell(); } else if (_active_video_stream >= 0) { get_video_stream(_active_video_stream, o, s); pos = _media_objects[o].tell(); } return pos; }
void media_input::select_video_stream(int video_stream) { if (_have_active_video_read) { (void)finish_video_frame_read(); } if (_have_active_audio_read) { (void)finish_audio_blob_read(); } if (_have_active_subtitle_read) { (void)finish_subtitle_box_read(); } assert(video_stream >= 0); assert(video_stream < video_streams()); if (_video_frame.stereo_layout == video_frame::separate) { _active_video_stream = 0; for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { _media_objects[i].video_stream_set_active(j, true); } } } else { _active_video_stream = video_stream; int o, s; get_video_stream(_active_video_stream, o, s); for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { _media_objects[i].video_stream_set_active(j, (i == static_cast<size_t>(o) && j == s)); } } } }
bool media_input::stereo_layout_is_supported(parameters::stereo_layout_t layout, bool) const { if (video_streams() < 1) { return false; } assert(_active_video_stream >= 0); assert(_active_video_stream < video_streams()); int o, s; get_video_stream(_active_video_stream, o, s); const video_frame &t = _media_objects[o].video_frame_template(s); bool supported = true; if (((layout == parameters::layout_left_right || layout == parameters::layout_left_right_half) && t.raw_width % 2 != 0) || ((layout == parameters::layout_top_bottom || layout == parameters::layout_top_bottom_half) && t.raw_height % 2 != 0) || (layout == parameters::layout_even_odd_rows && t.raw_height % 2 != 0) || (layout == parameters::layout_separate && !_supports_stereo_layout_separate)) { supported = false; } return supported; }
void media_input::set_stereo_layout(parameters::stereo_layout_t layout, bool swap) { assert(stereo_layout_is_supported(layout, swap)); if (_have_active_video_read) { (void)finish_video_frame_read(); } if (_have_active_audio_read) { (void)finish_audio_blob_read(); } if (_have_active_subtitle_read) { (void)finish_subtitle_box_read(); } int o, s; get_video_stream(_active_video_stream, o, s); const video_frame &t = _media_objects[o].video_frame_template(s); _video_frame = t; _video_frame.stereo_layout = layout; _video_frame.stereo_layout_swap = swap; _video_frame.set_view_dimensions(); // Reset active stream in case we switched to or from 'separate'. select_video_stream(_active_video_stream); if (layout == parameters::layout_separate) { // If we switched the layout to 'separate', then we have to seek to the // position of the first video stream, or else the second video stream // is out of sync. int64_t pos = _media_objects[o].tell(); if (pos > std::numeric_limits<int64_t>::min()) { seek(pos); } } }
void media_input::open(const std::vector<std::string> &urls, const device_request &dev_request) { assert(urls.size() > 0); // Open media objects _is_device = dev_request.is_device(); _media_objects.resize(urls.size()); for (size_t i = 0; i < urls.size(); i++) { _media_objects[i].open(urls[i], dev_request); } // Construct id for this input _id = basename(_media_objects[0].url()); for (size_t i = 1; i < _media_objects.size(); i++) { _id += '/'; _id += basename(_media_objects[i].url()); } // Gather metadata for (size_t i = 0; i < _media_objects.size(); i++) { // Note that we may have multiple identical tag names in our metadata for (size_t j = 0; j < _media_objects[i].tags(); j++) { _tag_names.push_back(_media_objects[i].tag_name(j)); _tag_values.push_back(_media_objects[i].tag_value(j)); } } // Gather streams and stream names for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { _video_stream_names.push_back(_media_objects[i].video_frame_template(j).format_info()); } } if (_video_stream_names.size() > 1) { for (size_t i = 0; i < _video_stream_names.size(); i++) { _video_stream_names[i].insert(0, std::string(1, '#') + str::from(i + 1) + '/' + str::from(_video_stream_names.size()) + ": "); } } for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].audio_streams(); j++) { _audio_stream_names.push_back(_media_objects[i].audio_blob_template(j).format_info()); } } if (_audio_stream_names.size() > 1) { for (size_t i = 0; i < _audio_stream_names.size(); i++) { _audio_stream_names[i].insert(0, std::string(1, '#') + str::from(i + 1) + '/' + str::from(_audio_stream_names.size()) + ": "); } } for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].subtitle_streams(); j++) { _subtitle_stream_names.push_back(_media_objects[i].subtitle_box_template(j).format_info()); } } if (_subtitle_stream_names.size() > 1) { for (size_t i = 0; i < _subtitle_stream_names.size(); i++) { _subtitle_stream_names[i].insert(0, std::string(1, '#') + str::from(i + 1) + '/' + str::from(_subtitle_stream_names.size()) + ": "); } } // Set duration information _duration = std::numeric_limits<int64_t>::max(); for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { int64_t d = _media_objects[i].video_duration(j); if (d < _duration) { _duration = d; } } for (int j = 0; j < _media_objects[i].audio_streams(); j++) { int64_t d = _media_objects[i].audio_duration(j); if (d < _duration) { _duration = d; } } // Ignore subtitle stream duration; it seems unreliable and is not important anyway. } // Skip advertisement in 3dtv.at movies. Only works for single media objects. try { _initial_skip = str::to<int64_t>(tag_value("StereoscopicSkip")); } catch (...) { } // Find stereo layout and set active video stream(s) _supports_stereo_layout_separate = false; if (video_streams() == 2) { int o0, o1, v0, v1; get_video_stream(0, o0, v0); get_video_stream(1, o1, v1); video_frame t0 = _media_objects[o0].video_frame_template(v0); video_frame t1 = _media_objects[o1].video_frame_template(v1); if (t0.width == t1.width && t0.height == t1.height && (t0.aspect_ratio <= t1.aspect_ratio && t0.aspect_ratio >= t1.aspect_ratio) && t0.layout == t1.layout && t0.color_space == t1.color_space && t0.value_range == t1.value_range && t0.chroma_location == t1.chroma_location) { _supports_stereo_layout_separate = true; } } if (_supports_stereo_layout_separate) { _active_video_stream = 0; int o, s; get_video_stream(_active_video_stream, o, s); _video_frame = _media_objects[o].video_frame_template(s); _video_frame.stereo_layout = parameters::layout_separate; } else if (video_streams() > 0) { _active_video_stream = 0; int o, s; get_video_stream(_active_video_stream, o, s); _video_frame = _media_objects[o].video_frame_template(s); } else { _active_video_stream = -1; } if (_active_video_stream >= 0) { select_video_stream(_active_video_stream); } // Set active audio stream _active_audio_stream = (audio_streams() > 0 ? 0 : -1); if (_active_audio_stream >= 0) { int o, s; get_audio_stream(_active_audio_stream, o, s); _audio_blob = _media_objects[o].audio_blob_template(s); select_audio_stream(_active_audio_stream); } // Set active subtitle stream _active_subtitle_stream = -1; // no subtitles by default // Print summary msg::inf(_("Input:")); for (int i = 0; i < video_streams(); i++) { int o, s; get_video_stream(i, o, s); msg::inf(4, _("Video %s: %s"), video_stream_name(i).c_str(), _media_objects[o].video_frame_template(s).format_name().c_str()); } if (video_streams() == 0) { msg::inf(4, _("No video.")); } for (int i = 0; i < audio_streams(); i++) { int o, s; get_audio_stream(i, o, s); msg::inf(4, _("Audio %s: %s"), audio_stream_name(i).c_str(), _media_objects[o].audio_blob_template(s).format_name().c_str()); } if (audio_streams() == 0) { msg::inf(4, _("No audio.")); } for (int i = 0; i < subtitle_streams(); i++) { int o, s; get_subtitle_stream(i, o, s); msg::inf(4, _("Subtitle %s: %s"), subtitle_stream_name(i).c_str(), _media_objects[o].subtitle_box_template(s).format_name().c_str()); } if (subtitle_streams() == 0) { msg::inf(4, _("No subtitle.")); } msg::inf(4, _("Duration: %g seconds"), duration() / 1e6f); if (video_streams() > 0) { msg::inf(4, _("Stereo layout: %s"), parameters::stereo_layout_to_string( video_frame_template().stereo_layout, video_frame_template().stereo_layout_swap).c_str()); } }
static DecoderContext *init_decoder(const char *filename) { DecoderContext *dc = (DecoderContext *)calloc(1, sizeof(DecoderContext)); AVCodecContext *codecCtx; // Open the stream if(avformat_open_input(&(dc->formatCtx), filename, NULL, NULL) != 0) { fprintf(stderr, "Couldn't open file"); exit(1); } // Retrieve stream information if(avformat_find_stream_info(dc->formatCtx, NULL) < 0) { fprintf(stderr, "Couldn't find stream information"); exit(1); } // Dump information about file onto standard error av_dump_format(dc->formatCtx, 0, filename, 0); // Get video Stream dc->videoStream = get_video_stream(dc->formatCtx); if (dc->videoStream == -1) { fprintf(stderr, "Couldn't find video stream"); exit(1); } codecCtx = dc->formatCtx->streams[dc->videoStream]->codec; /* find the decoder */ dc->codec = avcodec_find_decoder(codecCtx->codec_id); if (!dc->codec) { fprintf(stderr, "Codec not found\n"); exit(1); } /* Allocate codec context */ dc->codecCtx = avcodec_alloc_context3(dc->codec); if (!dc->codecCtx) { fprintf(stderr, "Could not allocate video codec context\n"); exit(1); } if(avcodec_copy_context(dc->codecCtx, codecCtx) != 0) { fprintf(stderr, "Couldn't copy codec context"); exit(1); // Error copying codec context } if(dc->codec->capabilities & CODEC_CAP_TRUNCATED) dc->codecCtx->flags |= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */ /* For some codecs, such as msmpeg4 and mpeg4, width and height MUST be initialized there because this information is not available in the bitstream. */ /* open it */ if (avcodec_open2(dc->codecCtx, dc->codec, NULL) < 0) { fprintf(stderr, "Could not open codec\n"); exit(1); } dc->frame = av_frame_alloc(); if (!dc->frame) { fprintf(stderr, "Could not allocate video frame\n"); exit(1); } // Allocate input buffer dc->numBytes = avpicture_get_size(dc->codecCtx->pix_fmt, dc->codecCtx->width, dc->codecCtx->height); dc->inbuf = calloc(1, dc->numBytes + FF_INPUT_BUFFER_PADDING_SIZE); if (!dc->inbuf) { fprintf(stderr, "Could not allocate buffer"); exit(1); } memset(dc->inbuf + dc->numBytes, 0, FF_INPUT_BUFFER_PADDING_SIZE); dc->frame_count = 0; return dc; }
void media_input::open(const std::vector<std::string> &urls) { assert(urls.size() > 0); // Open media objects _media_objects.resize(urls.size()); for (size_t i = 0; i < urls.size(); i++) { _media_objects[i].open(urls[i]); } // Construct id for this input _id = basename(_media_objects[0].url()); for (size_t i = 1; i < _media_objects.size(); i++) { _id += '/'; _id += basename(_media_objects[i].url()); } // Gather metadata for (size_t i = 0; i < _media_objects.size(); i++) { std::string pfx = (_media_objects.size() == 1 ? "" : str::from(i + 1) + " - "); for (size_t j = 0; j < _media_objects[i].tags(); j++) { _tag_names.push_back(pfx + _media_objects[i].tag_name(j)); _tag_values.push_back(pfx + _media_objects[i].tag_value(j)); } } // Gather streams and stream names for (size_t i = 0; i < _media_objects.size(); i++) { std::string pfx = (_media_objects.size() == 1 ? "" : str::from(i + 1) + " - "); for (int j = 0; j < _media_objects[i].video_streams(); j++) { std::string pfx2 = (_media_objects[i].video_streams() == 1 ? "" : str::from(j + 1) + " - "); _video_stream_names.push_back(pfx + pfx2 + _media_objects[i].video_frame_template(j).format_info()); } } for (size_t i = 0; i < _media_objects.size(); i++) { std::string pfx = (_media_objects.size() == 1 ? "" : str::from(i + 1) + " - "); for (int j = 0; j < _media_objects[i].audio_streams(); j++) { std::string pfx2 = (_media_objects[i].audio_streams() == 1 ? "" : str::from(j + 1) + " - "); _audio_stream_names.push_back(pfx + pfx2 + _media_objects[i].audio_blob_template(j).format_info()); } } for (size_t i = 0; i < _media_objects.size(); i++) { std::string pfx = (_media_objects.size() == 1 ? "" : str::from(i + 1) + " - "); for (int j = 0; j < _media_objects[i].subtitle_streams(); j++) { std::string pfx2 = (_media_objects[i].subtitle_streams() == 1 ? "" : str::from(j + 1) + " - "); _subtitle_stream_names.push_back(pfx + pfx2 + _media_objects[i].subtitle_box_template(j).format_info()); } } // Set duration information _duration = std::numeric_limits<int64_t>::max(); for (size_t i = 0; i < _media_objects.size(); i++) { for (int j = 0; j < _media_objects[i].video_streams(); j++) { int64_t d = _media_objects[i].video_duration(j); if (d < _duration) { _duration = d; } } for (int j = 0; j < _media_objects[i].audio_streams(); j++) { int64_t d = _media_objects[i].audio_duration(j); if (d < _duration) { _duration = d; } } for (int j = 0; j < _media_objects[i].subtitle_streams(); j++) { // int64_t d = _media_objects[i].subtitle_duration(j); // if (d < _duration) // { // _duration = d; // } } } // Skip advertisement in 3dtv.at movies. Only works for single media objects. try { _initial_skip = str::to<int64_t>(tag_value("StereoscopicSkip")); } catch (...) { } // Find stereo layout and set active video stream(s) _supports_stereo_layout_separate = false; if (video_streams() == 2) { int o0, o1, v0, v1; get_video_stream(0, o0, v0); get_video_stream(1, o1, v1); video_frame t0 = _media_objects[o0].video_frame_template(v0); video_frame t1 = _media_objects[o1].video_frame_template(v1); if (t0.width == t1.width && t0.height == t1.height && (t0.aspect_ratio <= t1.aspect_ratio && t0.aspect_ratio >= t1.aspect_ratio) && t0.layout == t1.layout && t0.color_space == t1.color_space && t0.value_range == t1.value_range && t0.chroma_location == t1.chroma_location) { _supports_stereo_layout_separate = true; } } if (_supports_stereo_layout_separate) { _active_video_stream = 0; int o, s; get_video_stream(_active_video_stream, o, s); _video_frame = _media_objects[o].video_frame_template(s); _video_frame.stereo_layout = video_frame::separate; } else if (video_streams() > 0) { _active_video_stream = 0; int o, s; get_video_stream(_active_video_stream, o, s); _video_frame = _media_objects[o].video_frame_template(s); } else { _active_video_stream = -1; } if (_active_video_stream >= 0) { select_video_stream(_active_video_stream); } // Set active audio stream _active_audio_stream = (audio_streams() > 0 ? 0 : -1); if (_active_audio_stream >= 0) { int o, s; get_audio_stream(_active_audio_stream, o, s); _audio_blob = _media_objects[o].audio_blob_template(s); select_audio_stream(_active_audio_stream); } // Set active subtitle stream _active_subtitle_stream = -1; // no subtitles by default // Print summary msg::inf("Input:"); for (int i = 0; i < video_streams(); i++) { int o, s; get_video_stream(i, o, s); msg::inf(" Video %s: %s", video_stream_name(i).c_str(), _media_objects[o].video_frame_template(s).format_name().c_str()); } if (video_streams() == 0) { msg::inf(" No video."); } for (int i = 0; i < audio_streams(); i++) { int o, s; get_audio_stream(i, o, s); msg::inf(" Audio %s: %s", audio_stream_name(i).c_str(), _media_objects[o].audio_blob_template(s).format_name().c_str()); } if (audio_streams() == 0) { msg::inf(" No audio."); } for (int i = 0; i < subtitle_streams(); i++) { int o, s; get_subtitle_stream(i, o, s); msg::inf(" Subtitle %s: %s", subtitle_stream_name(i).c_str(), _media_objects[o].subtitle_box_template(s).format_name().c_str()); } if (subtitle_streams() == 0) { msg::inf(" No subtitles."); } msg::inf(" Duration: %g seconds", duration() / 1e6f); if (video_streams() > 0) { msg::inf(" Stereo layout: %s", video_frame::stereo_layout_to_string( video_frame_template().stereo_layout, video_frame_template().stereo_layout_swap).c_str()); } }