xref: /aosp_15_r20/external/libaom/examples/lightfield_decoder.c (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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
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. There is an optional parameter
19 // allowing to choose the output format, and the supported formats are
20 // YUV1D(default), YUV, and NV12.
21 // After running the lightfield encoder, run lightfield decoder to decode a
22 // batch of tiles:
23 // examples/lightfield_decoder vase10x10.ivf vase_reference.yuv 4 tile_list.txt
24 // 0(optional)
25 // The tile_list.txt is expected to be of the form:
26 // Frame <frame_index0>
27 // <image_index0> <anchor_index0> <tile_col0> <tile_row0>
28 // <image_index1> <anchor_index1> <tile_col1> <tile_row1>
29 // ...
30 // Frame <frame_index1)
31 // ...
32 //
33 // The "Frame" markers indicate a new render frame and thus a new tile list
34 // will be started and the old one flushed.  The image_indexN, anchor_indexN,
35 // tile_colN, and tile_rowN identify an individual tile to be decoded and
36 // to use anchor_indexN anchor image for MCP.
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "aom/aom_decoder.h"
43 #include "aom/aomdx.h"
44 #include "aom_scale/yv12config.h"
45 #include "av1/common/enums.h"
46 #include "common/tools_common.h"
47 #include "common/video_reader.h"
48 
49 enum {
50   YUV1D,  // 1D tile output for conformance test.
51   YUV,    // Tile output in YUV format.
52   NV12,   // Tile output in NV12 format.
53 } UENUM1BYTE(OUTPUT_FORMAT);
54 
55 static const char *exec_name;
56 
usage_exit(void)57 void usage_exit(void) {
58   fprintf(stderr,
59           "Usage: %s <infile> <outfile> <num_references> <tile_list> <output "
60           "format(optional)>\n",
61           exec_name);
62   exit(EXIT_FAILURE);
63 }
64 
65 // Output frame size
66 static const int output_frame_width = 512;
67 static const int output_frame_height = 512;
68 
aom_img_copy_tile(const aom_image_t * src,const aom_image_t * dst,int dst_row_offset,int dst_col_offset)69 static void aom_img_copy_tile(const aom_image_t *src, const aom_image_t *dst,
70                               int dst_row_offset, int dst_col_offset) {
71   const int shift = (src->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 1 : 0;
72   int plane;
73 
74   for (plane = 0; plane < 3; ++plane) {
75     const unsigned char *src_buf = src->planes[plane];
76     const int src_stride = src->stride[plane];
77     unsigned char *dst_buf = dst->planes[plane];
78     const int dst_stride = dst->stride[plane];
79     const int roffset =
80         (plane > 0) ? dst_row_offset >> dst->y_chroma_shift : dst_row_offset;
81     const int coffset =
82         (plane > 0) ? dst_col_offset >> dst->x_chroma_shift : dst_col_offset;
83 
84     // col offset needs to be adjusted for HBD.
85     dst_buf += roffset * dst_stride + (coffset << shift);
86 
87     const int w = (aom_img_plane_width(src, plane) << shift);
88     const int h = aom_img_plane_height(src, plane);
89     int y;
90 
91     for (y = 0; y < h; ++y) {
92       memcpy(dst_buf, src_buf, w);
93       src_buf += src_stride;
94       dst_buf += dst_stride;
95     }
96   }
97 }
98 
decode_tile(aom_codec_ctx_t * codec,const unsigned char * frame,size_t frame_size,int tr,int tc,int ref_idx,aom_image_t * reference_images,aom_image_t * output,int * tile_idx,unsigned int * output_bit_depth,aom_image_t ** img_ptr,int output_format)99 static void decode_tile(aom_codec_ctx_t *codec, const unsigned char *frame,
100                         size_t frame_size, int tr, int tc, int ref_idx,
101                         aom_image_t *reference_images, aom_image_t *output,
102                         int *tile_idx, unsigned int *output_bit_depth,
103                         aom_image_t **img_ptr, int output_format) {
104   AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1_SET_TILE_MODE, 1);
105   AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1D_EXT_TILE_DEBUG, 1);
106   AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1_SET_DECODE_TILE_ROW, tr);
107   AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1_SET_DECODE_TILE_COL, tc);
108 
109   av1_ref_frame_t ref;
110   ref.idx = 0;
111   ref.use_external_ref = 1;
112   ref.img = reference_images[ref_idx];
113   if (AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1_SET_REFERENCE, &ref)) {
114     die_codec(codec, "Failed to set reference frame.");
115   }
116 
117   aom_codec_err_t aom_status = aom_codec_decode(codec, frame, frame_size, NULL);
118   if (aom_status) die_codec(codec, "Failed to decode tile.");
119 
120   aom_codec_iter_t iter = NULL;
121   aom_image_t *img = aom_codec_get_frame(codec, &iter);
122   if (!img) die_codec(codec, "Failed to get frame.");
123   *img_ptr = img;
124 
125   // aom_img_alloc() sets bit_depth as follows:
126   // output->bit_depth = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
127   // Use img->bit_depth(read from bitstream), so that aom_shift_img()
128   // works as expected.
129   output->bit_depth = img->bit_depth;
130   *output_bit_depth = img->bit_depth;
131 
132   if (output_format != YUV1D) {
133     // read out the tile size.
134     unsigned int tile_size = 0;
135     if (AOM_CODEC_CONTROL_TYPECHECKED(codec, AV1D_GET_TILE_SIZE, &tile_size))
136       die_codec(codec, "Failed to get the tile size");
137     const unsigned int tile_width = tile_size >> 16;
138     const unsigned int tile_height = tile_size & 65535;
139     const uint32_t output_frame_width_in_tiles =
140         output_frame_width / tile_width;
141 
142     // Copy the tile to the output frame.
143     const int row_offset =
144         (*tile_idx / output_frame_width_in_tiles) * tile_height;
145     const int col_offset =
146         (*tile_idx % output_frame_width_in_tiles) * tile_width;
147 
148     aom_img_copy_tile(img, output, row_offset, col_offset);
149     (*tile_idx)++;
150   }
151 }
152 
img_write_to_file(const aom_image_t * img,FILE * file,int output_format)153 static void img_write_to_file(const aom_image_t *img, FILE *file,
154                               int output_format) {
155   if (output_format == YUV)
156     aom_img_write(img, file);
157   else if (output_format == NV12)
158     aom_img_write_nv12(img, file);
159   else
160     die("Invalid output format");
161 }
162 
main(int argc,char ** argv)163 int main(int argc, char **argv) {
164   FILE *outfile = NULL;
165   AvxVideoReader *reader = NULL;
166   const AvxVideoInfo *info = NULL;
167   int num_references;
168   aom_img_fmt_t ref_fmt = 0;
169   aom_image_t reference_images[MAX_EXTERNAL_REFERENCES];
170   aom_image_t output;
171   aom_image_t *output_shifted = NULL;
172   size_t frame_size = 0;
173   const unsigned char *frame = NULL;
174   int i, j;
175   const char *tile_list_file = NULL;
176   int output_format = YUV1D;
177   exec_name = argv[0];
178 
179   if (argc < 5) die("Invalid number of arguments.");
180 
181   reader = aom_video_reader_open(argv[1]);
182   if (!reader) die("Failed to open %s for reading.", argv[1]);
183 
184   if (!(outfile = fopen(argv[2], "wb")))
185     die("Failed to open %s for writing.", argv[2]);
186 
187   num_references = (int)strtol(argv[3], NULL, 0);
188   tile_list_file = argv[4];
189 
190   if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
191   if (output_format < YUV1D || output_format > NV12)
192     die("Output format out of range [0, 2]");
193 
194   info = aom_video_reader_get_info(reader);
195 
196   aom_codec_iface_t *decoder;
197   if (info->codec_fourcc == LST_FOURCC)
198     decoder = get_aom_decoder_by_fourcc(AV1_FOURCC);
199   else
200     die("Unknown input codec.");
201   printf("Using %s\n", aom_codec_iface_name(decoder));
202 
203   aom_codec_ctx_t codec;
204   if (aom_codec_dec_init(&codec, decoder, NULL, 0))
205     die_codec(&codec, "Failed to initialize decoder.");
206 
207   if (AOM_CODEC_CONTROL_TYPECHECKED(&codec, AV1D_SET_IS_ANNEXB,
208                                     info->is_annexb)) {
209     die("Failed to set annex b status");
210   }
211 
212   // Decode anchor frames.
213   AOM_CODEC_CONTROL_TYPECHECKED(&codec, AV1_SET_TILE_MODE, 0);
214   for (i = 0; i < num_references; ++i) {
215     aom_video_reader_read_frame(reader);
216     frame = aom_video_reader_get_frame(reader, &frame_size);
217     if (aom_codec_decode(&codec, frame, frame_size, NULL))
218       die_codec(&codec, "Failed to decode frame.");
219 
220     if (i == 0) {
221       if (AOM_CODEC_CONTROL_TYPECHECKED(&codec, AV1D_GET_IMG_FORMAT, &ref_fmt))
222         die_codec(&codec, "Failed to get the image format");
223 
224       int frame_res[2];
225       if (AOM_CODEC_CONTROL_TYPECHECKED(&codec, AV1D_GET_FRAME_SIZE, frame_res))
226         die_codec(&codec, "Failed to get the image frame size");
227 
228       // Allocate memory to store decoded references. Allocate memory with the
229       // border so that it can be used as a reference.
230       for (j = 0; j < num_references; j++) {
231         unsigned int border = AOM_DEC_BORDER_IN_PIXELS;
232         if (!aom_img_alloc_with_border(&reference_images[j], ref_fmt,
233                                        frame_res[0], frame_res[1], 32, 8,
234                                        border)) {
235           die("Failed to allocate references.");
236         }
237       }
238     }
239 
240     if (AOM_CODEC_CONTROL_TYPECHECKED(&codec, AV1_COPY_NEW_FRAME_IMAGE,
241                                       &reference_images[i]))
242       die_codec(&codec, "Failed to copy decoded reference frame");
243 
244     aom_codec_iter_t iter = NULL;
245     aom_image_t *img = NULL;
246     while ((img = aom_codec_get_frame(&codec, &iter)) != NULL) {
247       char name[1024];
248       snprintf(name, sizeof(name), "ref_%d.yuv", i);
249       printf("writing ref image to %s, %u, %u\n", name, img->d_w, img->d_h);
250       FILE *ref_file = fopen(name, "wb");
251       aom_img_write(img, ref_file);
252       fclose(ref_file);
253     }
254   }
255 
256   FILE *infile = aom_video_reader_get_file(reader);
257   // Record the offset of the first camera image.
258   const FileOffset camera_frame_pos = ftello(infile);
259 
260   printf("Loading compressed frames into memory.\n");
261 
262   // Count the frames in the lightfield.
263   int num_frames = 0;
264   while (aom_video_reader_read_frame(reader)) {
265     ++num_frames;
266   }
267   if (num_frames < 1) die("Input light field has no frames.");
268 
269   // Read all of the lightfield frames into memory.
270   unsigned char **frames =
271       (unsigned char **)malloc(num_frames * sizeof(unsigned char *));
272   size_t *frame_sizes = (size_t *)malloc(num_frames * sizeof(size_t));
273   if (!(frames && frame_sizes)) die("Failed to allocate frame data.");
274   // Seek to the first camera image.
275   fseeko(infile, camera_frame_pos, SEEK_SET);
276   for (int f = 0; f < num_frames; ++f) {
277     aom_video_reader_read_frame(reader);
278     frame = aom_video_reader_get_frame(reader, &frame_size);
279     frames[f] = (unsigned char *)malloc(frame_size * sizeof(unsigned char));
280     if (!frames[f]) die("Failed to allocate frame data.");
281     memcpy(frames[f], frame, frame_size);
282     frame_sizes[f] = frame_size;
283   }
284   printf("Read %d frames.\n", num_frames);
285 
286   if (output_format != YUV1D) {
287     // Allocate the output frame.
288     aom_img_fmt_t out_fmt = ref_fmt;
289     if (FORCE_HIGHBITDEPTH_DECODING) out_fmt |= AOM_IMG_FMT_HIGHBITDEPTH;
290     if (!aom_img_alloc(&output, out_fmt, output_frame_width,
291                        output_frame_height, 32))
292       die("Failed to allocate output image.");
293   }
294 
295   printf("Decoding tile list from file.\n");
296   char line[1024];
297   FILE *tile_list_fptr = fopen(tile_list_file, "r");
298   if (!tile_list_fptr) die_codec(&codec, "Failed to open tile list file.");
299   int tile_list_cnt = 0;
300   int tile_list_writes = 0;
301   int tile_idx = 0;
302   aom_image_t *out = NULL;
303   unsigned int output_bit_depth = 0;
304 
305   while ((fgets(line, 1024, tile_list_fptr)) != NULL) {
306     if (line[0] == 'F') {
307       if (output_format != YUV1D) {
308         // Write out the tile list.
309         if (tile_list_cnt) {
310           out = &output;
311           if (output_bit_depth != 0) {
312             if (!aom_shift_img(output_bit_depth, &out, &output_shifted)) {
313               die("Error allocating image");
314             }
315           }
316           img_write_to_file(out, outfile, output_format);
317           tile_list_writes++;
318         }
319 
320         tile_list_cnt++;
321         tile_idx = 0;
322         // Then memset the frame.
323         memset(output.img_data, 0, output.sz);
324       }
325       continue;
326     }
327 
328     int image_idx, ref_idx, tc, tr;
329     sscanf(line, "%d %d %d %d", &image_idx, &ref_idx, &tc, &tr);
330     if (image_idx >= num_frames) {
331       die("Tile list image_idx out of bounds: %d >= %d.", image_idx,
332           num_frames);
333     }
334     if (ref_idx >= num_references) {
335       die("Tile list ref_idx out of bounds: %d >= %d.", ref_idx,
336           num_references);
337     }
338     frame = frames[image_idx];
339     frame_size = frame_sizes[image_idx];
340 
341     aom_image_t *img = NULL;
342     decode_tile(&codec, frame, frame_size, tr, tc, ref_idx, reference_images,
343                 &output, &tile_idx, &output_bit_depth, &img, output_format);
344     if (output_format == YUV1D) {
345       out = img;
346       if (output_bit_depth != 0) {
347         if (!aom_shift_img(output_bit_depth, &out, &output_shifted)) {
348           die("Error allocating image");
349         }
350       }
351       aom_img_write(out, outfile);
352     }
353   }
354 
355   if (output_format != YUV1D) {
356     // Write out the last tile list.
357     if (tile_list_writes < tile_list_cnt) {
358       out = &output;
359       if (output_bit_depth != 0) {
360         if (!aom_shift_img(output_bit_depth, &out, &output_shifted)) {
361           die("Error allocating image");
362         }
363       }
364       img_write_to_file(out, outfile, output_format);
365     }
366   }
367 
368   if (output_shifted) aom_img_free(output_shifted);
369   if (output_format != YUV1D) aom_img_free(&output);
370   for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]);
371   for (int f = 0; f < num_frames; ++f) {
372     free(frames[f]);
373   }
374   free(frame_sizes);
375   free(frames);
376   if (aom_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec");
377   aom_video_reader_close(reader);
378   fclose(outfile);
379 
380   return EXIT_SUCCESS;
381 }
382