Пример #1
0
bool
cluster_helper_c::must_duration_be_set(render_groups_c *rg,
                                       packet_cptr &new_packet) {
  size_t i;
  int64_t block_duration = 0;
  int64_t def_duration   = rg->m_source->get_track_default_duration();

  for (i = 0; rg->m_durations.size() > i; ++i)
    block_duration += rg->m_durations[i];
  block_duration += new_packet->get_duration();

  if (rg->m_duration_mandatory || new_packet->duration_mandatory) {
    if (   (0 == block_duration)
        || (   (0 < block_duration)
            && (block_duration != ((static_cast<int64_t>(rg->m_durations.size()) + 1) * def_duration))))
      return true;

  } else if (   (   g_use_durations
                 || (0 < def_duration))
             && (0 < block_duration)
             && (RND_TIMECODE_SCALE(block_duration) != RND_TIMECODE_SCALE((rg->m_durations.size() + 1) * def_duration)))
    return true;

  return false;
}
Пример #2
0
bool
cluster_helper_c::add_to_cues_maybe(packet_cptr &pack) {
  auto &source  = *pack->source;
  auto strategy = source.get_cue_creation();

  // Update the cues (index table) either if cue entries for I frames were requested and this is an I frame...
  bool add = (CUE_STRATEGY_IFRAMES == strategy) && pack->is_key_frame();

  // ... or if a codec state change is present ...
  add = add || !!pack->codec_state;

  // ... or if the user requested entries for all frames ...
  add = add || (CUE_STRATEGY_ALL == strategy);

  // ... or if this is an audio track, there is no video track and the
  // last cue entry was created more than 2s ago.
  add = add || (   (CUE_STRATEGY_SPARSE == strategy)
                && (track_audio         == source.get_track_type())
                && !g_video_packetizer
                && (   (0 > source.get_last_cue_timecode())
                    || ((pack->assigned_timecode - source.get_last_cue_timecode()) >= 2000000000)));

  if (!add)
    return false;

  source.set_last_cue_timecode(pack->assigned_timecode);

  ++m->num_cue_elements;
  g_cue_writing_requested = 1;

  return true;
}
Пример #3
0
int
mpeg1_2_video_packetizer_c::process(packet_cptr packet) {
  if (0.0 > m_fps)
    extract_fps(packet->data->get_buffer(), packet->data->get_size());

  if (!m_aspect_ratio_extracted)
    extract_aspect_ratio(packet->data->get_buffer(), packet->data->get_size());

  if (m_framed)
    return process_framed(packet);

  int state = m_parser.GetState();
  if ((MPV_PARSER_STATE_EOS == state) || (MPV_PARSER_STATE_ERROR == state))
    return FILE_STATUS_DONE;

  memory_cptr old_memory  = packet->data;
  unsigned char *data_ptr = old_memory->get_buffer();
  int new_bytes           = old_memory->get_size();

  if (packet->has_timecode())
    m_parser.AddTimecode(packet->timecode);

  do {
    int bytes_to_add = (m_parser.GetFreeBufferSpace() < new_bytes) ? m_parser.GetFreeBufferSpace() : new_bytes;
    if (0 < bytes_to_add) {
      m_parser.WriteData(data_ptr, bytes_to_add);
      data_ptr  += bytes_to_add;
      new_bytes -= bytes_to_add;
    }

    state = m_parser.GetState();
    while (MPV_PARSER_STATE_FRAME == state) {
      MPEGFrame *frame = m_parser.ReadFrame();
      if (NULL == frame)
        break;

      if (NULL == m_hcodec_private)
        create_private_data();

      packet_cptr new_packet   = packet_cptr(new packet_t(new memory_c(frame->data, frame->size, true), frame->timecode, frame->duration, frame->firstRef, frame->secondRef));
      new_packet->time_factor = MPEG2_PICTURE_TYPE_FRAME == frame->pictureStructure ? 1 : 2;

      put_sequence_headers_into_codec_state(new_packet);

      video_packetizer_c::process(new_packet);

      frame->data = NULL;
      delete frame;

      state = m_parser.GetState();
    }
  } while (0 < new_bytes);

  return FILE_STATUS_MOREDATA;
}
Пример #4
0
int
hdmv_textst_packetizer_c::process(packet_cptr packet) {
  if ((packet->data->get_size() < 13) || (static_cast<mtx::hdmv_textst::segment_type_e>(packet->data->get_buffer()[0]) != mtx::hdmv_textst::dialog_presentation_segment))
    return FILE_STATUS_MOREDATA;

  auto buf       = packet->data->get_buffer();
  auto start_pts = timestamp_c::mpeg((static_cast<int64_t>(buf[3] & 1) << 32) | get_uint32_be(&buf[4]));
  auto end_pts   = timestamp_c::mpeg((static_cast<int64_t>(buf[8] & 1) << 32) | get_uint32_be(&buf[9]));

  if (!packet->has_timestamp())
    packet->timestamp = start_pts.to_ns();

  if (!packet->has_duration())
    packet->duration = (end_pts - start_pts).abs().to_ns();

  packet->duration_mandatory = true;

  add_packet(packet);

  return FILE_STATUS_MOREDATA;
}
Пример #5
0
void
cluster_helper_c::split_if_necessary(packet_cptr &packet) {
    if (   !splitting()
            || (m->split_points.end() == m->current_split_point)
            || (g_file_num > g_split_max_num_files)
            || !packet->is_key_frame()
            || (   (packet->source->get_track_type() != track_video)
                   && g_video_packetizer))
        return;

    bool split_now = false;

    // Maybe we want to start a new file now.
    if (split_point_c::size == m->current_split_point->m_type) {
        int64_t additional_size = 0;

        if (!m->packets.empty())
            // Cluster + Cluster timecode: roughly 21 bytes. Add all frame sizes & their overheaders, too.
            additional_size = 21 + boost::accumulate(m->packets, 0, [](size_t size, const packet_cptr &p) {
            return size + p->data->get_size() + (p->is_key_frame() ? 10 : p->is_p_frame() ? 13 : 16);
        });

        additional_size += 18 * m->num_cue_elements;

        mxdebug_if(m->debug_splitting,
                   boost::format("cluster_helper split decision: header_overhead: %1%, additional_size: %2%, bytes_in_file: %3%, sum: %4%\n")
                   % m->header_overhead % additional_size % m->bytes_in_file % (m->header_overhead + additional_size + m->bytes_in_file));
        if ((m->header_overhead + additional_size + m->bytes_in_file) >= m->current_split_point->m_point)
            split_now = true;

    } else if (   (split_point_c::duration == m->current_split_point->m_type)
                  && (0 <= m->first_timecode_in_file)
                  && (packet->assigned_timecode - m->first_timecode_in_file) >= m->current_split_point->m_point)
        split_now = true;

    else if (   (   (split_point_c::timecode == m->current_split_point->m_type)
                    || (split_point_c::parts    == m->current_split_point->m_type))
                && (packet->assigned_timecode >= m->current_split_point->m_point))
        split_now = true;

    else if (   (   (split_point_c::frame_field       == m->current_split_point->m_type)
                    || (split_point_c::parts_frame_field == m->current_split_point->m_type))
                && (m->frame_field_number >= m->current_split_point->m_point))
        split_now = true;

    if (!split_now)
        return;

    split(packet);
}
Пример #6
0
int
pcm_packetizer_c::process(packet_cptr packet) {
  if (packet->has_timecode() && (packet->data->get_size() >= m_min_packet_size))
    return process_packaged(packet);

  m_buffer.add(packet->data->get_buffer(), packet->data->get_size());

  while (m_buffer.get_size() >= m_packet_size) {
    add_packet(new packet_t(memory_c::clone(m_buffer.get_buffer(), m_packet_size), m_samples_output * m_s2tc, m_samples_per_packet * m_s2tc));

    m_buffer.remove(m_packet_size);
    m_samples_output += m_samples_per_packet;
  }

  return FILE_STATUS_MOREDATA;
}
Пример #7
0
int
mpeg1_2_video_packetizer_c::process_unframed(packet_cptr packet) {
  int state = m_parser.GetState();
  if ((MPV_PARSER_STATE_EOS == state) || (MPV_PARSER_STATE_ERROR == state))
    return FILE_STATUS_DONE;

  auto old_memory = packet->data;
  auto data_ptr   = old_memory->get_buffer();
  int new_bytes   = old_memory->get_size();

  if (packet->has_timecode())
    m_parser.AddTimecode(packet->timecode);

  do {
    int bytes_to_add = std::min<int>(m_parser.GetFreeBufferSpace(), new_bytes);
    if (0 < bytes_to_add) {
      m_parser.WriteData(data_ptr, bytes_to_add);
      data_ptr  += bytes_to_add;
      new_bytes -= bytes_to_add;
    }

    state = m_parser.GetState();
    while (MPV_PARSER_STATE_FRAME == state) {
      auto frame = std::shared_ptr<MPEGFrame>(m_parser.ReadFrame());
      if (!frame)
        break;

      if (!m_hcodec_private)
        create_private_data();

      packet_cptr new_packet  = packet_cptr(new packet_t(new memory_c(frame->data, frame->size, true), frame->timecode, frame->duration, frame->refs[0], frame->refs[1]));
      new_packet->time_factor = MPEG2_PICTURE_TYPE_FRAME == frame->pictureStructure ? 1 : 2;

      remove_stuffing_bytes_and_handle_sequence_headers(new_packet);

      generic_video_packetizer_c::process(new_packet);

      frame->data = nullptr;
      state       = m_parser.GetState();
    }
  } while (0 < new_bytes);

  return FILE_STATUS_MOREDATA;
}
Пример #8
0
void
cluster_helper_c::render_before_adding_if_necessary(packet_cptr &packet) {
  int64_t timecode        = get_timecode();
  int64_t timecode_delay  = (   (packet->assigned_timecode > m->max_timecode_in_cluster)
                             || (-1 == m->max_timecode_in_cluster))                       ? packet->assigned_timecode : m->max_timecode_in_cluster;
  timecode_delay         -= (   (-1 == m->min_timecode_in_cluster)
                             || (packet->assigned_timecode < m->min_timecode_in_cluster)) ? packet->assigned_timecode : m->min_timecode_in_cluster;
  timecode_delay          = (int64_t)(timecode_delay / g_timecode_scale);

  mxdebug_if(m->debug_packets,
             boost::format("cluster_helper_c::add_packet(): new packet { source %1%/%2% "
                           "timecode: %3% duration: %4% bref: %5% fref: %6% assigned_timecode: %7% timecode_delay: %8% }\n")
             % packet->source->m_ti.m_id % packet->source->m_ti.m_fname % packet->timecode          % packet->duration
             % packet->bref              % packet->fref                 % packet->assigned_timecode % format_timecode(timecode_delay));

  bool is_video_keyframe = (packet->source == g_video_packetizer) && packet->is_key_frame();
  bool do_render         = (std::numeric_limits<int16_t>::max() < timecode_delay)
                        || (std::numeric_limits<int16_t>::min() > timecode_delay)
                        || (   (std::max<int64_t>(0, m->min_timecode_in_cluster) > m->previous_cluster_tc)
                            && (packet->assigned_timecode                        > m->min_timecode_in_cluster)
                            && (!g_video_packetizer || !is_video_keyframe || m->first_video_keyframe_seen)
                            && (   (packet->gap_following && !m->packets.empty())
                                || ((packet->assigned_timecode - timecode) > g_max_ns_per_cluster)
                                || is_video_keyframe));

  if (is_video_keyframe)
    m->first_video_keyframe_seen = true;

  mxdebug_if(m->debug_rendering,
             boost::format("render check cur_tc %9% min_tc_ic %1% prev_cl_tc %2% test %3% is_vid_and_key %4% tc_delay %5% gap_following_and_not_empty %6% cur_tc>min_tc_ic %8% first_video_key_seen %10% do_render %7%\n")
             % m->min_timecode_in_cluster % m->previous_cluster_tc % (std::max<int64_t>(0, m->min_timecode_in_cluster) > m->previous_cluster_tc) % is_video_keyframe
             % timecode_delay % (packet->gap_following && !m->packets.empty()) % do_render % (packet->assigned_timecode > m->min_timecode_in_cluster) % packet->assigned_timecode % m->first_video_keyframe_seen);

  if (!do_render)
    return;

  render();
  prepare_new_cluster();
}
Пример #9
0
int
opus_packetizer_c::process(packet_cptr packet) {
  try {
    auto toc = mtx::opus::toc_t::decode(packet->data);
    mxdebug_if(m_debug, boost::format("TOC: %1%\n") % toc);

    if (!packet->has_timecode() || (timecode_c::ns(packet->timecode) == m_previous_provided_timecode))
      packet->timecode             = m_next_calculated_timecode.to_ns();
    else
      m_previous_provided_timecode = timecode_c::ns(packet->timecode);

    packet->duration               = toc.packet_duration.to_ns();
    m_next_calculated_timecode     = timecode_c::ns(packet->timecode + packet->duration);

    add_packet(packet);

  } catch (mtx::opus::exception &ex) {
    mxdebug_if(m_debug, boost::format("Exception: %1%\n") % ex.what());
  }

  return FILE_STATUS_MOREDATA;
}
Пример #10
0
void
cluster_helper_c::add_packet(packet_cptr packet) {
  if (!m->cluster)
    prepare_new_cluster();

  packet->normalize_timecodes();
  render_before_adding_if_necessary(packet);
  split_if_necessary(packet);

  m->packets.push_back(packet);
  m->cluster_content_size += packet->data->get_size();

  if (packet->assigned_timecode > m->max_timecode_in_cluster)
    m->max_timecode_in_cluster = packet->assigned_timecode;

  if ((-1 == m->min_timecode_in_cluster) || (packet->assigned_timecode < m->min_timecode_in_cluster))
    m->min_timecode_in_cluster = packet->assigned_timecode;

  render_after_adding_if_necessary(packet);

  if (g_video_packetizer == packet->source)
    ++m->frame_field_number;
}
Пример #11
0
int
mpeg4_p10_es_video_packetizer_c::process(packet_cptr packet) {
  try {
    if (packet->has_timecode())
      m_parser.add_timecode(packet->timecode);
    m_parser.add_bytes(packet->data->get_buffer(), packet->data->get_size());
    flush_frames();

  } catch (nalu_size_length_x &error) {
    mxerror_tid(m_ti.m_fname, m_ti.m_id,
                boost::format(Y("This AVC/h.264 contains frames that are too big for the current maximum NALU size. "
                                "You have to re-run mkvmerge and set the maximum NALU size to %1% for this track "
                                "(command line parameter '--nalu-size-length %2%:%1%').\n"))
                % error.get_required_length() % m_ti.m_id);

  } catch (mtx::exception &error) {
    mxerror_tid(m_ti.m_fname, m_ti.m_id,
                boost::format(Y("mkvmerge encountered broken or unparsable data in this AVC/h.264 video track. "
                                "Either your file is damaged (which mkvmerge cannot cope with yet) or this is a bug in mkvmerge itself. "
                                "The error message was:\n%1%\n")) % error.error());
  }

  return FILE_STATUS_MOREDATA;
}
Пример #12
0
int
textsubs_packetizer_c::process(packet_cptr packet) {
  ++m_packetno;

  if (0 > packet->duration) {
    subtitle_number_packet_extension_c *extension = dynamic_cast<subtitle_number_packet_extension_c *>(packet->find_extension(packet_extension_c::SUBTITLE_NUMBER));
    mxwarn_tid(m_ti.m_fname, m_ti.m_id, boost::format(Y("Ignoring an entry which starts after it ends (%1%).\n")) % (extension ? extension->get_number() : static_cast<unsigned int>(m_packetno)));
    return FILE_STATUS_MOREDATA;
  }

  packet->duration_mandatory = true;

  std::string subs((char *)packet->data->get_buffer());

  subs = boost::regex_replace(subs, s_re_remove_cr,          "",     boost::match_default | boost::match_single_line);
  subs = boost::regex_replace(subs, s_re_remove_trailing_nl, "",     boost::match_default | boost::match_single_line);
  subs = boost::regex_replace(subs, s_re_translate_nl,       "\r\n", boost::match_default | boost::match_single_line);

  if (m_recode)
    subs = m_cc_utf8->utf8(subs);

  packet->data = memory_cptr(new memory_c((unsigned char *)subs.c_str(), subs.length(), false));

  add_packet(packet);

  return FILE_STATUS_MOREDATA;
}
void
generic_packetizer_c::add_packet2(packet_cptr pack) {
  if (pack->has_discard_padding())
    set_required_matroska_version(4);

  pack->timecode   = ADJUST_TIMECODE(pack->timecode);
  if (pack->has_bref())
    pack->bref     = ADJUST_TIMECODE(pack->bref);
  if (pack->has_fref())
    pack->fref     = ADJUST_TIMECODE(pack->fref);
  if (pack->has_duration()) {
    pack->duration = static_cast<int64_t>(pack->duration * m_ti.m_tcsync.numerator / m_ti.m_tcsync.denominator);
    if (pack->has_discard_padding())
      pack->duration -= std::min(pack->duration, pack->discard_padding.to_ns());
  }

  if ((2 > m_htrack_min_cache) && pack->has_fref()) {
    set_track_min_cache(2);
    rerender_track_headers();

  } else if ((1 > m_htrack_min_cache) && pack->has_bref()) {
    set_track_min_cache(1);
    rerender_track_headers();
  }

  if (0 > pack->timecode)
    return;

  // 'timecode < safety_last_timecode' may only occur for B frames. In this
  // case we have the coding order, e.g. IPB1B2 and the timecodes
  // I: 0, P: 120, B1: 40, B2: 80.
  if (!m_relaxed_timecode_checking && (pack->timecode < m_safety_last_timecode) && (0 > pack->fref) && hack_engaged(ENGAGE_ENABLE_TIMECODE_WARNING)) {
    if (track_audio == m_htrack_type) {
      int64_t needed_timecode_offset  = m_safety_last_timecode + m_safety_last_duration - pack->timecode;
      m_correction_timecode_offset   += needed_timecode_offset;
      pack->timecode                 += needed_timecode_offset;
      if (pack->has_bref())
        pack->bref += needed_timecode_offset;
      if (pack->has_fref())
        pack->fref += needed_timecode_offset;

      mxwarn_tid(m_ti.m_fname, m_ti.m_id,
                 boost::format(Y("The current packet's timecode is smaller than that of the previous packet. "
                                 "This usually means that the source file is a Matroska file that has not been created 100%% correctly. "
                                 "The timecodes of all packets will be adjusted by %1%ms in order not to lose any data. "
                                 "This may throw audio/video synchronization off, but that can be corrected with mkvmerge's \"--sync\" option. "
                                 "If you already use \"--sync\" and you still get this warning then do NOT worry -- this is normal. "
                                 "If this error happens more than once and you get this message more than once for a particular track "
                                 "then either is the source file badly mastered, or mkvmerge contains a bug. "
                                 "In this case you should contact the author Moritz Bunkus <*****@*****.**>.\n"))
                 % ((needed_timecode_offset + 500000) / 1000000));

    } else
      mxwarn_tid(m_ti.m_fname, m_ti.m_id,
                 boost::format("generic_packetizer_c::add_packet2: timecode < last_timecode (%1% < %2%). %3%\n")
                 % format_timestamp(pack->timecode) % format_timestamp(m_safety_last_timecode) % BUGMSG);
  }

  m_safety_last_timecode        = pack->timecode;
  m_safety_last_duration        = pack->duration;
  pack->timecode_before_factory = pack->timecode;

  m_packet_queue.push_back(pack);
  if (!m_timestamp_factory || (TFA_IMMEDIATE == m_timestamp_factory_application_mode))
    apply_factory_once(pack);
  else
    apply_factory();
}
Пример #14
0
void
timestamp_calculator_c::add_timecode(packet_cptr const &packet) {
  if (packet->has_timecode())
    add_timecode(timestamp_c::ns(packet->timecode));
}