Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2017, Alliance for Open Media. All rights reserved |
| 3 | * |
| 4 | * This source code is subject to the terms of the BSD 2 Clause License and |
| 5 | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
| 6 | * was not distributed with this source code in the LICENSE file, you can |
| 7 | * obtain it at www.aomedia.org/license/software. If the Alliance for Open |
| 8 | * Media Patent License 1.0 was not distributed with this source code in the |
| 9 | * PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
| 10 | */ |
| 11 | |
| 12 | // Lightfield Decoder |
| 13 | // ================== |
| 14 | // |
| 15 | // This is an example of a simple lightfield decoder. It builds upon the |
| 16 | // simple_decoder.c example. It takes an input file containing the compressed |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 17 | // data (in ivf format), treating it as a lightfield instead of a video; and a |
| 18 | // text file with a list of tiles to decode. |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 19 | // After running the lightfield encoder, run lightfield decoder to decode a |
Ryan Overbeck | 49a8217 | 2018-06-21 15:24:36 -0700 | [diff] [blame] | 20 | // batch of tiles: |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 21 | // examples/lightfield_decoder vase10x10.ivf vase_reference.yuv 4 tile_list.txt |
| 22 | // |
| 23 | // The tile_list.txt is expected to be of the form: |
| 24 | // Frame <frame_index0> |
| 25 | // <image_index0> <anchor_index0> <tile_col0> <tile_row0> |
| 26 | // <image_index1> <anchor_index1> <tile_col1> <tile_row1> |
| 27 | // ... |
| 28 | // Frame <frame_index1) |
| 29 | // ... |
| 30 | // |
| 31 | // The "Frame" markers indicate a new render frame and thus a new tile list |
| 32 | // will be started and the old one flushed. The image_indexN, anchor_indexN, |
| 33 | // tile_colN, and tile_rowN identify an individual tile to be decoded and |
| 34 | // to use anchor_indexN anchor image for MCP. |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 35 | |
| 36 | #include <stdio.h> |
| 37 | #include <stdlib.h> |
| 38 | #include <string.h> |
| 39 | |
| 40 | #include "aom/aom_decoder.h" |
| 41 | #include "aom/aomdx.h" |
Urvang Joshi | 6077b18 | 2018-08-21 11:49:38 -0700 | [diff] [blame] | 42 | #include "aom_scale/yv12config.h" |
| 43 | #include "av1/common/enums.h" |
Tom Finegan | 7790213 | 2018-05-21 10:19:15 -0700 | [diff] [blame] | 44 | #include "common/tools_common.h" |
| 45 | #include "common/video_reader.h" |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 46 | |
| 47 | static const char *exec_name; |
| 48 | |
| 49 | void usage_exit(void) { |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 50 | fprintf(stderr, "Usage: %s <infile> <outfile> <num_references> <tile_list>\n", |
| 51 | exec_name); |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 52 | exit(EXIT_FAILURE); |
| 53 | } |
| 54 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 55 | void decode_tile(aom_codec_ctx_t *codec, const unsigned char *frame, |
| 56 | size_t frame_size, int tr, int tc, int ref_idx, |
| 57 | aom_image_t *reference_images, FILE *outfile) { |
| 58 | aom_codec_control_(codec, AV1_SET_TILE_MODE, 1); |
| 59 | aom_codec_control_(codec, AV1D_EXT_TILE_DEBUG, 1); |
| 60 | aom_codec_control_(codec, AV1_SET_DECODE_TILE_ROW, tr); |
| 61 | aom_codec_control_(codec, AV1_SET_DECODE_TILE_COL, tc); |
Yunqing Wang | a2f1728 | 2018-06-18 22:43:08 -0700 | [diff] [blame] | 62 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 63 | av1_ref_frame_t ref; |
| 64 | ref.idx = 0; |
| 65 | ref.use_external_ref = 1; |
| 66 | ref.img = reference_images[ref_idx]; |
| 67 | if (aom_codec_control(codec, AV1_SET_REFERENCE, &ref)) { |
| 68 | die_codec(codec, "Failed to set reference frame."); |
| 69 | } |
| 70 | |
| 71 | aom_codec_err_t aom_status = aom_codec_decode(codec, frame, frame_size, NULL); |
| 72 | if (aom_status) die_codec(codec, "Failed to decode tile."); |
| 73 | |
| 74 | aom_codec_iter_t iter = NULL; |
| 75 | aom_image_t *img = aom_codec_get_frame(codec, &iter); |
| 76 | aom_img_write(img, outfile); |
| 77 | } |
Yunqing Wang | a2f1728 | 2018-06-18 22:43:08 -0700 | [diff] [blame] | 78 | |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 79 | int main(int argc, char **argv) { |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 80 | FILE *outfile = NULL; |
| 81 | aom_codec_ctx_t codec; |
| 82 | AvxVideoReader *reader = NULL; |
| 83 | const AvxInterface *decoder = NULL; |
| 84 | const AvxVideoInfo *info = NULL; |
Ryan Overbeck | 49a8217 | 2018-06-21 15:24:36 -0700 | [diff] [blame] | 85 | int num_references; |
Yunqing Wang | 93b18f3 | 2018-06-08 21:08:29 -0700 | [diff] [blame] | 86 | aom_image_t reference_images[MAX_EXTERNAL_REFERENCES]; |
Yunqing Wang | 946ec2b | 2018-06-06 12:34:23 -0700 | [diff] [blame] | 87 | size_t frame_size = 0; |
| 88 | const unsigned char *frame = NULL; |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 89 | int i, j; |
| 90 | const char *tile_list_file = NULL; |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 91 | exec_name = argv[0]; |
| 92 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 93 | if (argc != 5) die("Invalid number of arguments."); |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 94 | |
| 95 | reader = aom_video_reader_open(argv[1]); |
| 96 | if (!reader) die("Failed to open %s for reading.", argv[1]); |
| 97 | |
| 98 | if (!(outfile = fopen(argv[2], "wb"))) |
| 99 | die("Failed to open %s for writing.", argv[2]); |
| 100 | |
Ryan Overbeck | 49a8217 | 2018-06-21 15:24:36 -0700 | [diff] [blame] | 101 | num_references = (int)strtol(argv[3], NULL, 0); |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 102 | tile_list_file = argv[4]; |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 103 | |
| 104 | info = aom_video_reader_get_info(reader); |
| 105 | |
| 106 | decoder = get_aom_decoder_by_fourcc(info->codec_fourcc); |
| 107 | if (!decoder) die("Unknown input codec."); |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 108 | printf("Using %s\n", aom_codec_iface_name(decoder->codec_interface())); |
| 109 | |
| 110 | if (aom_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0)) |
| 111 | die_codec(&codec, "Failed to initialize decoder."); |
Yunqing Wang | 3dd5e3e | 2018-07-11 13:22:59 -0700 | [diff] [blame] | 112 | |
Jonathan Matthews | a6e4f96 | 2018-07-13 16:01:28 +0100 | [diff] [blame] | 113 | if (aom_codec_control(&codec, AV1D_SET_IS_ANNEXB, info->is_annexb)) { |
| 114 | die("Failed to set annex b status"); |
| 115 | } |
| 116 | |
Yunqing Wang | 946ec2b | 2018-06-06 12:34:23 -0700 | [diff] [blame] | 117 | // Decode anchor frames. |
| 118 | aom_codec_control_(&codec, AV1_SET_TILE_MODE, 0); |
Yunqing Wang | 946ec2b | 2018-06-06 12:34:23 -0700 | [diff] [blame] | 119 | for (i = 0; i < num_references; ++i) { |
| 120 | aom_video_reader_read_frame(reader); |
| 121 | frame = aom_video_reader_get_frame(reader, &frame_size); |
| 122 | if (aom_codec_decode(&codec, frame, frame_size, NULL)) |
| 123 | die_codec(&codec, "Failed to decode frame."); |
| 124 | |
Yunqing Wang | 3dd5e3e | 2018-07-11 13:22:59 -0700 | [diff] [blame] | 125 | if (i == 0) { |
| 126 | aom_img_fmt_t ref_fmt = 0; |
| 127 | if (aom_codec_control(&codec, AV1D_GET_IMG_FORMAT, &ref_fmt)) |
| 128 | die_codec(&codec, "Failed to get the image format"); |
| 129 | |
Jonathan Matthews | a6e4f96 | 2018-07-13 16:01:28 +0100 | [diff] [blame] | 130 | int frame_res[2]; |
| 131 | if (aom_codec_control(&codec, AV1D_GET_FRAME_SIZE, frame_res)) |
| 132 | die_codec(&codec, "Failed to get the image frame size"); |
| 133 | |
Yunqing Wang | 3dd5e3e | 2018-07-11 13:22:59 -0700 | [diff] [blame] | 134 | // Allocate memory to store decoded references. Allocate memory with the |
| 135 | // border so that it can be used as a reference. |
| 136 | for (j = 0; j < num_references; j++) { |
| 137 | unsigned int border = AOM_BORDER_IN_PIXELS; |
Jonathan Matthews | a6e4f96 | 2018-07-13 16:01:28 +0100 | [diff] [blame] | 138 | if (!aom_img_alloc_with_border(&reference_images[j], ref_fmt, |
| 139 | frame_res[0], frame_res[1], 32, 8, |
| 140 | border)) { |
Yunqing Wang | 3dd5e3e | 2018-07-11 13:22:59 -0700 | [diff] [blame] | 141 | die("Failed to allocate references."); |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | |
Yunqing Wang | 946ec2b | 2018-06-06 12:34:23 -0700 | [diff] [blame] | 146 | if (aom_codec_control(&codec, AV1_COPY_NEW_FRAME_IMAGE, |
| 147 | &reference_images[i])) |
| 148 | die_codec(&codec, "Failed to copy decoded reference frame"); |
| 149 | |
| 150 | aom_codec_iter_t iter = NULL; |
| 151 | aom_image_t *img = NULL; |
| 152 | while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) { |
| 153 | char name[1024]; |
| 154 | snprintf(name, sizeof(name), "ref_%d.yuv", i); |
| 155 | printf("writing ref image to %s, %d, %d\n", name, img->d_w, img->d_h); |
| 156 | FILE *ref_file = fopen(name, "wb"); |
| 157 | aom_img_write(img, ref_file); |
| 158 | fclose(ref_file); |
| 159 | } |
| 160 | } |
| 161 | |
Yunqing Wang | a2f1728 | 2018-06-18 22:43:08 -0700 | [diff] [blame] | 162 | FILE *infile = aom_video_reader_get_file(reader); |
| 163 | // Record the offset of the first camera image. |
| 164 | const FileOffset camera_frame_pos = ftello(infile); |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 165 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 166 | printf("Loading compressed frames into memory.\n"); |
Yunqing Wang | 946ec2b | 2018-06-06 12:34:23 -0700 | [diff] [blame] | 167 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 168 | // Count the frames in the lightfield. |
| 169 | int num_frames = 0; |
| 170 | while (aom_video_reader_read_frame(reader)) { |
| 171 | ++num_frames; |
| 172 | } |
| 173 | if (num_frames < 1) die("Input light field has no frames."); |
Yunqing Wang | a2f1728 | 2018-06-18 22:43:08 -0700 | [diff] [blame] | 174 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 175 | // Read all of the lightfield frames into memory. |
| 176 | unsigned char **frames = |
| 177 | (unsigned char **)malloc(num_frames * sizeof(unsigned char *)); |
| 178 | size_t *frame_sizes = (size_t *)malloc(num_frames * sizeof(size_t)); |
| 179 | // Seek to the first camera image. |
| 180 | fseeko(infile, camera_frame_pos, SEEK_SET); |
| 181 | for (int f = 0; f < num_frames; ++f) { |
| 182 | aom_video_reader_read_frame(reader); |
| 183 | frame = aom_video_reader_get_frame(reader, &frame_size); |
| 184 | frames[f] = (unsigned char *)malloc(frame_size * sizeof(unsigned char)); |
| 185 | memcpy(frames[f], frame, frame_size); |
| 186 | frame_sizes[f] = frame_size; |
| 187 | } |
| 188 | printf("Read %d frames.\n", num_frames); |
Yunqing Wang | a2f1728 | 2018-06-18 22:43:08 -0700 | [diff] [blame] | 189 | |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 190 | printf("Decoding tile list from file.\n"); |
| 191 | char line[1024]; |
| 192 | FILE *tile_list_fptr = fopen(tile_list_file, "r"); |
| 193 | while ((fgets(line, 1024, tile_list_fptr)) != NULL) { |
| 194 | if (line[0] == 'F') { |
| 195 | continue; |
Yunqing Wang | a2f1728 | 2018-06-18 22:43:08 -0700 | [diff] [blame] | 196 | } |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 197 | |
| 198 | int image_idx; |
| 199 | int ref_idx; |
| 200 | int tc; |
| 201 | int tr; |
| 202 | |
| 203 | sscanf(line, "%d %d %d %d", &image_idx, &ref_idx, &tc, &tr); |
| 204 | if (image_idx >= num_frames) { |
| 205 | die("Tile list image_idx out of bounds: %d >= %d.", image_idx, |
| 206 | num_frames); |
| 207 | } |
| 208 | if (ref_idx >= num_references) { |
| 209 | die("Tile list ref_idx out of bounds: %d >= %d.", ref_idx, |
| 210 | num_references); |
| 211 | } |
| 212 | frame = frames[image_idx]; |
| 213 | frame_size = frame_sizes[image_idx]; |
| 214 | decode_tile(&codec, frame, frame_size, tr, tc, ref_idx, reference_images, |
| 215 | outfile); |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 216 | } |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 217 | |
Yunqing Wang | 946ec2b | 2018-06-06 12:34:23 -0700 | [diff] [blame] | 218 | for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]); |
Ryan Overbeck | 1852139 | 2018-09-21 09:43:24 -0700 | [diff] [blame] | 219 | for (int f = 0; f < num_frames; ++f) { |
| 220 | free(frames[f]); |
| 221 | } |
| 222 | free(frame_sizes); |
| 223 | free(frames); |
Ryan Overbeck | a5fefa7 | 2017-09-19 11:39:10 -0700 | [diff] [blame] | 224 | if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec"); |
| 225 | aom_video_reader_close(reader); |
| 226 | fclose(outfile); |
| 227 | |
| 228 | return EXIT_SUCCESS; |
| 229 | } |