in vireo/tools/transcode/main.cpp [429:533]
functional::Video<encode::Sample> transcode(const functional::Video<decode::Sample> track,
const float fps,
const vector<common::EditBox> &edit_boxes,
const Config config,
FirstPtsAndTimescale &first_pts_and_timescale,
const bool print_info) {
settings::Video video_settings = track.settings();
THROW_IF(video_settings.codec != settings::Video::Codec::H264, Unsupported);
const uint16_t in_width = video_settings.width;
const uint16_t in_height = video_settings.height;
const settings::Video::Orientation in_orientation = video_settings.orientation;
auto resolution = out_resolution(in_width, in_height, in_orientation, config.height, config.square);
const uint16_t out_width = resolution.width;
const uint16_t out_height = resolution.height;
if (print_info) {
cout << "Video resolution " << out_width << "x" << out_height;
if (out_width != in_width || out_height != in_height) {
cout << ", resized from " << in_width << "x" << in_height;
}
cout << endl << "video framerate = " << fps << "fps";
cout << endl << "Optimization = " << config.optimization;
if (config.outfile_type == MP4 || config.outfile_type == MP2TS) {
if (config.rc_method == vireo::encode::RCMethod::CRF) {
cout << ", CRF = " << config.crf << ", number of b-frames = " << config.bframes;
} else {
cout << ", bitrate = " << config.video_bitrate;
}
} else {
cout << ", Quantizer = " << config.quantizer;
}
if (config.max_video_bitrate) {
cout << ", max bitrate = " << config.max_video_bitrate;
}
cout << endl << "Threads = " << config.decoder_threads << " (decoder)";
if (config.outfile_type == MP4 || config.outfile_type == MP2TS) {
cout << ", " << config.encoder_threads;
} else {
cout << ", 1";
}
cout << " (encoder)" << endl;
cout << "Video Profile = " << vireo::encode::kVideoProfileTypeToString[config.vprofile] << endl;
cout << "Output type = " << kFileTypeToString[config.outfile_type];
if (config.dash_init) {
cout << ", dash_init" << endl;
} else if (config.dash_data) {
cout << ", dash_data" << endl;
} else {
cout << endl;
}
}
auto crop_scale_rotate = [](frame::Frame frame, uint16_t in_width, uint16_t in_height, settings::Video::Orientation orientation, uint16_t out_width, uint16_t out_height) -> frame::Frame {
return (frame::Frame){ frame.pts, [in_width, in_height, orientation, out_width, out_height, yuv_func = frame.yuv]() {
// crop - if necessary
const bool square = (out_width == out_height);
const uint16_t min_dim = min(in_width, in_height);
const uint16_t crop_x_offset = square ? (in_width - min_dim) / 2 : 0;
const uint16_t crop_y_offset = square ? (in_height - min_dim) / 2 : 0;
auto yuv_cropped_func = (crop_x_offset == 0 && crop_y_offset == 0) ? yuv_func : [crop_x_offset, crop_y_offset, min_dim, &yuv_func](){
return yuv_func().crop(crop_x_offset, crop_y_offset, min_dim, min_dim);
};
const uint16_t cropped_width = in_width - 2 * crop_x_offset;
const uint16_t cropped_height = in_height - 2 * crop_y_offset;
// scale - if necessary
const bool is_portrait = orientation % 2;
const uint16_t real_height = is_portrait ? cropped_width : cropped_height;
auto yuv_scaled_func = (real_height == out_height) ? yuv_cropped_func : [real_height, out_height, &yuv_cropped_func](){
return yuv_cropped_func().scale(out_height, real_height);
};
// rotate - if necessary
auto yuv_rotated_func = (orientation == settings::Video::Landscape) ? yuv_scaled_func : [orientation, &yuv_scaled_func](){
return yuv_scaled_func().rotate((frame::Rotation)orientation);
};
return yuv_rotated_func();
}};
};
auto decoder = decode::Video(track, config.decoder_threads).filter(
[&edit_boxes, timescale = video_settings.timescale, start = config.start, duration = config.duration, &first_pts_and_timescale](const frame::Frame& frame) {
return include_pts(frame.pts, timescale, edit_boxes, start, duration, first_pts_and_timescale);
}
).transform<frame::Frame>(
[&edit_boxes, timescale = video_settings.timescale, &first_pts_and_timescale, &crop_scale_rotate, in_width, in_height, in_orientation, out_width, out_height](const frame::Frame& frame) {
int64_t first_pts = first_pts_and_timescale.first_pts;
CHECK(first_pts >= 0);
int64_t scaled_first_pts = first_pts * timescale / first_pts_and_timescale.timescale;
return crop_scale_rotate(frame.adjust_pts(edit_boxes).shift_pts(-scaled_first_pts), in_width, in_height, in_orientation, out_width, out_height);
}
);
auto output_video_settings = settings::Settings<SampleType::Video>(decoder.settings().codec, out_width, out_height, video_settings.timescale, settings::Video::Landscape, decoder.settings().sps_pps);
if (config.outfile_type == MP4 || config.outfile_type == MP2TS) {
auto computation = encode::H264Params::ComputationalParams(config.optimization, config.encoder_threads);
auto rc = encode::H264Params::RateControlParams(config.rc_method, config.crf, config.max_video_bitrate, config.video_bitrate, config.buffer_size, config.buffer_init, config.rc_look_ahead, config.is_second_pass, config.rc_b_mb_tree, config.aq_mode, config.qp_min, config.stats_log_path, config.mixed_refs, config.trellis, config.me_method, config.subpel_refine);
auto gop = encode::H264Params::GopParams(config.bframes, config.pyramid_mode, config.keyint_max, config.keyint_min, config.frame_references);
encode::H264Params params(computation, rc, gop, config.vprofile, fps);
return encode::H264(functional::Video<frame::Frame>(decoder, output_video_settings), params);
} else {
return encode::VP8(functional::Video<frame::Frame>(decoder, output_video_settings), config.quantizer, config.optimization, fps, config.max_video_bitrate);
}
};