void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) {
    wordcount_t i;
    for (i = 0; i < curve->num_bytes; ++i) {
        compressed[i+1] = public_key[i];
    }
#if uECC_VLI_NATIVE_LITTLE_ENDIAN
    compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01);
#else
    compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01);
#endif
}

Fix for #168


uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result,
                                        const uECC_word_t *left,
                                        const uECC_word_t *right,
                                        uECC_Curve curve) {
    uECC_word_t product[2 * uECC_MAX_WORDS];
    uECC_vli_mult(product, left, right, curve->num_words);
#if (uECC_OPTIMIZATION_LEVEL > 0)
    curve->mmod_fast(result, product);
#else
    uECC_vli_mmod(result, product, curve->p, curve->num_words);
#endif
}

Fix for #168


uECC_VLI_API cmpresult_t uECC_vli_cmp(const uECC_word_t *left,
                                      const uECC_word_t *right,
                                      wordcount_t num_words) {
    uECC_word_t tmp[uECC_MAX_WORDS];
    uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words);
    uECC_word_t equal = uECC_vli_isZero(tmp, num_words);
    return (!equal - 2 * neg);
}

Fix for #168


void uECC_point_mult(uECC_word_t *result,
                     const uECC_word_t *point,
                     const uECC_word_t *scalar,
                     uECC_Curve curve) {
    uECC_word_t tmp1[uECC_MAX_WORDS];
    uECC_word_t tmp2[uECC_MAX_WORDS];
    uECC_word_t *p2[2] = {tmp1, tmp2};
    uECC_word_t carry = regularize_k(scalar, tmp1, tmp2, curve);

    EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve);
}

Fix for #168


static void muladd(uECC_word_t a,
                   uECC_word_t b,
                   uECC_word_t *r0,
                   uECC_word_t *r1,
                   uECC_word_t *r2) {
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
    uint64_t a0 = a & 0xffffffffull;
    uint64_t a1 = a >> 32;
    uint64_t b0 = b & 0xffffffffull;
    uint64_t b1 = b >> 32;

    uint64_t i0 = a0 * b0;
    uint64_t i1 = a0 * b1;
    uint64_t i2 = a1 * b0;
    uint64_t i3 = a1 * b1;

    uint64_t p0, p1;

    i2 += (i0 >> 32);
    i2 += i1;
    if (i2 < i1) { /* overflow */
        i3 += 0x100000000ull;
    }

    p0 = (i0 & 0xffffffffull) | (i2 << 32);
    p1 = i3 + (i2 >> 32);

    *r0 += p0;
    *r1 += (p1 + (*r0 < p0));
    *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0));
#else
    uECC_dword_t p = (uECC_dword_t)a * b;
    uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0;
    r01 += p;
    *r2 += (r01 < p);
    *r1 = r01 >> uECC_WORD_BITS;
    *r0 = (uECC_word_t)r01;
#endif
}

Fix for #168


  explicit BoostedTreesDeserializeEnsembleOp(OpKernelConstruction* context)
      : OpKernel(context) {}

Prevent use after free.

A very old version of the code used `result` as a simple pointer to a resource. Two years later, the pointer got changed to a `unique_ptr` but author forgot to remove the call to `Unref`. Three years after that, we finally uncover the UAF.

PiperOrigin-RevId: 387924872
Change-Id: I70fb6f199164de49fac20c168132a07b84903f9b


  explicit BoostedTreesCreateEnsembleOp(OpKernelConstruction* context)
      : OpKernel(context) {}


  void Compute(OpKernelContext* context) override {
    // Looks up the resource.
    core::RefCountPtr<BoostedTreesEnsembleResource> tree_ensemble_resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &tree_ensemble_resource));
    tf_shared_lock l(*tree_ensemble_resource->get_mutex());

    // Sets the outputs.
    const int num_trees = tree_ensemble_resource->num_trees();
    const int num_finalized_trees =
        (num_trees <= 0 ||
         tree_ensemble_resource->IsTreeFinalized(num_trees - 1))
            ? num_trees
            : num_trees - 1;
    const int num_attempted_layers =
        tree_ensemble_resource->GetNumLayersAttempted();

    // growing_metadata
    Tensor* output_stamp_token_t = nullptr;
    Tensor* output_num_trees_t = nullptr;
    Tensor* output_num_finalized_trees_t = nullptr;
    Tensor* output_num_attempted_layers_t = nullptr;
    Tensor* output_last_layer_nodes_range_t = nullptr;

    OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape(),
                                                     &output_stamp_token_t));
    OP_REQUIRES_OK(context, context->allocate_output(1, TensorShape(),
                                                     &output_num_trees_t));
    OP_REQUIRES_OK(context,
                   context->allocate_output(2, TensorShape(),
                                            &output_num_finalized_trees_t));
    OP_REQUIRES_OK(context,
                   context->allocate_output(3, TensorShape(),
                                            &output_num_attempted_layers_t));
    OP_REQUIRES_OK(context, context->allocate_output(
                                4, {2}, &output_last_layer_nodes_range_t));

    output_stamp_token_t->scalar<int64>()() = tree_ensemble_resource->stamp();
    output_num_trees_t->scalar<int32>()() = num_trees;
    output_num_finalized_trees_t->scalar<int32>()() = num_finalized_trees;
    output_num_attempted_layers_t->scalar<int32>()() = num_attempted_layers;

    int32_t range_start;
    int32_t range_end;
    tree_ensemble_resource->GetLastLayerNodesRange(&range_start, &range_end);

    output_last_layer_nodes_range_t->vec<int32>()(0) = range_start;
    // For a completely empty ensemble, this will be 0. To make it a valid range
    // we add this max cond.
    output_last_layer_nodes_range_t->vec<int32>()(1) = std::max(1, range_end);
  }


  explicit BoostedTreesGetEnsembleStatesOp(OpKernelConstruction* context)
      : OpKernel(context) {}


  void Compute(OpKernelContext* context) override {
    core::RefCountPtr<BoostedTreesEnsembleResource> tree_ensemble_resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &tree_ensemble_resource));
    tf_shared_lock l(*tree_ensemble_resource->get_mutex());
    Tensor* output_stamp_token_t = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape(),
                                                     &output_stamp_token_t));
    output_stamp_token_t->scalar<int64>()() = tree_ensemble_resource->stamp();
    Tensor* output_proto_t = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output(1, TensorShape(), &output_proto_t));
    output_proto_t->scalar<tstring>()() =
        tree_ensemble_resource->SerializeAsString();
  }


  void Compute(OpKernelContext* context) override {
    core::RefCountPtr<BoostedTreesEnsembleResource> tree_ensemble_resource;
    OP_REQUIRES_OK(context, LookupResource(context, HandleFromInput(context, 0),
                                           &tree_ensemble_resource));
    mutex_lock l(*tree_ensemble_resource->get_mutex());

    // Get the stamp token.
    const Tensor* stamp_token_t;
    OP_REQUIRES_OK(context, context->input("stamp_token", &stamp_token_t));
    int64_t stamp_token = stamp_token_t->scalar<int64>()();

    // Get the tree ensemble proto.
    const Tensor* tree_ensemble_serialized_t;
    OP_REQUIRES_OK(context, context->input("tree_ensemble_serialized",
                                           &tree_ensemble_serialized_t));
    // Deallocate all the previous objects on the resource.
    tree_ensemble_resource->Reset();
    OP_REQUIRES(
        context,
        tree_ensemble_resource->InitFromSerialized(
            tree_ensemble_serialized_t->scalar<tstring>()(), stamp_token),
        errors::InvalidArgument("Unable to parse tree ensemble proto."));
  }


  explicit BoostedTreesSerializeEnsembleOp(OpKernelConstruction* context)
      : OpKernel(context) {}


  void Compute(OpKernelContext* context) override {
    // Get the stamp token.
    const Tensor* stamp_token_t;
    OP_REQUIRES_OK(context, context->input("stamp_token", &stamp_token_t));
    int64_t stamp_token = stamp_token_t->scalar<int64>()();

    // Get the tree ensemble proto.
    const Tensor* tree_ensemble_serialized_t;
    OP_REQUIRES_OK(context, context->input("tree_ensemble_serialized",
                                           &tree_ensemble_serialized_t));
    std::unique_ptr<BoostedTreesEnsembleResource> result(
        new BoostedTreesEnsembleResource());
    if (!result->InitFromSerialized(
            tree_ensemble_serialized_t->scalar<tstring>()(), stamp_token)) {
      result->Unref();
      result.release();  // Needed due to the `->Unref` above, to prevent UAF
      OP_REQUIRES(
          context, false,
          errors::InvalidArgument("Unable to parse tree ensemble proto."));
    }

    // Only create one, if one does not exist already. Report status for all
    // other exceptions.
    auto status =
        CreateResource(context, HandleFromInput(context, 0), result.release());
    if (status.code() != tensorflow::error::ALREADY_EXISTS) {
      OP_REQUIRES_OK(context, status);
    }
  }


static int cmd_i2c_set_bus_num(unsigned int busnum)
{
	struct udevice *bus;
	int ret;

	ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus);
	if (ret) {
		debug("%s: No bus %d\n", __func__, busnum);
		return ret;
	}
	i2c_cur_bus = bus;

	return 0;
}

i2c: fix stack buffer overflow vulnerability in i2c md command

When running "i2c md 0 0 80000100", the function do_i2c_md parses the
length into an unsigned int variable named length. The value is then
moved to a signed variable:

    int nbytes = length;
    #define DISP_LINE_LEN 16
    int linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
    ret = dm_i2c_read(dev, addr, linebuf, linebytes);

On systems where integers are 32 bits wide, 0x80000100 is a negative
value to "nbytes > DISP_LINE_LEN" is false and linebytes gets assigned
0x80000100 instead of 16.

The consequence is that the function which reads from the i2c device
(dm_i2c_read or i2c_read) is called with a 16-byte stack buffer to fill
but with a size parameter which is too large. In some cases, this could
trigger a crash. But with some i2c drivers, such as drivers/i2c/nx_i2c.c
(used with "nexell,s5pxx18-i2c" bus), the size is actually truncated to
a 16-bit integer. This is because function i2c_transfer expects an
unsigned short length. In such a case, an attacker who can control the
response of an i2c device can overwrite the return address of a function
and execute arbitrary code through Return-Oriented Programming.

Fix this issue by using unsigned integers types in do_i2c_md. While at
it, make also alen unsigned, as signed sizes can cause vulnerabilities
when people forgot to check that they can be negative.

Signed-off-by: Nicolas Iooss <nicolas.iooss+uboot@ledger.fr>
Reviewed-by: Heiko Schocher <hs@denx.de>


static int do_i2c_probe(struct cmd_tbl *cmdtp, int flag, int argc,
			char *const argv[])
{
	int j;
	int addr = -1;
	int found = 0;
#if defined(CONFIG_SYS_I2C_NOPROBES)
	int k, skip;
	unsigned int bus = GET_BUS_NUM;
#endif	/* NOPROBES */
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *bus, *dev;

	if (i2c_get_cur_bus(&bus))
		return CMD_RET_FAILURE;
#endif

	if (argc == 2)
		addr = simple_strtol(argv[1], 0, 16);

	puts ("Valid chip addresses:");
	for (j = 0; j < 128; j++) {
		if ((0 <= addr) && (j != addr))
			continue;

#if defined(CONFIG_SYS_I2C_NOPROBES)
		skip = 0;
		for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) {
			if (COMPARE_BUS(bus, k) && COMPARE_ADDR(j, k)) {
				skip = 1;
				break;
			}
		}
		if (skip)
			continue;
#endif
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_probe(bus, j, 0, &dev);
#else
		ret = i2c_probe(j);
#endif
		if (ret == 0) {
			printf(" %02X", j);
			found++;
		}
	}
	putc ('\n');

#if defined(CONFIG_SYS_I2C_NOPROBES)
	puts ("Excluded chip addresses:");
	for (k = 0; k < ARRAY_SIZE(i2c_no_probes); k++) {
		if (COMPARE_BUS(bus,k))
			printf(" %02X", NO_PROBE_ADDR(k));
	}
	putc ('\n');
#endif

	return (0 == found);
}


static int do_i2c(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	struct cmd_tbl *c;

#ifdef CONFIG_NEEDS_MANUAL_RELOC
	i2c_reloc();
#endif

	if (argc < 2)
		return CMD_RET_USAGE;

	/* Strip off leading 'i2c' command argument */
	argc--;
	argv++;

	c = find_cmd_tbl(argv[0], &cmd_i2c_sub[0], ARRAY_SIZE(cmd_i2c_sub));

	if (c)
		return c->cmd(cmdtp, flag, argc, argv);
	else
		return CMD_RET_USAGE;
}


static void show_bus(struct udevice *bus)
{
	struct udevice *dev;

	printf("Bus %d:\t%s", dev_seq(bus), bus->name);
	if (device_active(bus))
		printf("  (active %d)", dev_seq(bus));
	printf("\n");
	for (device_find_first_child(bus, &dev);
	     dev;
	     device_find_next_child(&dev)) {
		struct dm_i2c_chip *chip = dev_get_parent_plat(dev);

		printf("   %02x: %s, offset len %x, flags %x\n",
		       chip->chip_addr, dev->name, chip->offset_len,
		       chip->flags);
	}
}


static int do_i2c_crc(struct cmd_tbl *cmdtp, int flag, int argc,
		      char *const argv[])
{
	uint	chip;
	ulong	addr;
	uint	alen;
	uint	count;
	uchar	byte;
	ulong	crc;
	ulong	err;
	int ret = 0;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	if (argc < 4)
		return CMD_RET_USAGE;

	/*
	 * Chip is always specified.
	 */
	chip = hextoul(argv[1], NULL);

	/*
	 * Address is always specified.
	 */
	addr = hextoul(argv[2], NULL);
	alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
	if (alen > 3)
		return CMD_RET_USAGE;

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);
#endif
	/*
	 * Count is always specified
	 */
	count = hextoul(argv[3], NULL);

	printf ("CRC32 for %08lx ... %08lx ==> ", addr, addr + count - 1);
	/*
	 * CRC a byte at a time.  This is going to be slooow, but hey, the
	 * memories are small and slow too so hopefully nobody notices.
	 */
	crc = 0;
	err = 0;
	while (count-- > 0) {
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_read(dev, addr, &byte, 1);
#else
		ret = i2c_read(chip, addr, alen, &byte, 1);
#endif
		if (ret)
			err++;
		crc = crc32(crc, &byte, 1);
		addr++;
	}
	if (err > 0)
		i2c_report_err(ret, I2C_ERR_READ);
	else
		printf ("%08lx\n", crc);

	return 0;
}


static uint get_alen(char *arg, uint default_len)
{
	uint	j;
	uint	alen;

	alen = default_len;
	for (j = 0; j < 8; j++) {
		if (arg[j] == '.') {
			alen = arg[j+1] - '0';
			break;
		} else if (arg[j] == '\0')
			break;
	}
	return alen;
}


static int do_sdram(struct cmd_tbl *cmdtp, int flag, int argc,
		    char *const argv[])
{
	enum { unknown, EDO, SDRAM, DDR, DDR2, DDR3, DDR4 } type;

	uint	chip;
	u_char	data[128];
	u_char	cksum;
	int	j, ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	static const char *decode_CAS_DDR2[] = {
		" TBD", " 6", " 5", " 4", " 3", " 2", " TBD", " TBD"
	};

	static const char *decode_CAS_default[] = {
		" TBD", " 7", " 6", " 5", " 4", " 3", " 2", " 1"
	};

	static const char *decode_CS_WE_default[] = {
		" TBD", " 6", " 5", " 4", " 3", " 2", " 1", " 0"
	};

	static const char *decode_byte21_default[] = {
		"  TBD (bit 7)\n",
		"  Redundant row address\n",
		"  Differential clock input\n",
		"  Registerd DQMB inputs\n",
		"  Buffered DQMB inputs\n",
		"  On-card PLL\n",
		"  Registered address/control lines\n",
		"  Buffered address/control lines\n"
	};

	static const char *decode_byte22_DDR2[] = {
		"  TBD (bit 7)\n",
		"  TBD (bit 6)\n",
		"  TBD (bit 5)\n",
		"  TBD (bit 4)\n",
		"  TBD (bit 3)\n",
		"  Supports partial array self refresh\n",
		"  Supports 50 ohm ODT\n",
		"  Supports weak driver\n"
	};

	static const char *decode_row_density_DDR2[] = {
		"512 MiB", "256 MiB", "128 MiB", "16 GiB",
		"8 GiB", "4 GiB", "2 GiB", "1 GiB"
	};

	static const char *decode_row_density_default[] = {
		"512 MiB", "256 MiB", "128 MiB", "64 MiB",
		"32 MiB", "16 MiB", "8 MiB", "4 MiB"
	};

	if (argc < 2)
		return CMD_RET_USAGE;

	/*
	 * Chip is always specified.
	 */
	chip = hextoul(argv[1], NULL);

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret)
		ret = dm_i2c_read(dev, 0, data, sizeof(data));
#else
	ret = i2c_read(chip, 0, 1, data, sizeof(data));
#endif
	if (ret) {
		puts ("No SDRAM Serial Presence Detect found.\n");
		return 1;
	}

	cksum = 0;
	for (j = 0; j < 63; j++) {
		cksum += data[j];
	}
	if (cksum != data[63]) {
		printf ("WARNING: Configuration data checksum failure:\n"
			"  is 0x%02x, calculated 0x%02x\n", data[63], cksum);
	}
	printf ("SPD data revision            %d.%d\n",
		(data[62] >> 4) & 0x0F, data[62] & 0x0F);
	printf ("Bytes used                   0x%02X\n", data[0]);
	printf ("Serial memory size           0x%02X\n", 1 << data[1]);

	puts ("Memory type                  ");
	switch (data[2]) {
	case 2:
		type = EDO;
		puts ("EDO\n");
		break;
	case 4:
		type = SDRAM;
		puts ("SDRAM\n");
		break;
	case 7:
		type = DDR;
		puts("DDR\n");
		break;
	case 8:
		type = DDR2;
		puts ("DDR2\n");
		break;
	case 11:
		type = DDR3;
		puts("DDR3\n");
		break;
	case 12:
		type = DDR4;
		puts("DDR4\n");
		break;
	default:
		type = unknown;
		puts ("unknown\n");
		break;
	}

	puts ("Row address bits             ");
	if ((data[3] & 0x00F0) == 0)
		printf ("%d\n", data[3] & 0x0F);
	else
		printf ("%d/%d\n", data[3] & 0x0F, (data[3] >> 4) & 0x0F);

	puts ("Column address bits          ");
	if ((data[4] & 0x00F0) == 0)
		printf ("%d\n", data[4] & 0x0F);
	else
		printf ("%d/%d\n", data[4] & 0x0F, (data[4] >> 4) & 0x0F);

	switch (type) {
	case DDR2:
		printf ("Number of ranks              %d\n",
			(data[5] & 0x07) + 1);
		break;
	default:
		printf ("Module rows                  %d\n", data[5]);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("Module data width            %d bits\n", data[6]);
		break;
	default:
		printf ("Module data width            %d bits\n",
			(data[7] << 8) | data[6]);
		break;
	}

	puts ("Interface signal levels      ");
	switch(data[8]) {
		case 0:  puts ("TTL 5.0 V\n");	break;
		case 1:  puts ("LVTTL\n");	break;
		case 2:  puts ("HSTL 1.5 V\n");	break;
		case 3:  puts ("SSTL 3.3 V\n");	break;
		case 4:  puts ("SSTL 2.5 V\n");	break;
		case 5:  puts ("SSTL 1.8 V\n");	break;
		default: puts ("unknown\n");	break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM cycle time             ");
		print_ddr2_tcyc (data[9]);
		break;
	default:
		printf ("SDRAM cycle time             %d.%d ns\n",
			(data[9] >> 4) & 0x0F, data[9] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM access time            0.%d%d ns\n",
			(data[10] >> 4) & 0x0F, data[10] & 0x0F);
		break;
	default:
		printf ("SDRAM access time            %d.%d ns\n",
			(data[10] >> 4) & 0x0F, data[10] & 0x0F);
		break;
	}

	puts ("EDC configuration            ");
	switch (data[11]) {
		case 0:  puts ("None\n");	break;
		case 1:  puts ("Parity\n");	break;
		case 2:  puts ("ECC\n");	break;
		default: puts ("unknown\n");	break;
	}

	if ((data[12] & 0x80) == 0)
		puts ("No self refresh, rate        ");
	else
		puts ("Self refresh, rate           ");

	switch(data[12] & 0x7F) {
		case 0:  puts ("15.625 us\n");	break;
		case 1:  puts ("3.9 us\n");	break;
		case 2:  puts ("7.8 us\n");	break;
		case 3:  puts ("31.3 us\n");	break;
		case 4:  puts ("62.5 us\n");	break;
		case 5:  puts ("125 us\n");	break;
		default: puts ("unknown\n");	break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM width (primary)        %d\n", data[13]);
		break;
	default:
		printf ("SDRAM width (primary)        %d\n", data[13] & 0x7F);
		if ((data[13] & 0x80) != 0) {
			printf ("  (second bank)              %d\n",
				2 * (data[13] & 0x7F));
		}
		break;
	}

	switch (type) {
	case DDR2:
		if (data[14] != 0)
			printf ("EDC width                    %d\n", data[14]);
		break;
	default:
		if (data[14] != 0) {
			printf ("EDC width                    %d\n",
				data[14] & 0x7F);

			if ((data[14] & 0x80) != 0) {
				printf ("  (second bank)              %d\n",
					2 * (data[14] & 0x7F));
			}
		}
		break;
	}

	if (DDR2 != type) {
		printf ("Min clock delay, back-to-back random column addresses "
			"%d\n", data[15]);
	}

	puts ("Burst length(s)             ");
	if (data[16] & 0x80) puts (" Page");
	if (data[16] & 0x08) puts (" 8");
	if (data[16] & 0x04) puts (" 4");
	if (data[16] & 0x02) puts (" 2");
	if (data[16] & 0x01) puts (" 1");
	putc ('\n');
	printf ("Number of banks              %d\n", data[17]);

	switch (type) {
	case DDR2:
		puts ("CAS latency(s)              ");
		decode_bits (data[18], decode_CAS_DDR2, 0);
		putc ('\n');
		break;
	default:
		puts ("CAS latency(s)              ");
		decode_bits (data[18], decode_CAS_default, 0);
		putc ('\n');
		break;
	}

	if (DDR2 != type) {
		puts ("CS latency(s)               ");
		decode_bits (data[19], decode_CS_WE_default, 0);
		putc ('\n');
	}

	if (DDR2 != type) {
		puts ("WE latency(s)               ");
		decode_bits (data[20], decode_CS_WE_default, 0);
		putc ('\n');
	}

	switch (type) {
	case DDR2:
		puts ("Module attributes:\n");
		if (data[21] & 0x80)
			puts ("  TBD (bit 7)\n");
		if (data[21] & 0x40)
			puts ("  Analysis probe installed\n");
		if (data[21] & 0x20)
			puts ("  TBD (bit 5)\n");
		if (data[21] & 0x10)
			puts ("  FET switch external enable\n");
		printf ("  %d PLLs on DIMM\n", (data[21] >> 2) & 0x03);
		if (data[20] & 0x11) {
			printf ("  %d active registers on DIMM\n",
				(data[21] & 0x03) + 1);
		}
		break;
	default:
		puts ("Module attributes:\n");
		if (!data[21])
			puts ("  (none)\n");
		else
			decode_bits (data[21], decode_byte21_default, 0);
		break;
	}

	switch (type) {
	case DDR2:
		decode_bits (data[22], decode_byte22_DDR2, 0);
		break;
	default:
		puts ("Device attributes:\n");
		if (data[22] & 0x80) puts ("  TBD (bit 7)\n");
		if (data[22] & 0x40) puts ("  TBD (bit 6)\n");
		if (data[22] & 0x20) puts ("  Upper Vcc tolerance 5%\n");
		else                 puts ("  Upper Vcc tolerance 10%\n");
		if (data[22] & 0x10) puts ("  Lower Vcc tolerance 5%\n");
		else                 puts ("  Lower Vcc tolerance 10%\n");
		if (data[22] & 0x08) puts ("  Supports write1/read burst\n");
		if (data[22] & 0x04) puts ("  Supports precharge all\n");
		if (data[22] & 0x02) puts ("  Supports auto precharge\n");
		if (data[22] & 0x01) puts ("  Supports early RAS# precharge\n");
		break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM cycle time (2nd highest CAS latency)        ");
		print_ddr2_tcyc (data[23]);
		break;
	default:
		printf ("SDRAM cycle time (2nd highest CAS latency)        %d."
			"%d ns\n", (data[23] >> 4) & 0x0F, data[23] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM access from clock (2nd highest CAS latency) 0."
			"%d%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F);
		break;
	default:
		printf ("SDRAM access from clock (2nd highest CAS latency) %d."
			"%d ns\n", (data[24] >> 4) & 0x0F, data[24] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM cycle time (3rd highest CAS latency)        ");
		print_ddr2_tcyc (data[25]);
		break;
	default:
		printf ("SDRAM cycle time (3rd highest CAS latency)        %d."
			"%d ns\n", (data[25] >> 4) & 0x0F, data[25] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("SDRAM access from clock (3rd highest CAS latency) 0."
			"%d%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F);
		break;
	default:
		printf ("SDRAM access from clock (3rd highest CAS latency) %d."
			"%d ns\n", (data[26] >> 4) & 0x0F, data[26] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("Minimum row precharge        %d.%02d ns\n",
			(data[27] >> 2) & 0x3F, 25 * (data[27] & 0x03));
		break;
	default:
		printf ("Minimum row precharge        %d ns\n", data[27]);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("Row active to row active min %d.%02d ns\n",
			(data[28] >> 2) & 0x3F, 25 * (data[28] & 0x03));
		break;
	default:
		printf ("Row active to row active min %d ns\n", data[28]);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("RAS to CAS delay min         %d.%02d ns\n",
			(data[29] >> 2) & 0x3F, 25 * (data[29] & 0x03));
		break;
	default:
		printf ("RAS to CAS delay min         %d ns\n", data[29]);
		break;
	}

	printf ("Minimum RAS pulse width      %d ns\n", data[30]);

	switch (type) {
	case DDR2:
		puts ("Density of each row          ");
		decode_bits (data[31], decode_row_density_DDR2, 1);
		putc ('\n');
		break;
	default:
		puts ("Density of each row          ");
		decode_bits (data[31], decode_row_density_default, 1);
		putc ('\n');
		break;
	}

	switch (type) {
	case DDR2:
		puts ("Command and Address setup    ");
		if (data[32] >= 0xA0) {
			printf ("1.%d%d ns\n",
				((data[32] >> 4) & 0x0F) - 10, data[32] & 0x0F);
		} else {
			printf ("0.%d%d ns\n",
				((data[32] >> 4) & 0x0F), data[32] & 0x0F);
		}
		break;
	default:
		printf ("Command and Address setup    %c%d.%d ns\n",
			(data[32] & 0x80) ? '-' : '+',
			(data[32] >> 4) & 0x07, data[32] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		puts ("Command and Address hold     ");
		if (data[33] >= 0xA0) {
			printf ("1.%d%d ns\n",
				((data[33] >> 4) & 0x0F) - 10, data[33] & 0x0F);
		} else {
			printf ("0.%d%d ns\n",
				((data[33] >> 4) & 0x0F), data[33] & 0x0F);
		}
		break;
	default:
		printf ("Command and Address hold     %c%d.%d ns\n",
			(data[33] & 0x80) ? '-' : '+',
			(data[33] >> 4) & 0x07, data[33] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("Data signal input setup      0.%d%d ns\n",
			(data[34] >> 4) & 0x0F, data[34] & 0x0F);
		break;
	default:
		printf ("Data signal input setup      %c%d.%d ns\n",
			(data[34] & 0x80) ? '-' : '+',
			(data[34] >> 4) & 0x07, data[34] & 0x0F);
		break;
	}

	switch (type) {
	case DDR2:
		printf ("Data signal input hold       0.%d%d ns\n",
			(data[35] >> 4) & 0x0F, data[35] & 0x0F);
		break;
	default:
		printf ("Data signal input hold       %c%d.%d ns\n",
			(data[35] & 0x80) ? '-' : '+',
			(data[35] >> 4) & 0x07, data[35] & 0x0F);
		break;
	}

	puts ("Manufacturer's JEDEC ID      ");
	for (j = 64; j <= 71; j++)
		printf ("%02X ", data[j]);
	putc ('\n');
	printf ("Manufacturing Location       %02X\n", data[72]);
	puts ("Manufacturer's Part Number   ");
	for (j = 73; j <= 90; j++)
		printf ("%02X ", data[j]);
	putc ('\n');
	printf ("Revision Code                %02X %02X\n", data[91], data[92]);
	printf ("Manufacturing Date           %02X %02X\n", data[93], data[94]);
	puts ("Assembly Serial Number       ");
	for (j = 95; j <= 98; j++)
		printf ("%02X ", data[j]);
	putc ('\n');

	if (DDR2 != type) {
		printf ("Speed rating                 PC%d\n",
			data[126] == 0x66 ? 66 : data[126]);
	}
	return 0;
}


static int do_i2c_nm(struct cmd_tbl *cmdtp, int flag, int argc,
		     char *const argv[])
{
	return mod_i2c_mem (cmdtp, 0, flag, argc, argv);
}


static int do_i2c_loop(struct cmd_tbl *cmdtp, int flag, int argc,
		       char *const argv[])
{
	uint	chip;
	uint	alen;
	uint	addr;
	uint	length;
	u_char	bytes[16];
	int	delay;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	if (argc < 3)
		return CMD_RET_USAGE;

	/*
	 * Chip is always specified.
	 */
	chip = hextoul(argv[1], NULL);

	/*
	 * Address is always specified.
	 */
	addr = hextoul(argv[2], NULL);
	alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
	if (alen > 3)
		return CMD_RET_USAGE;
#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_WRITE);
#endif

	/*
	 * Length is the number of objects, not number of bytes.
	 */
	length = 1;
	length = hextoul(argv[3], NULL);
	if (length > sizeof(bytes))
		length = sizeof(bytes);

	/*
	 * The delay time (uSec) is optional.
	 */
	delay = 1000;
	if (argc > 3)
		delay = dectoul(argv[4], NULL);
	/*
	 * Run the loop...
	 */
	while (1) {
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_read(dev, addr, bytes, length);
#else
		ret = i2c_read(chip, addr, alen, bytes, length);
#endif
		if (ret)
			i2c_report_err(ret, I2C_ERR_READ);
		udelay(delay);
	}

	/* NOTREACHED */
	return 0;
}


static __maybe_unused void i2c_reloc(void)
{
	static int relocated;

	if (!relocated) {
		fixup_cmdtable(cmd_i2c_sub, ARRAY_SIZE(cmd_i2c_sub));
		relocated = 1;
	};
}


static int do_i2c_write(struct cmd_tbl *cmdtp, int flag, int argc,
			char *const argv[])
{
	uint	chip;
	uint	devaddr, length;
	uint	alen;
	u_char  *memaddr;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
	struct dm_i2c_chip *i2c_chip;
#endif

	if ((argc < 5) || (argc > 6))
		return cmd_usage(cmdtp);

	/*
	 * memaddr is the address where to store things in memory
	 */
	memaddr = (u_char *)hextoul(argv[1], NULL);

	/*
	 * I2C chip address
	 */
	chip = hextoul(argv[2], NULL);

	/*
	 * I2C data address within the chip.  This can be 1 or
	 * 2 bytes long.  Some day it might be 3 bytes long :-).
	 */
	devaddr = hextoul(argv[3], NULL);
	alen = get_alen(argv[3], DEFAULT_ADDR_LEN);
	if (alen > 3)
		return cmd_usage(cmdtp);

	/*
	 * Length is the number of bytes.
	 */
	length = hextoul(argv[4], NULL);

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_WRITE);
	i2c_chip = dev_get_parent_plat(dev);
	if (!i2c_chip)
		return i2c_report_err(ret, I2C_ERR_WRITE);
#endif

	if (argc == 6 && !strcmp(argv[5], "-s")) {
		/*
		 * Write all bytes in a single I2C transaction. If the target
		 * device is an EEPROM, it is your responsibility to not cross
		 * a page boundary. No write delay upon completion, take this
		 * into account if linking commands.
		 */
#if CONFIG_IS_ENABLED(DM_I2C)
		i2c_chip->flags &= ~DM_I2C_CHIP_WR_ADDRESS;
		ret = dm_i2c_write(dev, devaddr, memaddr, length);
#else
		ret = i2c_write(chip, devaddr, alen, memaddr, length);
#endif
		if (ret)
			return i2c_report_err(ret, I2C_ERR_WRITE);
	} else {
		/*
		 * Repeated addressing - perform <length> separate
		 * write transactions of one byte each
		 */
		while (length-- > 0) {
#if CONFIG_IS_ENABLED(DM_I2C)
			i2c_chip->flags |= DM_I2C_CHIP_WR_ADDRESS;
			ret = dm_i2c_write(dev, devaddr++, memaddr++, 1);
#else
			ret = i2c_write(chip, devaddr++, alen, memaddr++, 1);
#endif
			if (ret)
				return i2c_report_err(ret, I2C_ERR_WRITE);
/*
 * No write delay with FRAM devices.
 */
#if !defined(CONFIG_SYS_I2C_FRAM)
			udelay(11000);
#endif
		}
	}
	return 0;
}


static int do_i2c_read(struct cmd_tbl *cmdtp, int flag, int argc,
		       char *const argv[])
{
	uint	chip;
	uint	devaddr, length;
	uint	alen;
	u_char  *memaddr;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	if (argc != 5)
		return CMD_RET_USAGE;

	/*
	 * I2C chip address
	 */
	chip = hextoul(argv[1], NULL);

	/*
	 * I2C data address within the chip.  This can be 1 or
	 * 2 bytes long.  Some day it might be 3 bytes long :-).
	 */
	devaddr = hextoul(argv[2], NULL);
	alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
	if (alen > 3)
		return CMD_RET_USAGE;

	/*
	 * Length is the number of objects, not number of bytes.
	 */
	length = hextoul(argv[3], NULL);

	/*
	 * memaddr is the address where to store things in memory
	 */
	memaddr = (u_char *)hextoul(argv[4], NULL);

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (!ret)
		ret = dm_i2c_read(dev, devaddr, memaddr, length);
#else
	ret = i2c_read(chip, devaddr, alen, memaddr, length);
#endif
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);

	return 0;
}


static int do_i2c_bus_speed(struct cmd_tbl *cmdtp, int flag, int argc,
			    char *const argv[])
{
	int speed, ret=0;

#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *bus;

	if (i2c_get_cur_bus(&bus))
		return 1;
#endif
	if (argc == 1) {
#if CONFIG_IS_ENABLED(DM_I2C)
		speed = dm_i2c_get_bus_speed(bus);
#else
		speed = i2c_get_bus_speed();
#endif
		/* querying current speed */
		printf("Current bus speed=%d\n", speed);
	} else {
		speed = dectoul(argv[1], NULL);
		printf("Setting bus speed to %d Hz\n", speed);
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_set_bus_speed(bus, speed);
#else
		ret = i2c_set_bus_speed(speed);
#endif
		if (ret)
			printf("Failure changing bus speed (%d)\n", ret);
	}

	return ret ? CMD_RET_FAILURE : 0;
}


static int mod_i2c_mem(struct cmd_tbl *cmdtp, int incrflag, int flag, int argc,
		       char *const argv[])
{
	uint	chip;
	ulong	addr;
	int	alen;
	ulong	data;
	int	size = 1;
	int	nbytes;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	if (argc != 3)
		return CMD_RET_USAGE;

	bootretry_reset_cmd_timeout();	/* got a good command to get here */
	/*
	 * We use the last specified parameters, unless new ones are
	 * entered.
	 */
	chip = i2c_mm_last_chip;
	addr = i2c_mm_last_addr;
	alen = i2c_mm_last_alen;

	if ((flag & CMD_FLAG_REPEAT) == 0) {
		/*
		 * New command specified.  Check for a size specification.
		 * Defaults to byte if no or incorrect specification.
		 */
		size = cmd_get_data_size(argv[0], 1);

		/*
		 * Chip is always specified.
		 */
		chip = hextoul(argv[1], NULL);

		/*
		 * Address is always specified.
		 */
		addr = hextoul(argv[2], NULL);
		alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
		if (alen > 3)
			return CMD_RET_USAGE;
	}

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_WRITE);
#endif

	/*
	 * Print the address, followed by value.  Then accept input for
	 * the next value.  A non-converted value exits.
	 */
	do {
		printf("%08lx:", addr);
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_read(dev, addr, (uchar *)&data, size);
#else
		ret = i2c_read(chip, addr, alen, (uchar *)&data, size);
#endif
		if (ret)
			return i2c_report_err(ret, I2C_ERR_READ);

		data = cpu_to_be32(data);
		if (size == 1)
			printf(" %02lx", (data >> 24) & 0x000000FF);
		else if (size == 2)
			printf(" %04lx", (data >> 16) & 0x0000FFFF);
		else
			printf(" %08lx", data);

		nbytes = cli_readline(" ? ");
		if (nbytes == 0) {
			/*
			 * <CR> pressed as only input, don't modify current
			 * location and move to next.
			 */
			if (incrflag)
				addr += size;
			nbytes = size;
			/* good enough to not time out */
			bootretry_reset_cmd_timeout();
		}
#ifdef CONFIG_BOOT_RETRY_TIME
		else if (nbytes == -2)
			break;	/* timed out, exit the command	*/
#endif
		else {
			char *endp;

			data = hextoul(console_buffer, &endp);
			if (size == 1)
				data = data << 24;
			else if (size == 2)
				data = data << 16;
			data = be32_to_cpu(data);
			nbytes = endp - console_buffer;
			if (nbytes) {
				/*
				 * good enough to not time out
				 */
				bootretry_reset_cmd_timeout();
#if CONFIG_IS_ENABLED(DM_I2C)
				ret = dm_i2c_write(dev, addr, (uchar *)&data,
						   size);
#else
				ret = i2c_write(chip, addr, alen,
						(uchar *)&data, size);
#endif
				if (ret)
					return i2c_report_err(ret,
							      I2C_ERR_WRITE);
#if CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS > 0
				udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
#endif
				if (incrflag)
					addr += size;
			}
		}
	} while (nbytes);

	i2c_mm_last_chip = chip;
	i2c_mm_last_addr = addr;
	i2c_mm_last_alen = alen;

	return 0;
}


static int do_i2c_reset(struct cmd_tbl *cmdtp, int flag, int argc,
			char *const argv[])
{
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *bus;

	if (i2c_get_cur_bus(&bus))
		return CMD_RET_FAILURE;
	if (i2c_deblock(bus)) {
		printf("Error: Not supported by the driver\n");
		return CMD_RET_FAILURE;
	}
#elif CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
	i2c_init(I2C_ADAP->speed, I2C_ADAP->slaveaddr);
#endif
	return 0;
}


static int do_i2c_bus_num(struct cmd_tbl *cmdtp, int flag, int argc,
			  char *const argv[])
{
	int		ret = 0;
	int	bus_no;

	if (argc == 1) {
		/* querying current setting */
#if CONFIG_IS_ENABLED(DM_I2C)
		struct udevice *bus;

		if (!i2c_get_cur_bus(&bus))
			bus_no = dev_seq(bus);
		else
			bus_no = -1;
#else
		bus_no = i2c_get_bus_num();
#endif
		printf("Current bus is %d\n", bus_no);
	} else {
		bus_no = dectoul(argv[1], NULL);
#if CONFIG_IS_ENABLED(SYS_I2C_LEGACY)
		if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES) {
			printf("Invalid bus %d\n", bus_no);
			return -1;
		}
#endif
		printf("Setting bus to %d\n", bus_no);
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = cmd_i2c_set_bus_num(bus_no);
#else
		ret = i2c_set_bus_num(bus_no);
#endif
		if (ret)
			printf("Failure changing bus number (%d)\n", ret);
	}

	return ret ? CMD_RET_FAILURE : 0;
}


static int i2c_report_err(int ret, enum i2c_err_op op)
{
	printf("Error %s the chip: %d\n",
	       op == I2C_ERR_READ ? "reading" : "writing", ret);

	return CMD_RET_FAILURE;
}


static int do_i2c_mw(struct cmd_tbl *cmdtp, int flag, int argc,
		     char *const argv[])
{
	uint	chip;
	ulong	addr;
	uint	alen;
	uchar	byte;
	uint	count;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	if ((argc < 4) || (argc > 5))
		return CMD_RET_USAGE;

	/*
	 * Chip is always specified.
	 */
	chip = hextoul(argv[1], NULL);

	/*
	 * Address is always specified.
	 */
	addr = hextoul(argv[2], NULL);
	alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
	if (alen > 3)
		return CMD_RET_USAGE;

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_WRITE);
#endif
	/*
	 * Value to write is always specified.
	 */
	byte = hextoul(argv[3], NULL);

	/*
	 * Optional count
	 */
	if (argc == 5)
		count = hextoul(argv[4], NULL);
	else
		count = 1;

	while (count-- > 0) {
#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_write(dev, addr++, &byte, 1);
#else
		ret = i2c_write(chip, addr++, alen, &byte, 1);
#endif
		if (ret)
			return i2c_report_err(ret, I2C_ERR_WRITE);
		/*
		 * Wait for the write to complete.  The write can take
		 * up to 10mSec (we allow a little more time).
		 */
/*
 * No write delay with FRAM devices.
 */
#if !defined(CONFIG_SYS_I2C_FRAM)
		udelay(11000);
#endif
	}

	return 0;
}


static int do_i2c_olen(struct cmd_tbl *cmdtp, int flag, int argc,
		       char *const argv[])
{
	struct udevice *dev;
	uint olen;
	int chip;
	int ret;

	if (argc < 2)
		return CMD_RET_USAGE;

	chip = hextoul(argv[1], NULL);
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);

	if (argc > 2) {
		olen = hextoul(argv[2], NULL);
		ret = i2c_set_chip_offset_len(dev, olen);
	} else  {
		ret = i2c_get_chip_offset_len(dev);
		if (ret >= 0) {
			printf("%x\n", ret);
			ret = 0;
		}
	}
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);

	return 0;
}


static void decode_bits (u_char const b, char const *str[], int const do_once)
{
	u_char mask;

	for (mask = 0x80; mask != 0x00; mask >>= 1, ++str) {
		if (b & mask) {
			puts (*str);
			if (do_once)
				return;
		}
	}
}


static int i2c_get_cur_bus(struct udevice **busp)
{
#ifdef CONFIG_I2C_SET_DEFAULT_BUS_NUM
	if (!i2c_cur_bus) {
		if (cmd_i2c_set_bus_num(CONFIG_I2C_DEFAULT_BUS_NUMBER)) {
			printf("Default I2C bus %d not found\n",
			       CONFIG_I2C_DEFAULT_BUS_NUMBER);
			return -ENODEV;
		}
	}
#endif

	if (!i2c_cur_bus) {
		puts("No I2C bus selected\n");
		return -ENODEV;
	}
	*busp = i2c_cur_bus;

	return 0;
}


static int do_i2c_mm(struct cmd_tbl *cmdtp, int flag, int argc,
		     char *const argv[])
{
	return mod_i2c_mem (cmdtp, 1, flag, argc, argv);
}


static void print_ddr2_tcyc (u_char const b)
{
	printf ("%d.", (b >> 4) & 0x0F);
	switch (b & 0x0F) {
	case 0x0:
	case 0x1:
	case 0x2:
	case 0x3:
	case 0x4:
	case 0x5:
	case 0x6:
	case 0x7:
	case 0x8:
	case 0x9:
		printf ("%d ns\n", b & 0x0F);
		break;
	case 0xA:
		puts ("25 ns\n");
		break;
	case 0xB:
		puts ("33 ns\n");
		break;
	case 0xC:
		puts ("66 ns\n");
		break;
	case 0xD:
		puts ("75 ns\n");
		break;
	default:
		puts ("?? ns\n");
		break;
	}
}


static int do_i2c_flags(struct cmd_tbl *cmdtp, int flag, int argc,
			char *const argv[])
{
	struct udevice *dev;
	uint flags;
	int chip;
	int ret;

	if (argc < 2)
		return CMD_RET_USAGE;

	chip = hextoul(argv[1], NULL);
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);

	if (argc > 2) {
		flags = hextoul(argv[2], NULL);
		ret = i2c_set_chip_flags(dev, flags);
	} else  {
		ret = i2c_get_chip_flags(dev, &flags);
		if (!ret)
			printf("%x\n", flags);
	}
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);

	return 0;
}


static int do_i2c_md(struct cmd_tbl *cmdtp, int flag, int argc,
		     char *const argv[])
{
	uint	chip;
	uint	addr, length;
	uint	alen;
	uint	j, nbytes, linebytes;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	/* We use the last specified parameters, unless new ones are
	 * entered.
	 */
	chip   = i2c_dp_last_chip;
	addr   = i2c_dp_last_addr;
	alen   = i2c_dp_last_alen;
	length = i2c_dp_last_length;

	if (argc < 3)
		return CMD_RET_USAGE;

	if ((flag & CMD_FLAG_REPEAT) == 0) {
		/*
		 * New command specified.
		 */

		/*
		 * I2C chip address
		 */
		chip = hextoul(argv[1], NULL);

		/*
		 * I2C data address within the chip.  This can be 1 or
		 * 2 bytes long.  Some day it might be 3 bytes long :-).
		 */
		addr = hextoul(argv[2], NULL);
		alen = get_alen(argv[2], DEFAULT_ADDR_LEN);
		if (alen > 3)
			return CMD_RET_USAGE;

		/*
		 * If another parameter, it is the length to display.
		 * Length is the number of objects, not number of bytes.
		 */
		if (argc > 3)
			length = hextoul(argv[3], NULL);
	}

#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret && alen != -1)
		ret = i2c_set_chip_offset_len(dev, alen);
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);
#endif

	/*
	 * Print the lines.
	 *
	 * We buffer all read data, so we can make sure data is read only
	 * once.
	 */
	nbytes = length;
	do {
		unsigned char	linebuf[DISP_LINE_LEN];
		unsigned char	*cp;

		linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;

#if CONFIG_IS_ENABLED(DM_I2C)
		ret = dm_i2c_read(dev, addr, linebuf, linebytes);
#else
		ret = i2c_read(chip, addr, alen, linebuf, linebytes);
#endif
		if (ret)
			return i2c_report_err(ret, I2C_ERR_READ);
		else {
			printf("%04x:", addr);
			cp = linebuf;
			for (j=0; j<linebytes; j++) {
				printf(" %02x", *cp++);
				addr++;
			}
			puts ("    ");
			cp = linebuf;
			for (j=0; j<linebytes; j++) {
				if ((*cp < 0x20) || (*cp > 0x7e))
					puts (".");
				else
					printf("%c", *cp);
				cp++;
			}
			putc ('\n');
		}
		nbytes -= linebytes;
	} while (nbytes > 0);

	i2c_dp_last_chip   = chip;
	i2c_dp_last_addr   = addr;
	i2c_dp_last_alen   = alen;
	i2c_dp_last_length = length;

	return 0;
}


int do_edid(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
	uint chip;
	struct edid1_info edid;
	int ret;
#if CONFIG_IS_ENABLED(DM_I2C)
	struct udevice *dev;
#endif

	if (argc < 2) {
		cmd_usage(cmdtp);
		return 1;
	}

	chip = hextoul(argv[1], NULL);
#if CONFIG_IS_ENABLED(DM_I2C)
	ret = i2c_get_cur_bus_chip(chip, &dev);
	if (!ret)
		ret = dm_i2c_read(dev, 0, (uchar *)&edid, sizeof(edid));
#else
	ret = i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid));
#endif
	if (ret)
		return i2c_report_err(ret, I2C_ERR_READ);

	if (edid_check_info(&edid)) {
		puts("Content isn't valid EDID.\n");
		return 1;
	}

	edid_print_info(&edid);
	return 0;

}


static int do_i2c_show_bus(struct cmd_tbl *cmdtp, int flag, int argc,
			   char *const argv[])
{
	if (argc == 1) {
		/* show all busses */
#if CONFIG_IS_ENABLED(DM_I2C)
		struct udevice *bus;
		struct uclass *uc;
		int ret;

		ret = uclass_get(UCLASS_I2C, &uc);
		if (ret)
			return CMD_RET_FAILURE;
		uclass_foreach_dev(bus, uc)
			show_bus(bus);
#else
		int i;

		for (i = 0; i < CONFIG_SYS_NUM_I2C_BUSES; i++) {
			printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name);
#ifndef CONFIG_SYS_I2C_DIRECT_BUS
			int j;

			for (j = 0; j < CONFIG_SYS_I2C_MAX_HOPS; j++) {
				if (i2c_bus[i].next_hop[j].chip == 0)
					break;
				printf("->%s@0x%2x:%d",
				       i2c_bus[i].next_hop[j].mux.name,
				       i2c_bus[i].next_hop[j].chip,
				       i2c_bus[i].next_hop[j].channel);
			}
#endif
			printf("\n");
		}
#endif
	} else {
		int i;

		/* show specific bus */
		i = dectoul(argv[1], NULL);
#if CONFIG_IS_ENABLED(DM_I2C)
		struct udevice *bus;
		int ret;

		ret = uclass_get_device_by_seq(UCLASS_I2C, i, &bus);
		if (ret) {
			printf("Invalid bus %d: err=%d\n", i, ret);
			return CMD_RET_FAILURE;
		}
		show_bus(bus);
#else
		if (i >= CONFIG_SYS_NUM_I2C_BUSES) {
			printf("Invalid bus %d\n", i);
			return -1;
		}
		printf("Bus %d:\t%s", i, I2C_ADAP_NR(i)->name);
#ifndef CONFIG_SYS_I2C_DIRECT_BUS
			int j;
			for (j = 0; j < CONFIG_SYS_I2C_MAX_HOPS; j++) {
				if (i2c_bus[i].next_hop[j].chip == 0)
					break;
				printf("->%s@0x%2x:%d",
				       i2c_bus[i].next_hop[j].mux.name,
				       i2c_bus[i].next_hop[j].chip,
				       i2c_bus[i].next_hop[j].channel);
			}
#endif
		printf("\n");
#endif
	}

	return 0;
}


static int i2c_get_cur_bus_chip(uint chip_addr, struct udevice **devp)
{
	struct udevice *bus;
	int ret;

	ret = i2c_get_cur_bus(&bus);
	if (ret)
		return ret;

	return i2c_get_chip(bus, chip_addr, 1, devp);
}


SampleEncrypter::EncryptVideoSample(AP4_DataBuffer& sample, AP4_UI08 nalu_length_size)
{
    AP4_DataBuffer encrypted;

    AP4_UI08* nalu = sample.UseData();
    AP4_Size  bytes_remaining = sample.GetDataSize();
    while (bytes_remaining > nalu_length_size) {
        AP4_Size nalu_length = 0;
        switch (nalu_length_size) {
            case 1:
                nalu_length = nalu[0];
                break;

            case 2:
                nalu_length = AP4_BytesToUInt16BE(nalu);
                break;

            case 4:
                nalu_length = AP4_BytesToUInt32BE(nalu);
                break;

            default:
                break;
        }

        if (bytes_remaining < nalu_length_size+nalu_length) {
            break;
        }

        AP4_UI08 nalu_type = nalu[nalu_length_size] & 0x1F;
        if (nalu_length > 48 && (nalu_type == 1 || nalu_type == 5)) {
            AP4_Size encrypted_size = 16*((nalu_length-32)/16);
            if ((nalu_length%16) == 0) {
                encrypted_size -= 16;
            }
            m_StreamCipher->SetIV(m_IV);
            for (unsigned int i=0; i<encrypted_size; i += 10*16) {
                AP4_Size one_block_size = 16;
                m_StreamCipher->ProcessBuffer(nalu+nalu_length_size+32+i, one_block_size,
                                              nalu+nalu_length_size+32+i, &one_block_size);
            }

            // perform startcode emulation prevention
            AP4_DataBuffer escaped_nalu;
            PreventStartCodeEmulation(nalu+nalu_length_size, nalu_length, escaped_nalu);

            // the size may have changed
            // TODO: this could overflow if nalu_length_size is too small
            switch (nalu_length_size) {
                case 1:
                    nalu[0] = (AP4_UI08)(escaped_nalu.GetDataSize()&0xFF);
                    break;

                case 2:
                    AP4_BytesFromUInt16BE(nalu, escaped_nalu.GetDataSize());
                    break;

                case 4:
                    AP4_BytesFromUInt32BE(nalu, escaped_nalu.GetDataSize());
                    break;

                default:
                    break;
            }

            encrypted.AppendData(nalu, nalu_length_size);
            encrypted.AppendData(escaped_nalu.GetData(), escaped_nalu.GetDataSize());
        } else {
            encrypted.AppendData(nalu, nalu_length_size);
            encrypted.AppendData(nalu+nalu_length_size, nalu_length);
        }

        nalu            += nalu_length_size+nalu_length;
        bytes_remaining -= nalu_length_size+nalu_length;
    }

    sample.SetData(encrypted.GetData(), encrypted.GetDataSize());

    return AP4_SUCCESS;
}

fix #691


PackedAudioWriter::WriteSample(AP4_Sample&            sample,
                               AP4_DataBuffer&        sample_data,
                               AP4_SampleDescription* sample_description,
                               AP4_ByteStream&        output)
{
    AP4_AudioSampleDescription* audio_desc = AP4_DYNAMIC_CAST(AP4_AudioSampleDescription, sample_description);
    if (audio_desc == NULL) {
        return AP4_ERROR_INVALID_FORMAT;
    }
    if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
        AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);

        if (mpeg_audio_desc == NULL) return AP4_ERROR_NOT_SUPPORTED;
        if (mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_LC   &&
            mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_AAC_MAIN &&
            mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_SBR      &&
            mpeg_audio_desc->GetMpeg4AudioObjectType() != AP4_MPEG4_AUDIO_OBJECT_TYPE_PS) {
            return AP4_ERROR_NOT_SUPPORTED;
        }

        unsigned int sample_rate   = mpeg_audio_desc->GetSampleRate();
        unsigned int channel_count = mpeg_audio_desc->GetChannelCount();
        const AP4_DataBuffer& dsi  = mpeg_audio_desc->GetDecoderInfo();
        if (dsi.GetDataSize()) {
            AP4_Mp4AudioDecoderConfig dec_config;
            AP4_Result result = dec_config.Parse(dsi.GetData(), dsi.GetDataSize());
            if (AP4_SUCCEEDED(result)) {
                sample_rate = dec_config.m_SamplingFrequency;
                channel_count = dec_config.m_ChannelCount;
            }
        }
        unsigned int sampling_frequency_index = GetSamplingFrequencyIndex(sample_rate);
        unsigned int channel_configuration    = channel_count;

        WriteAdtsHeader(output, sample.GetSize(), sampling_frequency_index, channel_configuration);
    } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
        WriteAc4Header(output, sample.GetSize());
    }
    return output.Write(sample_data.GetData(), sample_data.GetDataSize());
}

fix #691


    SampleEncrypter(AP4_CbcStreamCipher* stream_cipher, const AP4_UI08* iv):
        m_StreamCipher(stream_cipher) {
        AP4_CopyMemory(m_IV, iv, 16);
    }

fix #691


MakeAudioSetupData(AP4_DataBuffer&        audio_setup_data,
                   AP4_SampleDescription* sample_description,
                   AP4_DataBuffer&        sample_data /* needed for encrypted AC3 */)
{
    AP4_UI08       audio_type[4];
    AP4_DataBuffer setup_data;

    if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
        AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
        if (mpeg_audio_desc == NULL ||
            !(mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO          ||
              mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC   ||
              mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN)) {
            return AP4_ERROR_NOT_SUPPORTED;
        }
        const AP4_DataBuffer& dsi = mpeg_audio_desc->GetDecoderInfo();
        setup_data.SetData(dsi.GetData(), dsi.GetDataSize());

        AP4_Mp4AudioDecoderConfig dec_config;
        AP4_Result result = dec_config.Parse(dsi.GetData(), dsi.GetDataSize());
        if (AP4_FAILED(result)) {
            return AP4_ERROR_INVALID_FORMAT;
        }

        if (dec_config.m_Extension.m_SbrPresent || dec_config.m_Extension.m_PsPresent) {
            if (dec_config.m_Extension.m_PsPresent) {
                audio_type[0] = 'z';
                audio_type[1] = 'a';
                audio_type[2] = 'c';
                audio_type[3] = 'p';
            } else {
                audio_type[0] = 'z';
                audio_type[1] = 'a';
                audio_type[2] = 'c';
                audio_type[3] = 'h';
            }
        } else {
            audio_type[0] = 'z';
            audio_type[1] = 'a';
            audio_type[2] = 'a';
            audio_type[3] = 'c';
        }
    } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
        audio_type[0] = 'z';
        audio_type[1] = 'a';
        audio_type[2] = 'c';
        audio_type[3] = '3';

        // the setup data is the first 10 bytes of a syncframe
        if (sample_data.GetDataSize() >= 10) {
            setup_data.SetData(sample_data.GetData(), 10);
        }
    } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
        audio_type[0] = 'z';
        audio_type[1] = 'e';
        audio_type[2] = 'c';
        audio_type[3] = '3';

        AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, sample_description->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
        if (dec3 == NULL) {
            fprintf(stderr, "ERROR: failed to find 'dec3' atom in sample description\n");
            return 1;
        }
        setup_data.SetData(dec3->GetRawBytes().GetData(), dec3->GetRawBytes().GetDataSize());
    } else {
        return AP4_SUCCESS;
    }

    audio_setup_data.SetDataSize(8+setup_data.GetDataSize());
    AP4_UI08* payload = audio_setup_data.UseData();
    payload[0] = audio_type[0];
    payload[1] = audio_type[1];
    payload[2] = audio_type[2];
    payload[3] = audio_type[3];
    payload[4] = 0; // priming
    payload[5] = 0; // priming
    payload[6] = 1; // version
    payload[7] = setup_data.GetDataSize(); // setup_data_length
    if (setup_data.GetDataSize()) {
        AP4_CopyMemory(&payload[8], setup_data.GetData(), setup_data.GetDataSize());
    }

    return AP4_SUCCESS;
}

fix #691


    EncryptingStream(AP4_CbcStreamCipher* stream_cipher, AP4_ByteStream* output):
        m_ReferenceCount(1),
        m_StreamCipher(stream_cipher),
        m_Output(output),
        m_Size(0) {
        output->AddReference();
    }

fix #691


WriteAc4Header(AP4_ByteStream& output,
                unsigned int    frame_size)
{
    unsigned char bits[7];

    bits[0] = 0xac;
    bits[1] = 0x40;
    bits[2] = 0xff;
    bits[3] = 0xff;
    bits[4] = (frame_size>>16)&0xFF;
    bits[5] = (frame_size>>8 )&0xFF;
    bits[6] = (frame_size    )&0xFF;

    return output.Write(bits, 7);
}

fix #691


GetSamplingFrequencyIndex(unsigned int sampling_frequency)
{
    switch (sampling_frequency) {
        case 96000: return 0;
        case 88200: return 1;
        case 64000: return 2;
        case 48000: return 3;
        case 44100: return 4;
        case 32000: return 5;
        case 24000: return 6;
        case 22050: return 7;
        case 16000: return 8;
        case 12000: return 9;
        case 11025: return 10;
        case 8000:  return 11;
        case 7350:  return 12;
        default:    return 0;
    }
}

fix #691


OpenOutput(const char* filename_pattern, unsigned int segment_number)
{
    AP4_ByteStream* output = NULL;
    char filename[4096];
    sprintf(filename, filename_pattern, segment_number);
    AP4_Result result = AP4_FileByteStream::Create(filename, AP4_FileByteStream::STREAM_MODE_WRITE, output);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
        return NULL;
    }

    return output;
}

fix #691


virtual AP4_Result Seek(AP4_Position) { return AP4_ERROR_NOT_SUPPORTED; }

fix #691


    ~EncryptingStream() {
        m_Output->Release();
        delete m_StreamCipher;
    }

fix #691


SampleEncrypter::EncryptAudioSample(AP4_DataBuffer& sample, AP4_SampleDescription* sample_description)
{
    if (sample.GetDataSize() <= 16) {
        return AP4_SUCCESS;
    }

    // deal with E-AC3 syncframes
    if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
        // syncword    : 16
        // strmtyp     : 2
        // substreamid : 3
        // frmsiz      : 11
        AP4_UI08*    data      = sample.UseData();
        unsigned int data_size = sample.GetDataSize();
        while (data_size > 4) {
            unsigned int syncword = (data[0]<<8) | data[1];
            if (syncword != 0x0b77) {
                return AP4_ERROR_INVALID_FORMAT; // unexpected!
            }
            unsigned int frmsiz = 1+(((data[2]<<8)|(data[3]))&0x7FF);
            unsigned int frame_size = 2*frmsiz;
            if (data_size < frame_size) {
                return AP4_ERROR_INVALID_FORMAT; // unexpected!
            }

            // encrypt the syncframe
            if (frame_size > 16) {
                unsigned int encrypted_block_count = (frame_size-16)/16;
                AP4_Size encrypted_size = encrypted_block_count*16;
                m_StreamCipher->SetIV(m_IV);
                m_StreamCipher->ProcessBuffer(data+16, encrypted_size, data+16, &encrypted_size);
            }

            data      += frame_size;
            data_size -= frame_size;
        }
    } else {
        unsigned int encrypted_block_count = (sample.GetDataSize()-16)/16;
        AP4_Size encrypted_size = encrypted_block_count*16;
        m_StreamCipher->SetIV(m_IV);
        m_StreamCipher->ProcessBuffer(sample.UseData()+16, encrypted_size, sample.UseData()+16, &encrypted_size);
    }

    return AP4_SUCCESS;
}

fix #691


PreventStartCodeEmulation(const AP4_UI08* payload, AP4_Size payload_size, AP4_DataBuffer& output)
{
    output.Reserve(payload_size*2); // more than enough
    AP4_Size  output_size = 0;
    AP4_UI08* buffer = output.UseData();

	unsigned int zero_counter = 0;
	for (unsigned int i = 0; i < payload_size; i++) {
		if (zero_counter == 2) {
            if (payload[i] == 0 || payload[i] == 1 || payload[i] == 2 || payload[i] == 3) {
                buffer[output_size++] = 3;
                zero_counter = 0;
            }
		}

        buffer[output_size++] = payload[i];

		if (payload[i] == 0) {
			++zero_counter;
		} else {
			zero_counter = 0;
        }
	}

    output.SetDataSize(output_size);
}

fix #691


    virtual AP4_Result Flush() {
        AP4_UI08 trailer[16];
        AP4_Size trailer_size = sizeof(trailer);
        AP4_Result result = m_StreamCipher->ProcessBuffer(NULL, 0, trailer, &trailer_size, true);
        if (AP4_SUCCEEDED(result) && trailer_size) {
            m_Output->Write(trailer, trailer_size);
            m_Size += 16-(m_Size%16);
        }

        return AP4_SUCCESS;
    }

fix #691


main(int argc, char** argv)
{
    if (argc < 2) {
        PrintUsageAndExit();
    }

    // default options
    Options.input                          = NULL;
    Options.verbose                        = false;
    Options.hls_version                    = 0;
    Options.pmt_pid                        = 0x100;
    Options.audio_pid                      = 0x101;
    Options.video_pid                      = 0x102;
    Options.audio_track_id                 = -1;
    Options.video_track_id                 = -1;
    Options.audio_format                   = AUDIO_FORMAT_TS;
    Options.output_single_file             = false;
    Options.show_info                      = false;
    Options.index_filename                 = "stream.m3u8";
    Options.iframe_index_filename          = NULL;
    Options.segment_filename_template      = NULL;
    Options.segment_url_template           = NULL;
    Options.segment_duration               = 6;
    Options.segment_duration_threshold     = DefaultSegmentDurationThreshold;
    Options.allow_cache                    = NULL;
    Options.encryption_key_hex             = NULL;
    Options.encryption_mode                = ENCRYPTION_MODE_NONE;
    Options.encryption_iv_mode             = ENCRYPTION_IV_MODE_NONE;
    Options.encryption_key_uri             = "key.bin";
    Options.encryption_key_format          = NULL;
    Options.encryption_key_format_versions = NULL;
    Options.pcr_offset                     = AP4_MPEG2_TS_DEFAULT_PCR_OFFSET;
    AP4_SetMemory(Options.encryption_key, 0, sizeof(Options.encryption_key));
    AP4_SetMemory(Options.encryption_iv,  0, sizeof(Options.encryption_iv));
    AP4_SetMemory(&Stats, 0, sizeof(Stats));

    // parse command line
    AP4_Result result;
    char** args = argv+1;
    while (const char* arg = *args++) {
        if (!strcmp(arg, "--verbose")) {
            Options.verbose = true;
        } else if (!strcmp(arg, "--hls-version")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --hls-version requires a number\n");
                return 1;
            }
            Options.hls_version = (unsigned int)strtoul(*args++, NULL, 10);
            if (Options.hls_version ==0) {
                fprintf(stderr, "ERROR: --hls-version requires number > 0\n");
                return 1;
            }
        } else if (!strcmp(arg, "--segment-duration")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-duration requires a number\n");
                return 1;
            }
            Options.segment_duration = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--segment-duration-threshold")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-duration-threshold requires a number\n");
                return 1;
            }
            Options.segment_duration_threshold = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--segment-filename-template")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-filename-template requires an argument\n");
                return 1;
            }
            Options.segment_filename_template = *args++;
        } else if (!strcmp(arg, "--segment-url-template")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --segment-url-template requires an argument\n");
                return 1;
            }
            Options.segment_url_template = *args++;
        } else if (!strcmp(arg, "--allow-cache")) {
            if (*args == NULL || (strcmp(*args, "NO") && strcmp(*args, "YES"))) {
                fprintf(stderr, "ERROR: --allow-cache requires a YES or NO argument\n");
                return 1;
            }
            Options.allow_cache = *args++;
        } else if (!strcmp(arg, "--pmt-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --pmt-pid requires a number\n");
                return 1;
            }
            Options.pmt_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-pid requires a number\n");
                return 1;
            }
            Options.audio_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--video-pid")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --video-pid requires a number\n");
                return 1;
            }
            Options.video_pid = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-track-id")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-track-id requires a number\n");
                return 1;
            }
            Options.audio_track_id = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--audio-format")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --audio-format requires an argument\n");
                return 1;
            }
            const char* format = *args++;
            if (!strcmp(format, "ts")) {
                Options.audio_format = AUDIO_FORMAT_TS;
            } else if (!strcmp(format, "packed")) {
                Options.audio_format = AUDIO_FORMAT_PACKED;
            } else {
                fprintf(stderr, "ERROR: unknown audio format\n");
                return 1;
            }
        } else if (!strcmp(arg, "--video-track-id")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --video-track-id requires a number\n");
                return 1;
            }
            Options.video_track_id = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--pcr-offset")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --pcr-offset requires a number\n");
                return 1;
            }
            Options.pcr_offset = (unsigned int)strtoul(*args++, NULL, 10);
        } else if (!strcmp(arg, "--output-single-file")) {
            Options.output_single_file = true;
        } else if (!strcmp(arg, "--index-filename")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --index-filename requires a filename\n");
                return 1;
            }
            Options.index_filename = *args++;
        } else if (!strcmp(arg, "--iframe-index-filename")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --iframe-index-filename requires a filename\n");
                return 1;
            }
            Options.iframe_index_filename = *args++;
        } else if (!strcmp(arg, "--show-info")) {
            Options.show_info = true;
        } else if (!strcmp(arg, "--encryption-key")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key requires an argument\n");
                return 1;
            }
            Options.encryption_key_hex = *args++;
            result = AP4_ParseHex(Options.encryption_key_hex, Options.encryption_key, 16);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "ERROR: invalid hex key\n");
                return 1;
            }
            if (Options.encryption_mode == ENCRYPTION_MODE_NONE) {
                Options.encryption_mode = ENCRYPTION_MODE_AES_128;
            }
        } else if (!strcmp(arg, "--encryption-mode")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-mode requires an argument\n");
                return 1;
            }
            if (strncmp(*args, "AES-128", 7) == 0) {
                Options.encryption_mode = ENCRYPTION_MODE_AES_128;
            } else if (strncmp(*args, "SAMPLE-AES", 10) == 0) {
                Options.encryption_mode = ENCRYPTION_MODE_SAMPLE_AES;
            } else {
                fprintf(stderr, "ERROR: unknown encryption mode\n");
                return 1;
            }
            ++args;
        } else if (!strcmp(arg, "--encryption-iv-mode")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-iv-mode requires an argument\n");
                return 1;
            }
            if (strncmp(*args, "sequence", 8) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
            } else if (strncmp(*args, "random", 6) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
            } else if (strncmp(*args, "fps", 3) == 0) {
                Options.encryption_iv_mode = ENCRYPTION_IV_MODE_FPS;
            } else {
                fprintf(stderr, "ERROR: unknown encryption IV mode\n");
                return 1;
            }
            ++args;
        } else if (!strcmp(arg, "--encryption-key-uri")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-uri requires an argument\n");
                return 1;
            }
            Options.encryption_key_uri = *args++;
        } else if (!strcmp(arg, "--encryption-key-format")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-format requires an argument\n");
                return 1;
            }
            Options.encryption_key_format = *args++;
        } else if (!strcmp(arg, "--encryption-key-format-versions")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-format-versions requires an argument\n");
                return 1;
            }
            Options.encryption_key_format_versions = *args++;
        } else if (!strcmp(arg, "--encryption-key-line")) {
            if (*args == NULL) {
                fprintf(stderr, "ERROR: --encryption-key-line requires an argument\n");
                return 1;
            }
            Options.encryption_key_lines.Append(*args++);
        } else if (Options.input == NULL) {
            Options.input = arg;
        } else {
            fprintf(stderr, "ERROR: unexpected argument: %s\n", arg);
            return 1;
        }
    }

    // check args
    if (Options.input == NULL) {
        fprintf(stderr, "ERROR: missing input file name\n");
        return 1;
    }
    if (Options.encryption_mode == ENCRYPTION_MODE_NONE && Options.encryption_key_lines.ItemCount() != 0) {
        fprintf(stderr, "ERROR: --encryption-key-line requires --encryption-key and --encryption-key-mode\n");
        return 1;
    }
    if (Options.encryption_mode != ENCRYPTION_MODE_NONE && Options.encryption_key_hex == NULL) {
        fprintf(stderr, "ERROR: no encryption key specified\n");
        return 1;
    }
    if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES && Options.hls_version > 0 && Options.hls_version < 5) {
        Options.hls_version = 5;
        fprintf(stderr, "WARNING: forcing version to 5 in order to support SAMPLE-AES encryption\n");
    }
    if (Options.iframe_index_filename && Options.hls_version > 0 && Options.hls_version < 4) {
        fprintf(stderr, "WARNING: forcing version to 4 in order to support I-FRAME-ONLY playlists\n");
        Options.hls_version = 4;
    }
    if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_NONE && Options.encryption_mode != ENCRYPTION_MODE_NONE) {
        if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
            // sequence-mode IVs don't work well with i-frame only playlists, use random instead
            Options.encryption_iv_mode = ENCRYPTION_IV_MODE_RANDOM;
        } else {
            Options.encryption_iv_mode = ENCRYPTION_IV_MODE_SEQUENCE;
        }
    }
    if ((Options.encryption_key_format || Options.encryption_key_format_versions) && Options.hls_version > 0 && Options.hls_version < 5) {
        Options.hls_version = 5;
        fprintf(stderr, "WARNING: forcing version to 5 in order to support KEYFORMAT and/or KEYFORMATVERSIONS\n");
    }
    if (Options.output_single_file && Options.hls_version > 0 && Options.hls_version < 4) {
        Options.hls_version = 4;
        fprintf(stderr, "WARNING: forcing version to 4 in order to support single file output\n");
    }
    if (Options.hls_version == 0) {
        // default version is 3 for cleartext or AES-128 encryption, and 5 for SAMPLE-AES
        if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
            Options.hls_version = 5;
        } else if (Options.output_single_file || Options.iframe_index_filename) {
            Options.hls_version = 4;
        } else {
            Options.hls_version = 3;
        }
    }

    if (Options.verbose && Options.show_info) {
        fprintf(stderr, "WARNING: --verbose will be ignored because --show-info is selected\n");
        Options.verbose = false;
    }

    // compute some derived values
    if (Options.iframe_index_filename == NULL) {
        if (Options.hls_version >= 4) {
            Options.iframe_index_filename = "iframes.m3u8";
        }
    }
    if (Options.audio_format == AUDIO_FORMAT_TS) {
        if (Options.segment_filename_template == NULL) {
            if (Options.output_single_file) {
                Options.segment_filename_template = "stream.ts";
            } else {
                Options.segment_filename_template = "segment-%d.ts";
            }
        }
        if (Options.segment_url_template == NULL) {
            if (Options.output_single_file) {
                Options.segment_url_template = "stream.ts";
            } else {
                Options.segment_url_template = "segment-%d.ts";
            }
        }
    }

    if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_FPS) {
        if (Options.encryption_key_hex == NULL || AP4_StringLength(Options.encryption_key_hex) != 64) {
            fprintf(stderr, "ERROR: 'fps' IV mode requires a 32 byte key value (64 characters in hex)\n");
            return 1;
        }
        result = AP4_ParseHex(Options.encryption_key_hex+32, Options.encryption_iv, 16);
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: invalid hex IV\n");
            return 1;
        }
    } else if (Options.encryption_iv_mode == ENCRYPTION_IV_MODE_RANDOM) {
        result = AP4_System_GenerateRandomBytes(Options.encryption_iv, sizeof(Options.encryption_iv));
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: failed to get random IV (%d)\n", result);
            return 1;
        }
    }

	// create the input stream
    AP4_ByteStream* input = NULL;
    result = AP4_FileByteStream::Create(Options.input, AP4_FileByteStream::STREAM_MODE_READ, input);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
        return 1;
    }

	// open the file
    AP4_File* input_file = new AP4_File(*input, true);

    // get the movie
    AP4_SampleDescription* sample_description;
    AP4_Movie* movie = input_file->GetMovie();
    if (movie == NULL) {
        fprintf(stderr, "ERROR: no movie in file\n");
        return 1;
    }

    // get the audio and video tracks
    AP4_Track* audio_track = NULL;
    if (Options.audio_track_id == -1) {
        audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO);
    } else if (Options.audio_track_id > 0) {
        audio_track = movie->GetTrack((AP4_UI32)Options.audio_track_id);
        if (audio_track == NULL) {
            fprintf(stderr, "ERROR: audio track ID %d not found\n", Options.audio_track_id);
            return 1;
        }
        if (audio_track->GetType() != AP4_Track::TYPE_AUDIO) {
            fprintf(stderr, "ERROR: track ID %d is not an audio track\n", Options.audio_track_id);
            return 1;
        }
    }
    AP4_Track* video_track = NULL;
    if (Options.video_track_id == -1) {
        video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
    } else if (Options.video_track_id > 0) {
        video_track = movie->GetTrack((AP4_UI32)Options.video_track_id);
        if (video_track == NULL) {
            fprintf(stderr, "ERROR: video track ID %d not found\n", Options.video_track_id);
            return 1;
        }
        if (video_track->GetType() != AP4_Track::TYPE_VIDEO) {
            fprintf(stderr, "ERROR: track ID %d is not a video track\n", Options.video_track_id);
            return 1;
        }
    }
    if (audio_track == NULL && video_track == NULL) {
        fprintf(stderr, "ERROR: no suitable tracks found\n");
        delete input_file;
        input->Release();
        return 1;
    }
    if (Options.audio_format == AUDIO_FORMAT_PACKED && video_track != NULL) {
        if (audio_track == NULL) {
            fprintf(stderr, "ERROR: packed audio format requires an audio track\n");
            return 1;
        }
        fprintf(stderr, "WARNING: ignoring video track because of the packed audio format\n");
        video_track = NULL;
    }
    if (video_track == NULL) {
        Options.segment_duration_threshold = 0;
    }

    // create the appropriate readers
    AP4_LinearReader* linear_reader = NULL;
    SampleReader*     audio_reader  = NULL;
    SampleReader*     video_reader  = NULL;
    if (movie->HasFragments()) {
        // create a linear reader to get the samples
        linear_reader = new AP4_LinearReader(*movie, input);

        if (audio_track) {
            linear_reader->EnableTrack(audio_track->GetId());
            audio_reader = new FragmentedSampleReader(*linear_reader, audio_track->GetId());
        }
        if (video_track) {
            linear_reader->EnableTrack(video_track->GetId());
            video_reader = new FragmentedSampleReader(*linear_reader, video_track->GetId());
        }
    } else {
        if (audio_track) {
            audio_reader = new TrackSampleReader(*audio_track);
        }
        if (video_track) {
            video_reader = new TrackSampleReader(*video_track);
        }
    }

    AP4_Mpeg2TsWriter*               ts_writer = NULL;
    AP4_Mpeg2TsWriter::SampleStream* audio_stream = NULL;
    AP4_Mpeg2TsWriter::SampleStream* video_stream = NULL;
    AP4_UI08                         nalu_length_size = 0;
    PackedAudioWriter*               packed_writer = NULL;
    if (Options.audio_format == AUDIO_FORMAT_PACKED) {
        packed_writer = new PackedAudioWriter();

        // figure out the file extensions if needed
        sample_description = audio_track->GetSampleDescription(0);
        if (sample_description == NULL) {
            fprintf(stderr, "ERROR: unable to parse audio sample description\n");
            goto end;
        }
        if (Options.segment_filename_template == NULL || Options.segment_url_template == NULL) {
            const char* default_stream_name    = "stream.es";
            const char* default_stream_pattern = "segment-%d.es";
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
                AP4_MpegAudioSampleDescription* mpeg_audio_desc = AP4_DYNAMIC_CAST(AP4_MpegAudioSampleDescription, sample_description);
                if (mpeg_audio_desc == NULL ||
                    !(mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG4_AUDIO          ||
                      mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_LC   ||
                      mpeg_audio_desc->GetObjectTypeId() == AP4_OTI_MPEG2_AAC_AUDIO_MAIN)) {
                    fprintf(stderr, "ERROR: only AAC audio is supported\n");
                    return 1;
                }
                default_stream_name    = "stream.aac";
                default_stream_pattern = "segment-%d.aac";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
                default_stream_name    = "stream.ac3";
                default_stream_pattern = "segment-%d.ac3";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
                default_stream_name    = "stream.ec3";
                default_stream_pattern = "segment-%d.ec3";
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_4) {
                default_stream_name    = "stream.ac4";
                default_stream_pattern = "segment-%d.ac4";
            }

            // override the segment names
            if (Options.segment_filename_template == NULL) {
                if (Options.output_single_file) {
                    Options.segment_filename_template = default_stream_name;
                } else {
                    Options.segment_filename_template = default_stream_pattern;
                }
            }
            if (Options.segment_url_template == NULL) {
                if (Options.output_single_file) {
                    Options.segment_url_template = default_stream_name;
                } else {
                    Options.segment_url_template = default_stream_pattern;
                }
            }
        }
    } else {
        // create an MPEG2 TS Writer
        ts_writer = new AP4_Mpeg2TsWriter(Options.pmt_pid);

        // add the audio stream
        if (audio_track) {
            sample_description = audio_track->GetSampleDescription(0);
            if (sample_description == NULL) {
                fprintf(stderr, "ERROR: unable to parse audio sample description\n");
                goto end;
            }

            unsigned int stream_type = 0;
            unsigned int stream_id   = 0;
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ISO_IEC_13818_7;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ISO_IEC_13818_7;
                }
                stream_id   = AP4_MPEG2_TS_DEFAULT_STREAM_ID_AUDIO;
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_AC3;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_AC3;
                }
                stream_id   = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_ATSC_EAC3;
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_EAC3;
                }
                stream_id   = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1;
            } else {
                fprintf(stderr, "ERROR: audio codec not supported\n");
                return 1;
            }
            if (stream_type == AP4_MPEG2_STREAM_TYPE_ATSC_EAC3) {
                // E-AC-3 descriptor
                unsigned int number_of_channels = 0;
                AP4_String track_language;
                AP4_Dec3Atom* dec3 = AP4_DYNAMIC_CAST(AP4_Dec3Atom, sample_description->GetDetails().GetChild(AP4_ATOM_TYPE_DEC3));
                AP4_BitWriter bits(8);
                bits.Write(0xCC, 8);
                bits.Write(0x06, 8);    // fixed value
                bits.Write(0xC0, 8);    // reserved, bsid_flag, mainid_flag, asvc_flag, mixinfoexists, substream1_flag, substream2_flag and substream3_flag
                bits.Write(24, 5);      // reserved, full_service_flag and service_type
                if (dec3->GetSubStreams()[0].acmod == 0) {
                    number_of_channels = 1;
                } else if (dec3->GetSubStreams()[0].acmod == 1) {
                    number_of_channels = 0;
                } else if (dec3->GetSubStreams()[0].acmod == 2) {
                    number_of_channels = 2;
                } else {
                    number_of_channels = 4;
                }
                if (dec3->GetSubStreams()[0].num_dep_sub > 0) {
                    number_of_channels = 5;
                }
                bits.Write(number_of_channels, 3);              // number_of_channels
                bits.Write(4, 3);                               // language_flag, language_flag_2, reserved
                bits.Write(dec3->GetSubStreams()[0].bsid, 5);   // bsid
                track_language = audio_track->GetTrackLanguage();
                if (track_language.GetLength() == 3) {
                    bits.Write(track_language.GetChars()[0], 8);
                    bits.Write(track_language.GetChars()[1], 8);
                    bits.Write(track_language.GetChars()[2], 8);
                } else {
                    bits.Write(0x75, 8);
                    bits.Write(0x6E, 8);
                    bits.Write(0x64, 8);
                }
                 // setup the audio stream
                result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
                                                   stream_type,
                                                   stream_id,
                                                   audio_stream,
                                                   Options.audio_pid,
                                                   bits.GetData(), 8,
                                                   Options.pcr_offset);
            } else {
            // setup the audio stream
            result = ts_writer->SetAudioStream(audio_track->GetMediaTimeScale(),
                                               stream_type,
                                               stream_id,
                                               audio_stream,
                                               Options.audio_pid,
                                               NULL, 0,
                                               Options.pcr_offset);
            }
            if (AP4_FAILED(result)) {
                fprintf(stderr, "could not create audio stream (%d)\n", result);
                goto end;
            }
        }

        // add the video stream
        if (video_track) {
            sample_description = video_track->GetSampleDescription(0);
            if (sample_description == NULL) {
                fprintf(stderr, "ERROR: unable to parse video sample description\n");
                goto end;
            }

            // decide on the stream type
            unsigned int stream_type = 0;
            unsigned int stream_id   = AP4_MPEG2_TS_DEFAULT_STREAM_ID_VIDEO;
            if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC1 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC2 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC4 ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVAV ||
                sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVA1) {
                if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                    stream_type = AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC;
                    AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sample_description);
                    if (avc_desc == NULL) {
                        fprintf(stderr, "ERROR: not a proper AVC track\n");
                        return 1;
                    }
                    nalu_length_size = avc_desc->GetNaluLengthSize();
                } else {
                    stream_type = AP4_MPEG2_STREAM_TYPE_AVC;
                }
            } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HEV1 ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HVC1 ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVHE ||
                       sample_description->GetFormat() == AP4_SAMPLE_FORMAT_DVH1) {
                stream_type = AP4_MPEG2_STREAM_TYPE_HEVC;
            } else {
                fprintf(stderr, "ERROR: video codec not supported\n");
                return 1;
            }
            if (Options.encryption_mode == ENCRYPTION_MODE_SAMPLE_AES) {
                if (stream_type != AP4_MPEG2_STREAM_TYPE_SAMPLE_AES_AVC) {
                    fprintf(stderr, "ERROR: AES-SAMPLE encryption can only be used with H.264 video\n");
                    return 1;
                }
            }

            // setup the video stream
            result = ts_writer->SetVideoStream(video_track->GetMediaTimeScale(),
                                               stream_type,
                                               stream_id,
                                               video_stream,
                                               Options.video_pid,
                                               NULL, 0,
                                               Options.pcr_offset);
            if (AP4_FAILED(result)) {
                fprintf(stderr, "could not create video stream (%d)\n", result);
                goto end;
            }
        }
    }

    result = WriteSamples(ts_writer, packed_writer,
                          audio_track, audio_reader, audio_stream,
                          video_track, video_reader, video_stream,
                          Options.segment_duration_threshold,
                          nalu_length_size);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: failed to write samples (%d)\n", result);
    }

    if (Options.show_info) {
        double average_segment_bitrate = 0.0;
        if (Stats.segments_total_duration != 0.0) {
            average_segment_bitrate = 8.0*(double)Stats.segments_total_size/Stats.segments_total_duration;
        }
        double average_iframe_bitrate = 0.0;
        if (Stats.segments_total_duration != 0.0) {
            average_iframe_bitrate = 8.0*(double)Stats.iframes_total_size/Stats.segments_total_duration;
        }

        double frame_rate = 0.0;
        if (video_track && (Stats.segments_total_duration != 0.0)) {
            double sample_count = (double)video_track->GetSampleCount();
            double media_duration = (double)video_track->GetMediaDuration();
            double timescale = (double)video_track->GetMediaTimeScale();
            if (media_duration > 0.0) {
                frame_rate = sample_count/(media_duration/timescale);
            }
        }

        printf(
            "{\n"
        );
        printf(
            "  \"stats\": {\n"
            "    \"duration\": %f,\n"
            "    \"avg_segment_bitrate\": %f,\n"
            "    \"max_segment_bitrate\": %f,\n"
            "    \"avg_iframe_bitrate\": %f,\n"
            "    \"max_iframe_bitrate\": %f,\n"
            "    \"frame_rate\": %f\n"
            "  }",
            (double)movie->GetDurationMs()/1000.0,
            average_segment_bitrate,
            Stats.max_segment_bitrate,
            average_iframe_bitrate,
            Stats.max_iframe_bitrate,
            frame_rate
        );
        if (audio_track) {
            AP4_String codec;
            AP4_SampleDescription* sdesc = audio_track->GetSampleDescription(0);
            if (sdesc) {
                sdesc->GetCodecString(codec);
            }
            printf(
                ",\n"
                "  \"audio\": {\n"
                "    \"codec\": \"%s\"\n"
                "  }",
                codec.GetChars()
            );
        }
        if (video_track) {
            AP4_String codec;
            AP4_UI16 width = (AP4_UI16)(video_track->GetWidth()/65536.0);
            AP4_UI16 height = (AP4_UI16)(video_track->GetHeight()/65536.0);
            AP4_SampleDescription* sdesc = video_track->GetSampleDescription(0);
            if (sdesc) {
                sdesc->GetCodecString(codec);
                AP4_VideoSampleDescription* video_desc = AP4_DYNAMIC_CAST(AP4_VideoSampleDescription, sdesc);
                if (video_desc) {
                    width = video_desc->GetWidth();
                    height = video_desc->GetHeight();
                }
            }
            printf(
                ",\n"
                "  \"video\": {\n"
                "    \"codec\": \"%s\",\n"
                "    \"width\": %d,\n"
                "    \"height\": %d\n"
                "  }",
                codec.GetChars(),
                width,
                height
            );
        }
        printf(
            "\n"
            "}\n"
        );
    }

end:
    delete ts_writer;
    delete packed_writer;
    delete input_file;
    input->Release();
    delete linear_reader;
    delete audio_reader;
    delete video_reader;

    return result == AP4_SUCCESS?0:1;
}

fix #691


PackedAudioWriter::WriteHeader(double          timestamp,
                               const char*     private_extension_name,
                               const AP4_UI08* private_extension_data,
                               unsigned int    private_extension_data_size,
                               AP4_ByteStream& output)
{
    unsigned int header_size = 10+10+45+8;
    unsigned int private_extension_name_size = private_extension_name?(unsigned int)AP4_StringLength(private_extension_name)+1:0;
    if (private_extension_name) {
        header_size += 10+private_extension_name_size+private_extension_data_size;;
    }
    AP4_UI08* header = new AP4_UI08[header_size];

    /* ID3 Tag Header
     ID3v2/file identifier      "ID3"
     ID3v2 version              $04 00
     ID3v2 flags                %abcd0000
     ID3v2 size                 4 * %0xxxxxxx
    */
    header[0] = 'I';
    header[1] = 'D';
    header[2] = '3';
    header[3] = 4;
    header[4] = 0;
    header[5] = 0;
    header[6] = 0;
    header[7] = 0;
    header[8] = ((header_size-10)>>7) & 0x7F;
    header[9] =  (header_size-10)     & 0x7F;

    /* PRIV frame
     Frame ID   $xx xx xx xx  (four characters)
     Size       $xx xx xx xx
     Flags      $xx xx

     Owner identifier      <text string> $00
     The private data      <binary data>
    */
    header[10] = 'P';
    header[11] = 'R';
    header[12] = 'I';
    header[13] = 'V';
    header[14] = 0;
    header[15] = 0;
    header[16] = 0;
    header[17] = 45+8;
    header[18] = 0;
    header[19] = 0;
    AP4_CopyMemory(&header[20], "com.apple.streaming.transportStreamTimestamp", 45);
    AP4_UI64 mpeg_ts = (AP4_UI64)(timestamp*90000.0);
    AP4_BytesFromUInt64BE(&header[10+10+45], mpeg_ts);

    // add the extra private extension if needed
    if (private_extension_name) {
        AP4_UI08* ext = &header[10+10+45+8];
        ext[0] = 'P';
        ext[1] = 'R';
        ext[2] = 'I';
        ext[3] = 'V';
        ext[4] = 0;
        ext[5] = 0;
        ext[6] = ((private_extension_name_size+private_extension_data_size)>>7) & 0x7F;
        ext[7] =  (private_extension_name_size+private_extension_data_size)     & 0x7F;
        ext[8] = 0;
        ext[9] = 0;
        AP4_CopyMemory(&ext[10], private_extension_name, private_extension_name_size);
        AP4_CopyMemory(&ext[10+private_extension_name_size], private_extension_data, private_extension_data_size);
    }

    // write the header out
    AP4_Result result = output.Write(header, header_size);
    delete[] header;

    return result;
}

fix #691


TrackSampleReader(AP4_Track& track) : m_Track(track), m_SampleIndex(0) {}

fix #691


MakeSampleAesVideoDescriptor(AP4_DataBuffer& descriptor)
{
    descriptor.SetDataSize(6);
    AP4_UI08* payload = descriptor.UseData();
    payload[0] = AP4_MPEG2_PRIVATE_DATA_INDICATOR_DESCRIPTOR_TAG;
    payload[1] = 4;
    payload[2] = 'z';
    payload[3] = 'a';
    payload[4] = 'v';
    payload[5] = 'c';

    return AP4_SUCCESS;
}

fix #691


SampleEncrypter::Create(const AP4_UI08* key, const AP4_UI08* iv, SampleEncrypter*& encrypter) {
    encrypter = NULL;
    AP4_BlockCipher* block_cipher = NULL;
    AP4_Result result = AP4_DefaultBlockCipherFactory::Instance.CreateCipher(AP4_BlockCipher::AES_128,
                                                                             AP4_BlockCipher::ENCRYPT,
                                                                             AP4_BlockCipher::CBC,
                                                                             NULL,
                                                                             key,
                                                                             16,
                                                                             block_cipher);
    if (AP4_FAILED(result)) return result;
    AP4_CbcStreamCipher* stream_cipher = new AP4_CbcStreamCipher(block_cipher);
    encrypter = new SampleEncrypter(stream_cipher, iv);

    return AP4_SUCCESS;
}

fix #691


ReadSample(SampleReader&   reader,
           AP4_Track&      track,
           AP4_Sample&     sample,
           AP4_DataBuffer& sample_data,
           double&         ts,
           double&         duration,
           bool&           eos)
{
    AP4_Result result = reader.ReadSample(sample, sample_data);
    if (AP4_FAILED(result)) {
        if (result == AP4_ERROR_EOS) {
            // advance the timestamp by the last sample's duration
            ts += duration;
            eos = true;
            return AP4_SUCCESS;
        } else {
            return result;
        }
    }
    ts = (double)sample.GetDts()/(double)track.GetMediaTimeScale();
    duration = sample.GetDuration()/(double)track.GetMediaTimeScale();

    return AP4_SUCCESS;
}

fix #691


MakeSampleAesAudioDescriptor(AP4_DataBuffer&        descriptor,
                             AP4_SampleDescription* sample_description,
                             AP4_DataBuffer&        sample_data /* needed for encrypted AC3 */)
{
    // descriptor
    descriptor.SetDataSize(6);
    AP4_UI08* payload = descriptor.UseData();
    payload[0] = AP4_MPEG2_PRIVATE_DATA_INDICATOR_DESCRIPTOR_TAG;
    payload[1] = 4;
    if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) {
        payload[2] = 'a';
        payload[3] = 'a';
        payload[4] = 'c';
        payload[5] = 'd';
    } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3) {
        payload[2] = 'a';
        payload[3] = 'c';
        payload[4] = '3';
        payload[5] = 'd';
    } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) {
        payload[2] = 'e';
        payload[3] = 'c';
        payload[4] = '3';
        payload[5] = 'd';
    }

    // compute the audio setup data
    AP4_DataBuffer audio_setup_data;
    AP4_Result result = MakeAudioSetupData(audio_setup_data, sample_description, sample_data);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: failed to make audio setup data (%d)\n", result);
        return result;
    }

    descriptor.SetDataSize(descriptor.GetDataSize()+6+audio_setup_data.GetDataSize());
    payload = descriptor.UseData()+6;
    payload[0] = AP4_MPEG2_REGISTRATION_DESCRIPTOR_TAG;
    payload[1] = 4+audio_setup_data.GetDataSize();
    payload[2] = 'a';
    payload[3] = 'p';
    payload[4] = 'a';
    payload[5] = 'd';
    AP4_CopyMemory(&payload[6], audio_setup_data.GetData(), audio_setup_data.GetDataSize());

    return AP4_SUCCESS;
}

fix #691


    virtual AP4_Result WritePartial(const void* buffer,
                                    AP4_Size    bytes_to_write,
                                    AP4_Size&   bytes_written) {
        AP4_UI08* out = new AP4_UI08[bytes_to_write+16];
        AP4_Size  out_size = bytes_to_write+16;
        AP4_Result result = m_StreamCipher->ProcessBuffer((const AP4_UI08*)buffer,
                                                          bytes_to_write,
                                                          out,
                                                          &out_size);
        if (AP4_SUCCEEDED(result)) {
            result = m_Output->Write(out, out_size);
            bytes_written = bytes_to_write;
            m_Size       += bytes_to_write;
        } else {
            bytes_written = 0;
        }
        delete[] out;
        return result;
    }

fix #691


virtual AP4_Result Tell(AP4_Position& position) { position = m_Size; return AP4_SUCCESS; }

fix #691


    void AddReference() {
        ++m_ReferenceCount;
    }

fix #691


EncryptingStream::Create(const AP4_UI08* key, const AP4_UI08* iv, AP4_ByteStream* output, EncryptingStream*& stream) {
    stream = NULL;
    AP4_BlockCipher* block_cipher = NULL;
    AP4_Result result = AP4_DefaultBlockCipherFactory::Instance.CreateCipher(AP4_BlockCipher::AES_128,
                                                                             AP4_BlockCipher::ENCRYPT,
                                                                             AP4_BlockCipher::CBC,
                                                                             NULL,
                                                                             key,
                                                                             16,
                                                                             block_cipher);
    if (AP4_FAILED(result)) return result;
    AP4_CbcStreamCipher* stream_cipher = new AP4_CbcStreamCipher(block_cipher);
    stream_cipher->SetIV(iv);
    stream = new EncryptingStream(stream_cipher, output);

    return AP4_SUCCESS;
}

fix #691


TrackSampleReader::ReadSample(AP4_Sample& sample, AP4_DataBuffer& sample_data)
{
    if (m_SampleIndex >= m_Track.GetSampleCount()) return AP4_ERROR_EOS;
    return m_Track.ReadSample(m_SampleIndex++, sample, sample_data);
}

fix #691


    void Release() {
        if (--m_ReferenceCount == 0) {
            delete this;
        }
    }

fix #691