| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <math.h> |
| #include <fstream> |
| #include <iostream> |
| #include <memory> |
| |
| #include <lib/media/codec_impl/fourcc.h> |
| #include <lib/zx/vmar.h> |
| #include <lib/zx/vmo.h> |
| |
| #include "raw_frames.h" |
| |
| size_t AlignUp(size_t raw, size_t alignment) { |
| return (raw + alignment - 1) / alignment * alignment; |
| } |
| |
| // static |
| std::optional<RawFrames> RawFrames::FromI420File(std::string file, |
| RawFrames::Layout layout) { |
| size_t file_size; |
| std::fstream input_file(file, |
| std::ios::binary | std::ios::in | std::ios::ate); |
| if (!input_file.is_open()) { |
| fprintf(stderr, "Failed to open %s.\n", file.c_str()); |
| return std::nullopt; |
| } |
| file_size = input_file.tellg(); |
| input_file.seekg(0); |
| |
| const size_t source_frame_size = layout.width * layout.height * 3 / 2; |
| if (file_size % source_frame_size) { |
| fprintf(stderr, "%s is not raw I420 data of the given dimensions.\n", |
| file.c_str()); |
| return std::nullopt; |
| } |
| |
| const size_t frame_count = file_size / source_frame_size; |
| if (frame_count == 0) { |
| fprintf(stderr, "%s has no frames in it.\n", file.c_str()); |
| return std::nullopt; |
| } |
| |
| const size_t frame_stored_size = |
| AlignUp(source_frame_size, layout.frame_alignment); |
| const size_t total_storage_size = frame_stored_size * frame_count; |
| |
| zx::vmo vmo; |
| fzl::VmoMapper mapper; |
| zx_status_t err = mapper.CreateAndMap( |
| total_storage_size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, nullptr, &vmo); |
| if (err != ZX_OK) { |
| fprintf(stderr, "Failed to create and map vmo: %d\n", err); |
| return std::nullopt; |
| } |
| |
| // We prepare the image in YV12 format, and add padding for each row if |
| // `stride` > `width`. |
| for (size_t i = 0; i < frame_count; ++i) { |
| char* y_start = |
| reinterpret_cast<char*>(mapper.start()) + i * frame_stored_size; |
| char* v_start = y_start + layout.height * layout.stride; |
| char* u_start = v_start + (layout.height / 2) * (layout.stride / 2); |
| // Y Plane |
| for (size_t j = 0; j < layout.height; ++j) { |
| input_file.read(y_start + j * layout.stride, layout.width); |
| } |
| // U Plane |
| for (size_t j = 0; j < layout.height / 2; ++j) { |
| input_file.read(u_start + j * (layout.stride / 2), layout.width / 2); |
| } |
| // V Plane |
| for (size_t j = 0; j < layout.height / 2; ++j) { |
| input_file.read(v_start + j * (layout.stride / 2), layout.width / 2); |
| } |
| } |
| |
| return RawFrames(layout, std::move(vmo), std::move(mapper), frame_stored_size, |
| frame_count); |
| } |
| |
| std::optional<RawFrames::Image> RawFrames::Frame(size_t frame_index) { |
| if (frame_index >= frame_count_) { |
| return std::nullopt; |
| } |
| |
| zx::vmo vmo; |
| zx_status_t err = frames_.duplicate( |
| ZX_RIGHT_READ | ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER | ZX_RIGHT_MAP, |
| &vmo); |
| if (err != ZX_OK) { |
| fprintf(stderr, "Failed to duplicate frames vmo: %d\n", err); |
| return std::nullopt; |
| } |
| |
| fuchsia::media::VideoUncompressedFormat format = { |
| .fourcc = make_fourcc('Y', 'V', '1', '2'), |
| .primary_width_pixels = static_cast<uint32_t>(layout_.width), |
| .primary_height_pixels = static_cast<uint32_t>(layout_.height), |
| .secondary_width_pixels = static_cast<uint32_t>(layout_.width / 2), |
| .secondary_height_pixels = static_cast<uint32_t>(layout_.height / 2), |
| .planar = true, |
| .swizzled = false, |
| .primary_line_stride_bytes = static_cast<uint32_t>(layout_.stride), |
| .secondary_line_stride_bytes = static_cast<uint32_t>(layout_.stride / 2), |
| .primary_start_offset = 0, |
| .secondary_start_offset = |
| static_cast<uint32_t>(layout_.stride * layout_.height), |
| .tertiary_start_offset = |
| static_cast<uint32_t>(layout_.stride * layout_.height + |
| layout_.stride / 2 * layout_.height / 2), |
| .primary_display_width_pixels = static_cast<uint32_t>(layout_.width), |
| .primary_display_height_pixels = static_cast<uint32_t>(layout_.height), |
| .pixel_aspect_ratio_width = 1, |
| .pixel_aspect_ratio_height = 1, |
| }; |
| const size_t offset = frame_index * frame_stored_size_; |
| |
| return {{ |
| .format = std::move(format), |
| .image_size = frame_stored_size_, |
| .vmo_offset = offset, |
| .vmo = std::move(vmo), |
| .image_start = reinterpret_cast<uint8_t*>(mapper_.start()) + offset, |
| }}; |
| } |
| |
| size_t RawFrames::frame_count() { return frame_count_; } |
| |
| RawFrames::RawFrames(RawFrames::Layout layout, zx::vmo frames, |
| fzl::VmoMapper mapper, size_t frame_stored_size, |
| size_t frame_count) |
| : layout_(std::move(layout)), |
| frames_(std::move(frames)), |
| mapper_(std::move(mapper)), |
| frame_stored_size_(frame_stored_size), |
| frame_count_(frame_count) {} |