1391 lines
36 KiB
C
1391 lines
36 KiB
C
|
|
/*
|
|
* Copyright (c) 2024 Amlogic, Inc. All rights reserved.
|
|
*
|
|
* This source code is subject to the terms and conditions defined in the
|
|
* file 'LICENSE' which is part of this source code package.
|
|
*
|
|
* Description:
|
|
*/
|
|
|
|
/**************************************************
|
|
* example based on amcodec
|
|
**************************************************/
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <ctype.h>
|
|
#include <semaphore.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <stdint.h>
|
|
|
|
#include "vcodec_utils.h"
|
|
#include "v4l2_dec.h"
|
|
|
|
//#define DEBUG_FRAME
|
|
|
|
#define BUFFER_SIZE (1024*1024*4)
|
|
#define AML_VP9_HEADER_SIZE 16
|
|
#define VP9_IVF_HEAD_SIZE 32
|
|
#define VP9_IVF_FRAME_HEAD_SIZE 12
|
|
#define STREAM_MODE_WRITE_BLOCK_SIZE (64 * 1024)
|
|
#define is_ivf_type(type) \
|
|
(type == VFORMAT_AV1)
|
|
|
|
static int video_type;
|
|
FILE* fp = NULL;
|
|
static char *filename;
|
|
static char *frame_size_file;
|
|
static char *pix_name;
|
|
int g_dw_mode = 16;
|
|
int g_dump_dec_info_num = 5;
|
|
static sem_t wait_for_end;
|
|
int g_log_level = 0;
|
|
int g_output_flag = 0;
|
|
int g_nv21 = 1;
|
|
|
|
enum v4lplayer_mem_type g_mem_type = V4LPLAYER_MEM_TYPE_MMAP;
|
|
enum v4lplayer_play_mode g_play_mode = V4LPLAYER_FRAME_MODE;
|
|
static int write_es_data(const uint8_t *data, int size)
|
|
{
|
|
v4l2_dec_write_es(data, size);
|
|
v4l2_dec_frame_done();
|
|
return size;
|
|
}
|
|
|
|
int send_buffer_to_device(char *buffer, int Readlen)
|
|
{
|
|
int isize = 0;
|
|
int ret;
|
|
|
|
do {
|
|
ret = write_es_data((const uint8_t *)(buffer + isize), (Readlen - isize));
|
|
if (ret < 0) {
|
|
if (errno != EAGAIN) {
|
|
debug_print(DEBUG_ERROR, "write data failed, errno %d\n", errno);
|
|
return -1;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
isize += ret;
|
|
debug_print(DEBUG_FRAME, "write %d, cur isize %d\n", ret, isize);
|
|
}
|
|
debug_print(DEBUG_FRAME, "ret %d, isize %d\n", ret, isize);
|
|
} while (isize < Readlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************** ivf parser *******************/
|
|
#define MAX_SIZE 0x200000
|
|
|
|
/*!\brief OBU types. */
|
|
typedef enum ATTRIBUTE_PACKED {
|
|
OBU_SEQUENCE_HEADER = 1,
|
|
OBU_TEMPORAL_DELIMITER = 2,
|
|
OBU_FRAME_HEADER = 3,
|
|
OBU_TILE_GROUP = 4,
|
|
OBU_METADATA = 5,
|
|
OBU_FRAME = 6,
|
|
OBU_REDUNDANT_FRAME_HEADER = 7,
|
|
OBU_TILE_LIST = 8,
|
|
OBU_PADDING = 15,
|
|
} OBU_TYPE;
|
|
|
|
/*!\brief OBU metadata types. */
|
|
typedef enum {
|
|
OBU_METADATA_TYPE_RESERVED_0 = 0,
|
|
OBU_METADATA_TYPE_HDR_CLL = 1,
|
|
OBU_METADATA_TYPE_HDR_MDCV = 2,
|
|
OBU_METADATA_TYPE_SCALABILITY = 3,
|
|
OBU_METADATA_TYPE_ITUT_T35 = 4,
|
|
OBU_METADATA_TYPE_TIMECODE = 5,
|
|
} OBU_METADATA_TYPE;
|
|
|
|
typedef struct {
|
|
size_t size; // Size (1 or 2 bytes) of the OBU header (including the
|
|
// optional OBU extension header) in the bitstream.
|
|
OBU_TYPE type;
|
|
int has_size_field;
|
|
int has_extension;
|
|
// The following fields come from the OBU extension header and therefore are
|
|
// only used if has_extension is true.
|
|
int temporal_layer_id;
|
|
int spatial_layer_id;
|
|
} ObuHeader;
|
|
|
|
static const size_t kMaximumLeb128Size = 8;
|
|
static const uint8_t kLeb128ByteMask = 0x7f; // Binary: 01111111
|
|
|
|
// Disallow values larger than 32-bits to ensure consistent behavior on 32 and
|
|
// 64 bit targets: value is typically used to determine buffer allocation size
|
|
// when decoded.
|
|
static const uint64_t kMaximumLeb128Value = UINT32_MAX;
|
|
|
|
size_t uleb_size_in_bytes(uint64_t value) {
|
|
size_t size = 0;
|
|
do {
|
|
++size;
|
|
} while ((value >>= 7) != 0);
|
|
return size;
|
|
}
|
|
|
|
int uleb_decode(const uint8_t *buffer, size_t available, uint64_t *value, size_t *length)
|
|
{
|
|
uint32_t i = 0;
|
|
if (buffer && value) {
|
|
*value = 0;
|
|
for (i = 0; i < kMaximumLeb128Size && i < available; ++i) {
|
|
const uint8_t decoded_byte = *(buffer + i) & kLeb128ByteMask;
|
|
*value |= ((uint64_t)decoded_byte) << (i * 7);
|
|
if ((*(buffer + i) >> 7) == 0) {
|
|
if (length) {
|
|
*length = i + 1;
|
|
}
|
|
|
|
// Fail on values larger than 32-bits to ensure consistent behavior on
|
|
// 32 and 64 bit targets: value is typically used to determine buffer
|
|
// allocation size.
|
|
if (*value > UINT32_MAX) return -1;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we get here, either the buffer/value pointers were invalid,
|
|
// or we ran over the available space
|
|
return -1;
|
|
}
|
|
|
|
int uleb_encode(uint64_t value, size_t available, uint8_t *coded_value, size_t *coded_size)
|
|
{
|
|
uint32_t i = 0;
|
|
const size_t leb_size = uleb_size_in_bytes(value);
|
|
if (value > kMaximumLeb128Value || leb_size > kMaximumLeb128Size ||
|
|
leb_size > available || !coded_value || !coded_size) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < leb_size; ++i) {
|
|
uint8_t byte = value & 0x7f;
|
|
value >>= 7;
|
|
|
|
if (value != 0) byte |= 0x80; // Signal that more bytes follow.
|
|
|
|
*(coded_value + i) = byte;
|
|
}
|
|
|
|
*coded_size = leb_size;
|
|
return 0;
|
|
}
|
|
|
|
int uleb_encode_fixed_size(uint64_t value, size_t available,
|
|
size_t pad_to_size, uint8_t *coded_value, size_t *coded_size)
|
|
{
|
|
uint32_t i;
|
|
if (value > kMaximumLeb128Value || !coded_value || !coded_size ||
|
|
available < pad_to_size || pad_to_size > kMaximumLeb128Size) {
|
|
return -1;
|
|
}
|
|
const uint64_t limit = 1ULL << (7 * pad_to_size);
|
|
if (value >= limit) {
|
|
// Can't encode 'value' within 'pad_to_size' bytes
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < pad_to_size; ++i) {
|
|
uint8_t byte = value & 0x7f;
|
|
value >>= 7;
|
|
|
|
if (i < pad_to_size - 1) byte |= 0x80; // Signal that more bytes follow.
|
|
|
|
*(coded_value + i) = byte;
|
|
}
|
|
|
|
*coded_size = pad_to_size;
|
|
return 0;
|
|
}
|
|
|
|
// Returns 1 when OBU type is valid, and 0 otherwise.
|
|
static int valid_obu_type(int obu_type)
|
|
{
|
|
int valid_type = 0;
|
|
switch (obu_type) {
|
|
case OBU_SEQUENCE_HEADER:
|
|
case OBU_TEMPORAL_DELIMITER:
|
|
case OBU_FRAME_HEADER:
|
|
case OBU_TILE_GROUP:
|
|
case OBU_METADATA:
|
|
case OBU_FRAME:
|
|
case OBU_REDUNDANT_FRAME_HEADER:
|
|
case OBU_TILE_LIST:
|
|
case OBU_PADDING: valid_type = 1; break;
|
|
default: break;
|
|
}
|
|
return valid_type;
|
|
}
|
|
|
|
char obu_type_name[16][32] = {
|
|
"UNKNOWN",
|
|
"OBU_SEQUENCE_HEADER",
|
|
"OBU_TEMPORAL_DELIMITER",
|
|
"OBU_FRAME_HEADER",
|
|
"OBU_TILE_GROUP",
|
|
"OBU_METADATA",
|
|
"OBU_FRAME",
|
|
"OBU_REDUNDANT_FRAME_HEADER",
|
|
"OBU_TILE_LIST",
|
|
"UNKNOWN",
|
|
"UNKNOWN",
|
|
"UNKNOWN",
|
|
"UNKNOWN",
|
|
"UNKNOWN",
|
|
"UNKNOWN",
|
|
"OBU_PADDING"
|
|
};
|
|
|
|
char meta_type_name[6][32] = {
|
|
"OBU_METADATA_TYPE_RESERVED_0",
|
|
"OBU_METADATA_TYPE_HDR_CLL",
|
|
"OBU_METADATA_TYPE_HDR_MDCV",
|
|
"OBU_METADATA_TYPE_SCALABILITY",
|
|
"OBU_METADATA_TYPE_ITUT_T35",
|
|
"OBU_METADATA_TYPE_TIMECODE"
|
|
};
|
|
|
|
struct read_bit_buffer {
|
|
const uint8_t *bit_buffer;
|
|
const uint8_t *bit_buffer_end;
|
|
uint32_t bit_offset;
|
|
};
|
|
|
|
typedef struct DataBuffer {
|
|
const uint8_t *data;
|
|
size_t size;
|
|
} DataBuffer;
|
|
|
|
static int rb_read_bit(struct read_bit_buffer *rb)
|
|
{
|
|
const uint32_t off = rb->bit_offset;
|
|
const uint32_t p = off >> 3;
|
|
const int q = 7 - (int)(off & 0x7);
|
|
if (rb->bit_buffer + p < rb->bit_buffer_end) {
|
|
const int bit = (rb->bit_buffer[p] >> q) & 1;
|
|
rb->bit_offset = off + 1;
|
|
return bit;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int rb_read_literal(struct read_bit_buffer *rb, int bits)
|
|
{
|
|
int value = 0, bit;
|
|
for (bit = bits - 1; bit >= 0; bit--) value |= rb_read_bit(rb) << bit;
|
|
return value;
|
|
}
|
|
|
|
static int read_obu_size(const uint8_t *data,
|
|
size_t bytes_available,
|
|
size_t *const obu_size,
|
|
size_t *const length_field_size)
|
|
{
|
|
uint64_t u_obu_size = 0;
|
|
if (uleb_decode(data, bytes_available, &u_obu_size, length_field_size) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (u_obu_size > UINT32_MAX) return -1;
|
|
*obu_size = (size_t)u_obu_size;
|
|
return 0;
|
|
}
|
|
|
|
// Parses OBU header and stores values in 'header'.
|
|
static int read_obu_header(struct read_bit_buffer *rb, int is_annexb, ObuHeader *header)
|
|
{
|
|
if (!rb || !header) return -1;
|
|
|
|
//const ptrdiff_t bit_buffer_byte_length = rb->bit_buffer_end - rb->bit_buffer;
|
|
const long int bit_buffer_byte_length = rb->bit_buffer_end - rb->bit_buffer;
|
|
if (bit_buffer_byte_length < 1) return -1;
|
|
|
|
header->size = 1;
|
|
|
|
if (rb_read_bit(rb) != 0) {
|
|
// Forbidden bit. Must not be set.
|
|
return -1;
|
|
}
|
|
|
|
header->type = (OBU_TYPE)rb_read_literal(rb, 4);
|
|
|
|
if (!valid_obu_type(header->type))
|
|
return -1;
|
|
|
|
header->has_extension = rb_read_bit(rb);
|
|
header->has_size_field = rb_read_bit(rb);
|
|
|
|
if (!header->has_size_field && !is_annexb) {
|
|
// section 5 obu streams must have obu_size field set.
|
|
return -1;
|
|
}
|
|
|
|
if (rb_read_bit(rb) != 0) {
|
|
// obu_reserved_1bit must be set to 0.
|
|
return -1;
|
|
}
|
|
|
|
if (header->has_extension) {
|
|
if (bit_buffer_byte_length == 1) return -1;
|
|
|
|
header->size += 1;
|
|
header->temporal_layer_id = rb_read_literal(rb, 3);
|
|
header->spatial_layer_id = rb_read_literal(rb, 2);
|
|
if (rb_read_literal(rb, 3) != 0) {
|
|
// extension_header_reserved_3bits must be set to 0.
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int read_obu_header_and_size(const uint8_t *data,
|
|
size_t bytes_available, int is_annexb, ObuHeader *obu_header,
|
|
size_t *const payload_size, size_t *const bytes_read)
|
|
{
|
|
size_t length_field_size_obu = 0;
|
|
size_t length_field_size_payload = 0;
|
|
size_t obu_size = 0;
|
|
int status = 0;
|
|
|
|
if (is_annexb) {
|
|
// Size field comes before the OBU header, and includes the OBU header
|
|
status =
|
|
read_obu_size(data, bytes_available, &obu_size, &length_field_size_obu);
|
|
|
|
if (status != 0) return status;
|
|
}
|
|
|
|
struct read_bit_buffer rb = { data + length_field_size_obu,
|
|
data + bytes_available, 0};
|
|
|
|
status = read_obu_header(&rb, is_annexb, obu_header);
|
|
if (status != 0) return status;
|
|
|
|
if (!obu_header->has_size_field) {
|
|
// Derive the payload size from the data we've already read
|
|
if (obu_size < obu_header->size) return -1;
|
|
|
|
*payload_size = obu_size - obu_header->size;
|
|
} else {
|
|
// Size field comes after the OBU header, and is just the payload size
|
|
status = read_obu_size(
|
|
data + length_field_size_obu + obu_header->size,
|
|
bytes_available - length_field_size_obu - obu_header->size,
|
|
payload_size, &length_field_size_payload);
|
|
if (status != 0) return status;
|
|
}
|
|
|
|
*bytes_read =
|
|
length_field_size_obu + obu_header->size + length_field_size_payload;
|
|
return 0;
|
|
}
|
|
|
|
int parser_frame(int is_annexb, uint8_t *data, const uint8_t *data_end,
|
|
uint8_t *dst_data, uint32_t *frame_len, uint8_t *meta_buf, uint32_t *meta_len)
|
|
{
|
|
int frame_decoding_finished = 0;
|
|
uint32_t obu_size = 0;
|
|
ObuHeader obu_header;
|
|
memset(&obu_header, 0, sizeof(obu_header));
|
|
int seen_frame_header = 0;
|
|
//int next_start_tile = 0;
|
|
DataBuffer obu_size_hdr;
|
|
uint8_t header[20] = {
|
|
0x00, 0x00, 0x01, 0x54,
|
|
0xFF, 0xFF, 0xFE, 0xAB,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
0x41, 0x4D, 0x4C, 0x56,
|
|
0xD0, 0x82, 0x80, 0x00
|
|
};
|
|
uint8_t *p = NULL;
|
|
uint32_t rpu_size = 0;
|
|
|
|
// decode frame as a series of OBUs
|
|
while (!frame_decoding_finished) {
|
|
// struct read_bit_buffer rb;
|
|
size_t payload_size = 0;
|
|
size_t header_size = 0;
|
|
size_t bytes_read = 0;
|
|
size_t bytes_written = 0;
|
|
const size_t bytes_available = data_end - data;
|
|
unsigned int i;
|
|
OBU_METADATA_TYPE meta_type;
|
|
uint64_t type;
|
|
|
|
if (bytes_available == 0 && !seen_frame_header) {
|
|
break;
|
|
}
|
|
|
|
int status = read_obu_header_and_size(data, bytes_available, is_annexb,
|
|
&obu_header, &payload_size, &bytes_read);
|
|
|
|
if (status != 0) {
|
|
return -1;
|
|
}
|
|
|
|
// Record obu size header information.
|
|
obu_size_hdr.data = data + obu_header.size;
|
|
obu_size_hdr.size = bytes_read - obu_header.size;
|
|
|
|
// Note: read_obu_header_and_size() takes care of checking that this
|
|
// doesn't cause 'data' to advance past 'data_end'.
|
|
|
|
if ((size_t)(data_end - data - bytes_read) < payload_size) {
|
|
return -1;
|
|
}
|
|
#ifdef DEBUG_FRAME
|
|
debug_print(DEBUG_FRAME, "\tobu %s len %zu+%zu\n", obu_type_name[obu_header.type], bytes_read, payload_size);
|
|
#endif
|
|
obu_size = bytes_read + payload_size + 4;
|
|
|
|
if (!is_annexb) {
|
|
obu_size = bytes_read + payload_size + 4;
|
|
header_size = 20;
|
|
uleb_encode_fixed_size(obu_size, 4, 4, header + 16, &bytes_written);
|
|
}
|
|
else {
|
|
obu_size = bytes_read + payload_size;
|
|
header_size = 16;
|
|
}
|
|
header[0] = ((obu_size + 4) >> 24) & 0xff;
|
|
header[1] = ((obu_size + 4) >> 16) & 0xff;
|
|
header[2] = ((obu_size + 4) >> 8) & 0xff;
|
|
header[3] = ((obu_size + 4) >> 0) & 0xff;
|
|
header[4] = header[0] ^ 0xff;
|
|
header[5] = header[1] ^ 0xff;
|
|
header[6] = header[2] ^ 0xff;
|
|
header[7] = header[3] ^ 0xff;
|
|
memcpy(dst_data, header, header_size);
|
|
dst_data += header_size;
|
|
memcpy(dst_data, data, bytes_read + payload_size);
|
|
dst_data += bytes_read + payload_size;
|
|
|
|
data += bytes_read;
|
|
*frame_len += 20 + bytes_read + payload_size;
|
|
|
|
switch (obu_header.type) {
|
|
case OBU_TEMPORAL_DELIMITER:
|
|
seen_frame_header = 0;
|
|
//next_start_tile = 0;
|
|
break;
|
|
case OBU_SEQUENCE_HEADER:
|
|
// The sequence header should not change in the middle of a frame.
|
|
if (seen_frame_header) {
|
|
return -1;
|
|
}
|
|
break;
|
|
case OBU_FRAME_HEADER:
|
|
if (data_end == data + payload_size) {
|
|
frame_decoding_finished = 1;
|
|
}
|
|
else {
|
|
seen_frame_header = 1;
|
|
}
|
|
break;
|
|
case OBU_REDUNDANT_FRAME_HEADER:
|
|
case OBU_FRAME:
|
|
if (obu_header.type == OBU_REDUNDANT_FRAME_HEADER) {
|
|
if (!seen_frame_header) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
// OBU_FRAME_HEADER or OBU_FRAME.
|
|
if (seen_frame_header) {
|
|
return -1;
|
|
}
|
|
}
|
|
if (obu_header.type == OBU_FRAME) {
|
|
if (data_end == data + payload_size) {
|
|
frame_decoding_finished = 1;
|
|
seen_frame_header = 0;
|
|
}
|
|
}
|
|
break;
|
|
case OBU_TILE_GROUP:
|
|
if (!seen_frame_header) {
|
|
return -1;
|
|
}
|
|
if (data + payload_size == data_end)
|
|
frame_decoding_finished = 1;
|
|
if (frame_decoding_finished)
|
|
seen_frame_header = 0;
|
|
break;
|
|
case OBU_METADATA:
|
|
uleb_decode(data, 8, &type, &bytes_read);
|
|
if (type < 6)
|
|
meta_type = type;
|
|
else
|
|
meta_type = 0;
|
|
p = data + bytes_read;
|
|
debug_print(DEBUG_FRAME, "\t meta type %s %zu+%zu\n", meta_type_name[type], bytes_read, payload_size - bytes_read);
|
|
|
|
if (meta_type == OBU_METADATA_TYPE_ITUT_T35) {
|
|
#if 0 /* for dumping original obu payload */
|
|
for (i = 0; i < payload_size - bytes_read; i++) {
|
|
debug_print(DEBUG_FRAME, "%02x ", p[i]);
|
|
if (i % 16 == 15) debug_print(DEBUG_FRAME, "\n");
|
|
}
|
|
if (i % 16 != 0) debug_print(DEBUG_FRAME, "\n");
|
|
#endif
|
|
if ((p[0] == 0xb5) /* country code */
|
|
&& ((p[1] == 0x00) && (p[2] == 0x3b)) /* terminal_provider_code */
|
|
&& ((p[3] == 0x00) && (p[4] == 0x00) && (p[5] == 0x08) && (p[6] == 0x00))) { /* terminal_provider_oriented_code */
|
|
meta_buf[0] = meta_buf[1] = meta_buf[2] = 0;
|
|
meta_buf[3] = 0x01; meta_buf[4] = 0x19;
|
|
|
|
if (p[11] & 0x10) {
|
|
rpu_size = 0x100;
|
|
rpu_size |= (p[11] & 0x0f) << 4;
|
|
rpu_size |= (p[12] >> 4) & 0x0f;
|
|
if (p[12] & 0x08) {
|
|
debug_print(DEBUG_FRAME, "\t meta rpu in obu exceed 512 bytes\n");
|
|
break;
|
|
}
|
|
for (i = 0; i < rpu_size; i++) {
|
|
meta_buf[5 + i] = (p[12 + i] & 0x07) << 5;
|
|
meta_buf[5 + i] |= (p[13 + i] >> 3) & 0x1f;
|
|
}
|
|
rpu_size += 5;
|
|
} else {
|
|
rpu_size = (p[10] & 0x1f) << 3;
|
|
rpu_size |= (p[11] >> 5) & 0x07;
|
|
for (i = 0; i < rpu_size; i++) {
|
|
meta_buf[5 + i] = (p[11 + i] & 0x0f) << 4;
|
|
meta_buf[5 + i] |= (p[12 + i] >> 4) & 0x0f;
|
|
}
|
|
rpu_size += 5;
|
|
}
|
|
*meta_len = rpu_size;
|
|
}
|
|
} else if (meta_type == OBU_METADATA_TYPE_HDR_CLL) {
|
|
debug_print(DEBUG_FRAME, "\t\t hdr10 cll:\n");
|
|
debug_print(DEBUG_FRAME, "\t\t max_cll = %x\n", (p[0] << 8) | p[1]);
|
|
debug_print(DEBUG_FRAME, "\t\t max_fall = %x\n", (p[2] << 8) | p[3]);
|
|
} else if (meta_type == OBU_METADATA_TYPE_HDR_MDCV) {
|
|
debug_print(DEBUG_FRAME, "\t\t hdr10 primaries[r,g,b] = \n");
|
|
for (i = 0; i < 3; i++) {
|
|
printf("\t\t %x, %x\n", (p[i * 4] << 8) | p[i * 4 + 1],
|
|
(p[i * 4 + 2] << 8) | p[i * 4 + 3]);
|
|
}
|
|
debug_print(DEBUG_FRAME, "\t\t white point = %x, %x\n", (p[12] << 8) | p[13], (p[14] << 8) | p[15]);
|
|
debug_print(DEBUG_FRAME, "\t\t maxl = %x\n", (p[16] << 24) | (p[17] << 16) | (p[18] << 8) | p[19]);
|
|
debug_print(DEBUG_FRAME, "\t\t minl = %x\n", (p[20] << 24) | (p[21] << 16) | (p[22] << 8) | p[23]);
|
|
}
|
|
break;
|
|
case OBU_TILE_LIST:
|
|
break;
|
|
case OBU_PADDING:
|
|
break;
|
|
default:
|
|
// Skip unrecognized OBUs
|
|
break;
|
|
}
|
|
|
|
data += payload_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool is_video_file_type_ivf(FILE *fp, char *buffer)
|
|
{
|
|
if (fp && is_ivf_type(video_type)) {
|
|
fread(buffer, 1, 4, fp);
|
|
fseek(fp, 0, SEEK_SET);
|
|
if ((buffer[0] == 0x44) &&
|
|
(buffer[1] == 0x4B) &&
|
|
(buffer[2] == 0x49) &&
|
|
(buffer[3] == 0x46))
|
|
return true;
|
|
} else if (is_ivf_type(video_type)) {
|
|
if ((buffer[0] == 0x44) &&
|
|
(buffer[1] == 0x4B) &&
|
|
(buffer[2] == 0x49) &&
|
|
(buffer[3] == 0x46))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int av1_ivf_write_dat(FILE *src_fp, uint8_t *src_buffer)
|
|
{
|
|
int frame_count = 0;
|
|
unsigned int src_frame_size = 0;
|
|
unsigned int dst_frame_size = 0;
|
|
unsigned int meta_size = 0;
|
|
uint8_t *dst_buffer = NULL;
|
|
uint8_t *meta_buffer = NULL;
|
|
int process_count = -1;
|
|
unsigned int *p_size;
|
|
|
|
meta_buffer = calloc(1, 1024);
|
|
if (!meta_buffer) {
|
|
debug_print(DEBUG_ERROR, "fail to alloc meta buf\n");
|
|
return -1;
|
|
}
|
|
if (fread(src_buffer, 1, 32, src_fp) != 32) {
|
|
debug_print(DEBUG_ERROR, "read input file error!\n");
|
|
free(meta_buffer);
|
|
return -1;
|
|
}
|
|
p_size = (unsigned int *)(src_buffer + 24);
|
|
process_count = *p_size;
|
|
debug_print(DEBUG_DEF, "av1 ivf frame number = %d\n", process_count);
|
|
/*
|
|
* no frame count limit
|
|
*/
|
|
/* coverity[tainted_data:SUPPRESS] */
|
|
while (frame_count < process_count) {
|
|
if (fread(src_buffer, 1, 12, src_fp) != 12) {
|
|
printf("end of file!\n");
|
|
break;
|
|
}
|
|
p_size = (unsigned int *)src_buffer;
|
|
src_frame_size = *p_size;
|
|
if (src_frame_size > BUFFER_SIZE)
|
|
src_frame_size = BUFFER_SIZE;
|
|
|
|
#ifdef DEBUG_FRAME
|
|
debug_print(DEBUG_FRAME, "frame %d, size %d\n", frame_count, src_frame_size);
|
|
#endif
|
|
if (fread(src_buffer, 1, src_frame_size, src_fp) != src_frame_size) {
|
|
debug_print(DEBUG_ERROR, "read input file error %d!\n", src_frame_size);
|
|
break;
|
|
}
|
|
|
|
dst_buffer = calloc(1, src_frame_size + 4096);
|
|
if (!dst_buffer) {
|
|
debug_print(DEBUG_ERROR, "failed to alloc frame buf\n");
|
|
break;
|
|
}
|
|
dst_frame_size = 0;
|
|
meta_size = 0;
|
|
|
|
parser_frame(0, src_buffer, src_buffer + src_frame_size, dst_buffer, &dst_frame_size, meta_buffer, &meta_size);
|
|
if (dst_frame_size) {
|
|
#ifdef DEBUG_FRAME
|
|
debug_print(DEBUG_FRAME, "\toutput len=%d\n", dst_frame_size);
|
|
#endif
|
|
if (send_buffer_to_device((char *)dst_buffer, dst_frame_size) < 0) {
|
|
free(dst_buffer);
|
|
break;
|
|
}
|
|
}
|
|
if (meta_size) {
|
|
debug_print(DEBUG_FRAME, "\t meta len=%d\n", meta_size);
|
|
/* dump meta here */
|
|
}
|
|
free(dst_buffer);
|
|
frame_count++;
|
|
}
|
|
debug_print(DEBUG_STATE, "Process %d frame\n", frame_count);
|
|
free(meta_buffer);
|
|
return 0;
|
|
}
|
|
|
|
/****************** end of ivf parer *************/
|
|
|
|
#if 1
|
|
//AV1
|
|
static int obu_frame_frame_head_come_after_tile = 0;
|
|
//static int frame_decoded = 0;
|
|
static int decoding_data_flag = 0;
|
|
#endif
|
|
|
|
unsigned char is_picture_start(int nal_unit_type)
|
|
{
|
|
unsigned char ret = 0;
|
|
if (video_type == VFORMAT_AVS) {
|
|
if (nal_unit_type == 0xB3 || //I_PICTURE_START_CODE
|
|
nal_unit_type == 0xB6 //PB_PICTURE_START_CODE
|
|
)
|
|
ret = 1;
|
|
} else if (video_type == VFORMAT_VP9) {
|
|
/*to do*/
|
|
goto check_av1;
|
|
} else if (video_type == VFORMAT_AV1) {
|
|
check_av1:
|
|
if (nal_unit_type == OBU_FRAME_HEADER ||
|
|
nal_unit_type == OBU_FRAME) {
|
|
ret = 1;
|
|
obu_frame_frame_head_come_after_tile = 1;
|
|
if (nal_unit_type == OBU_FRAME) { /*have tile group in this OBU*/
|
|
obu_frame_frame_head_come_after_tile = 0;
|
|
decoding_data_flag = 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ret = 1;
|
|
return ret;
|
|
}
|
|
|
|
unsigned char is_picture_end(int nal_unit_type)
|
|
{
|
|
unsigned char ret = 0;
|
|
if (video_type == VFORMAT_AVS) {
|
|
if (nal_unit_type == 0xB3 || //I_PICTURE_START_CODE
|
|
nal_unit_type == 0xB6 || //PB_PICTURE_START_CODE
|
|
nal_unit_type == 0xB0 || //SEQUENCE_HEADER_CODE
|
|
nal_unit_type == 0xB1 //SEQUENCE_END_CODE
|
|
)
|
|
ret = 1;
|
|
} else if (video_type == VFORMAT_VP9) {
|
|
/*to do*/
|
|
goto check_av1;
|
|
} else if (video_type == VFORMAT_AV1) {
|
|
check_av1:
|
|
if (nal_unit_type == OBU_TILE_GROUP) {
|
|
obu_frame_frame_head_come_after_tile = 0;
|
|
decoding_data_flag = 1;
|
|
} else if (nal_unit_type == OBU_FRAME_HEADER ||
|
|
nal_unit_type == OBU_FRAME) {
|
|
if (decoding_data_flag)
|
|
ret = 1;
|
|
decoding_data_flag = 0;
|
|
obu_frame_frame_head_come_after_tile = 1;
|
|
if (nal_unit_type == OBU_FRAME) { /*have tile group in this OBU*/
|
|
obu_frame_frame_head_come_after_tile = 0;
|
|
decoding_data_flag = 1;
|
|
}
|
|
} else if (nal_unit_type == OBU_REDUNDANT_FRAME_HEADER &&
|
|
obu_frame_frame_head_come_after_tile == 0) {
|
|
decoding_data_flag = 0;
|
|
debug_print(DEBUG_ERROR, "Warning, OBU_REDUNDANT_FRAME_HEADER come without OBU_FRAME or OBU_FRAME_HEAD\n");
|
|
}
|
|
} else
|
|
ret = 1;
|
|
return ret;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
|
|
unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
|
|
unsigned max_size; //! Nal Unit Buffer size
|
|
int forbidden_bit; //! should be always FALSE
|
|
int nal_reference_idc; //! NALU_PRIORITY_xxxx
|
|
int nal_unit_type; //! NALU_TYPE_xxxx
|
|
unsigned char *buf; //! contains the first byte followed by the EBSP
|
|
unsigned short lost_packets; //! true, if packet loss is detected
|
|
} NALU_t;
|
|
|
|
NALU_t *AllocNALU(int buffersize)
|
|
{
|
|
NALU_t *n;
|
|
|
|
if ((n = (NALU_t*)calloc (1, sizeof (NALU_t))) == NULL) {
|
|
debug_print(DEBUG_ERROR, "AllocNALU: n");
|
|
exit(0);
|
|
}
|
|
|
|
n->max_size=buffersize;
|
|
|
|
if ((n->buf = (unsigned char*)calloc (buffersize, sizeof (char))) == NULL) {
|
|
free (n);
|
|
debug_print(DEBUG_ERROR, "AllocNALU: n->buf");
|
|
exit(0);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
void FreeNALU(NALU_t *n)
|
|
{
|
|
if (n) {
|
|
if (n->buf) {
|
|
free(n->buf);
|
|
n->buf=NULL;
|
|
}
|
|
free (n);
|
|
}
|
|
}
|
|
|
|
static int FindStartCode2 (unsigned char *Buf)
|
|
{
|
|
if ((Buf[0] != 0) || (Buf[1] != 0) || (Buf[2] != 1)) return 0; //0x000001
|
|
else return 1;
|
|
}
|
|
|
|
static int FindStartCode3 (unsigned char *Buf)
|
|
{
|
|
if ((Buf[0] != 0) || (Buf[1] != 0) || (Buf[2] != 0) || (Buf[3] != 1)) return 0;//0x00000001
|
|
else return 1;
|
|
}
|
|
|
|
static int FindAmlStartCode(unsigned char *Buf)
|
|
{
|
|
unsigned int len;
|
|
unsigned int len2;
|
|
len = (Buf[0] << 24) | (Buf[1] << 16) | (Buf[2] << 8) | (Buf[3] << 0);
|
|
len2 = (Buf[4] << 24) | (Buf[5] << 16) | (Buf[6] << 8) | (Buf[7] << 0);
|
|
if (((len + len2) == 0xffffffff) &&
|
|
(Buf[8] == 0 && Buf[9] == 0 && Buf[10] == 0 && Buf[11] == 1) &&
|
|
(Buf[12] == 0x41 && Buf[13] == 0x4d && Buf[14] == 0x4c && Buf[15] == 0x56)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int fgetc_direct(FILE *fe)
|
|
{
|
|
int ret;
|
|
|
|
ret = fgetc(fe);
|
|
return (ret != EOF) ? ret : '0';
|
|
}
|
|
|
|
int GetAnnexbNALU (FILE* fe, NALU_t *nalu)
|
|
{
|
|
int pos = 0;
|
|
int StartCodeFound, rewind;
|
|
unsigned char *Buf;
|
|
int info2 = 0, info3 = 0;
|
|
int i;
|
|
int prefix_len = 4;
|
|
if (video_type == VFORMAT_VP9 || video_type == VFORMAT_AV1)
|
|
prefix_len = 16;
|
|
if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL) {
|
|
debug_print(DEBUG_ERROR, "GetAnnexbNALU: Could not allocate Buf memory\n");
|
|
return -1;
|
|
}
|
|
|
|
nalu->startcodeprefix_len=0;
|
|
while (!feof(fe)) {
|
|
if (nalu->startcodeprefix_len<prefix_len) {
|
|
Buf[nalu->startcodeprefix_len++] = fgetc_direct(fe);
|
|
} else{
|
|
if (prefix_len == 4) {
|
|
for (i = 0; i < 3; i++)
|
|
Buf[i] = Buf[i+1];
|
|
} else {
|
|
for (i = 0; i < prefix_len; i++)
|
|
Buf[i] = Buf[i+1];
|
|
}
|
|
Buf[nalu->startcodeprefix_len - 1] = fgetc_direct(fe);
|
|
}
|
|
if (prefix_len == 4) {
|
|
if (nalu->startcodeprefix_len >= 3) {
|
|
if (FindStartCode2(Buf)) {
|
|
pos = nalu->startcodeprefix_len;
|
|
nalu->startcodeprefix_len = 3;
|
|
break;
|
|
}
|
|
}
|
|
if (nalu->startcodeprefix_len == 4) {
|
|
if (FindStartCode3(Buf)) {
|
|
pos = nalu->startcodeprefix_len;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (nalu->startcodeprefix_len == prefix_len) {
|
|
if (FindAmlStartCode(Buf)) {
|
|
pos = nalu->startcodeprefix_len;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
StartCodeFound = 0;
|
|
info2 = 0;
|
|
info3 = 0;
|
|
|
|
while (!StartCodeFound) {
|
|
if (feof (fe)) {
|
|
rewind = -1;
|
|
goto fill_data;
|
|
}
|
|
Buf[pos++] = fgetc_direct (fe);
|
|
if (prefix_len == 4) {
|
|
info3 = FindStartCode3(&Buf[pos - 4]);
|
|
if (info3 != 1)
|
|
info2 = FindStartCode2(&Buf[pos - 3]);
|
|
StartCodeFound = (info2 == 1 || info3 == 1);
|
|
} else {
|
|
StartCodeFound = FindAmlStartCode(&Buf[pos-prefix_len]);
|
|
}
|
|
}
|
|
|
|
// Here, we have found another start code (and read length of startcode bytes more than we should
|
|
// have. Hence, go back in the file
|
|
if (prefix_len == 4)
|
|
rewind = (info3 == 1) ? -4 : -3;
|
|
else
|
|
rewind = -prefix_len;
|
|
|
|
if (0 != fseek (fe, rewind, SEEK_CUR))
|
|
debug_print(DEBUG_ERROR, "GetAnnexbNALU: Cannot fseek in the bit stream file");
|
|
|
|
// Here the Start code, the complete NALU, and the next start code is in the Buf.
|
|
// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
|
|
// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code
|
|
fill_data:
|
|
nalu->len = (pos + rewind);
|
|
if (nalu->len > MAX_SIZE) {
|
|
debug_print(DEBUG_ERROR, "%d: Error: to many data to copy %d\n", __LINE__, nalu->len);
|
|
exit(0);
|
|
}
|
|
memcpy (nalu->buf, &Buf[0], nalu->len);//copy nalu, include 0x000001 or 0x00000001
|
|
if (video_type == VFORMAT_VP9 || video_type == VFORMAT_AV1) {
|
|
unsigned char* p = &nalu->buf[nalu->startcodeprefix_len];
|
|
while ((*p++)&0x80) {
|
|
|
|
}
|
|
nalu->nal_reference_idc = 0;
|
|
nalu->forbidden_bit = *p & 0x80;
|
|
nalu->nal_unit_type = (*p>>3)&0xf;
|
|
} else {
|
|
nalu->forbidden_bit = nalu->buf[nalu->startcodeprefix_len] & 0x80; //1 bit
|
|
nalu->nal_reference_idc = nalu->buf[nalu->startcodeprefix_len] & 0x60; // 2 bit
|
|
//nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
|
|
nalu->nal_unit_type = (nalu->buf[nalu->startcodeprefix_len]);
|
|
}
|
|
free(Buf);
|
|
|
|
return (pos + rewind);
|
|
}
|
|
|
|
static void dump(NALU_t *n)
|
|
{
|
|
if (!n)return;
|
|
/*
|
|
printf("a new nal:");
|
|
|
|
printf(" len: %d ", n->len);
|
|
printf("nal_unit_type: %x\n", n->nal_unit_type);
|
|
*/
|
|
}
|
|
|
|
int av1_frame_mode_write_dat(FILE *fp, char *buffer)
|
|
{
|
|
NALU_t *n = AllocNALU(MAX_SIZE);
|
|
int buf_pos = 0;
|
|
|
|
|
|
//unsigned char pic_head_found = 0;
|
|
while (!feof(fp)) {
|
|
GetAnnexbNALU(fp, n);
|
|
dump(n);
|
|
if ((buf_pos + n->len) > BUFFER_SIZE) {
|
|
debug_print(DEBUG_ERROR, "%d: Error: to many data to copy %d\n", __LINE__, buf_pos + n->len);
|
|
exit(0);
|
|
}
|
|
memcpy(&buffer[buf_pos], n->buf, n->len);
|
|
buf_pos += n->len;
|
|
if (is_picture_start(n->nal_unit_type)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (!feof(fp)) {
|
|
GetAnnexbNALU(fp, n);
|
|
if (is_picture_end(n->nal_unit_type)) {
|
|
//printf("Send Data Size =%d\n", buf_pos);
|
|
#ifndef TEST_ON_PC
|
|
/*
|
|
int delay_time = 0;
|
|
if (delay_time > 0)
|
|
delay(delay_time);
|
|
*/
|
|
if (send_buffer_to_device(buffer, buf_pos) < 0)
|
|
goto error_ret;
|
|
#endif
|
|
buf_pos = 0;
|
|
}
|
|
|
|
if ((buf_pos + n->len) > BUFFER_SIZE) {
|
|
debug_print(DEBUG_ERROR, "%d: Error: to many data to copy %d\n", __LINE__, buf_pos + n->len);
|
|
goto error_ret;
|
|
}
|
|
|
|
memcpy(&buffer[buf_pos], n->buf, n->len);
|
|
buf_pos += n->len;
|
|
dump(n);
|
|
}
|
|
error_ret:
|
|
free(n);
|
|
return 0;
|
|
}
|
|
|
|
static int padding_for_mpeg12_dma_frame(char *buffer, int frame_size)
|
|
{
|
|
if ((g_mem_type == V4LPLAYER_MEM_TYPE_DMA) &&
|
|
(g_play_mode == V4LPLAYER_FRAME_MODE) &&
|
|
(video_type == VFORMAT_MPEG12)) {
|
|
buffer[frame_size] = 0x00;
|
|
buffer[frame_size + 1] = 0x00;
|
|
buffer[frame_size + 2] = 0x01;
|
|
buffer[frame_size + 3] = 0x00;
|
|
|
|
return (frame_size + 4);
|
|
}
|
|
|
|
return frame_size;
|
|
}
|
|
|
|
int frame_mode_write_dat(FILE *fp, FILE *fszp, char *buffer)
|
|
{
|
|
char frame_size_str[32];
|
|
char *s_rt;
|
|
int frame_size;
|
|
int ret;
|
|
|
|
while (!feof(fp)) {
|
|
memset(frame_size_str, 0, sizeof(frame_size_str));
|
|
s_rt = fgets(frame_size_str, 32, fszp);
|
|
if (s_rt == NULL)
|
|
break;
|
|
frame_size = atoi(frame_size_str);
|
|
if (frame_size) {
|
|
memset(buffer, 0, BUFFER_SIZE);
|
|
ret = fread(buffer, 1, frame_size, fp);
|
|
ret = padding_for_mpeg12_dma_frame(buffer, frame_size);
|
|
#ifdef DEBUG_FRAME
|
|
debug_print(DEBUG_FRAME, "read size %d/%d, %x %x %x %x %x %x %x %x ...\n", ret, frame_size,
|
|
buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
|
|
#endif
|
|
if (ret < frame_size)
|
|
debug_print(DEBUG_ERROR, "read back size %d, less than frame size %d\n", ret, frame_size);
|
|
else {
|
|
if (send_buffer_to_device(buffer, ret) < 0) {
|
|
debug_print(DEBUG_ERROR, "send data failed\n");
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
debug_print(DEBUG_ERROR, "error: read frame size 0\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int stream_mode_write_dat(FILE *fp, char *buffer)
|
|
{
|
|
int ret;
|
|
char padding[STREAM_MODE_WRITE_BLOCK_SIZE] = {0};
|
|
|
|
while (!feof(fp)) {
|
|
memset(buffer, 0, BUFFER_SIZE);
|
|
ret = fread(buffer, 1, STREAM_MODE_WRITE_BLOCK_SIZE, fp);
|
|
if (ret == 0) {
|
|
debug_print(DEBUG_ERROR, "error write data done\n");
|
|
break;
|
|
}else if(ret < 0) {
|
|
debug_print(DEBUG_ERROR, "error write data error:%d\n", ret);
|
|
} else {
|
|
if (send_buffer_to_device(buffer, ret) < 0) {
|
|
debug_print(DEBUG_ERROR, "send data failed\n");
|
|
break;
|
|
}
|
|
}
|
|
// can not get read point, it's meaningless to use 'ring_buf.start',
|
|
// in other word, we cannot know whether buf is full or not, so write buf must sleep.
|
|
usleep(100 * 1000);
|
|
}
|
|
|
|
send_buffer_to_device(padding, STREAM_MODE_WRITE_BLOCK_SIZE);
|
|
|
|
debug_print(0, "stream write data done\n");
|
|
return 0;
|
|
}
|
|
|
|
static void usage()
|
|
{
|
|
printf("Command help:\n");
|
|
printf("1. To play a video: v4lplayer -i <file> -f <format> -d <dw_mode> -s <FSZ> -l <log_level> -o <dump> -n <num>\n");
|
|
printf(" -i, --ifile, input es file\n");
|
|
printf(" -f, --format, video format\n");
|
|
printf("\t0:mpeg12\t1:mpeg4\t\t2:h264\t\t3:mjpeg\n");
|
|
printf("\t5:jpeg\t\t6:vc1\t\t7:avs\t\t11:hevc\n");
|
|
printf("\t14:vp9\t\t15:avs2\t\t16:av1\t\t17:avs3\n");
|
|
printf("\t18:h266\n");
|
|
printf(" -s, --size, frame size file\n");
|
|
printf(" -d, --dw_mode: (No dw mode will be forced to dw 16)\n");
|
|
printf("\t0: no DW compressed only\n");
|
|
printf("\t1: DW with 1:1 ratio\n");
|
|
printf("\t2: DW with 1:4 down sample\n");
|
|
printf("\t3: DW with 1:4 down sample\n");
|
|
printf("\t4: DW with 1:2 down sample\n");
|
|
printf("\t16: DW only\n");
|
|
printf(" -l, --log_level, enable more log\n");
|
|
printf("\tbit 0: decoder state\tbit 1: debug frame\n");
|
|
printf(" -o, --output, output yuv and crc\n");
|
|
printf("\tbit 0: print crc\n");
|
|
printf("\tbit 1: dump yuv to /data/tmp\n");
|
|
printf("\tbit 2: dump crc to /data/tmp\n");
|
|
printf("\tbit 3: reserved\n");
|
|
printf("\tbit 4: dump stream info to /data/tmp\n");
|
|
printf("\tbit 5: dump statistic info to /data/tmp\n");
|
|
printf("\tbit 6: dump afd info to /data/tmp\n");
|
|
printf("\tbit 7: dump cc info to /data/tmp\n");
|
|
printf("\tbit 8: dump hdr10 info to /data/tmp\n");
|
|
printf("\tbit 9: dump hdr10p info to /data/tmp\n");
|
|
printf("\tbit 10: dump cuva info to /data/tmp\n");
|
|
printf("\tbit 11: dump amdv info to /data/tmp\n");
|
|
printf("\tbit 12: dump frame info to /data/tmp\n");
|
|
printf("\tBefore dumping, run 'mkdir -p /data/tmp -m 777;setenforce 0;rm /data/tmp/* -rf' command\n");
|
|
printf(" -n, --number, dump decoder info num\n");
|
|
printf(" -p, --pix_fmt, set nv12 or nv21\n");
|
|
printf(" -m, memory_type and play_mode:\n");
|
|
printf("\t bit:0 memory type: 0 for mmap , 1 for dma\n");
|
|
printf("\t bit:1 play_mode: 0 for frame mode , 1 for stream mode\n");
|
|
printf(" -h, --help, usage\n");
|
|
printf("example : v4lplayer -f 2 -d 16 -i /data/h264.es -s /data/h264.fsz\n");
|
|
printf("--subformat, additional arg for vc1 video\n");
|
|
printf("\t0: WMV3 video\t1: WVC1 video\n");
|
|
}
|
|
|
|
static const char short_options[] = "i:s:d:f:l:o:n:p:m:h";
|
|
|
|
static const struct option
|
|
long_options[] = {
|
|
{ "sizefile", required_argument, NULL, 's' },
|
|
{ "ifile", required_argument, NULL, 'i' },
|
|
{ "double_write", required_argument, NULL, 'd' },
|
|
{ "format", required_argument, NULL, 'f' },
|
|
{ "log", required_argument, NULL, 'l' },
|
|
{ "output", required_argument, NULL, 'o' },
|
|
{ "num", required_argument, NULL, 'n' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "mem_and_mode", required_argument, NULL, 'm' },
|
|
{ "pix_fmt", required_argument, NULL, 'p' },
|
|
{ "subformat", required_argument, NULL, 0 },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static int parse_para(int argc, char *argv[])
|
|
{
|
|
for (;;) {
|
|
int idx;
|
|
int c;
|
|
unsigned long value = 0;
|
|
char *endptr;
|
|
|
|
c = getopt_long(argc, argv, short_options, long_options, &idx);
|
|
|
|
if (-1 == c)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 0: /* getopt_long() flag */
|
|
if (strcmp(long_options[idx].name, "subformat") == 0) {
|
|
value = atoi(optarg);
|
|
if (!(value == 0 || value == 1)) {
|
|
debug_print(DEBUG_ERROR, "invalid subformat %d\n",
|
|
value);
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
set_vc1_subformat(value);
|
|
}
|
|
break;
|
|
case 'l':
|
|
g_log_level = atoi(optarg);
|
|
break;
|
|
case 'd':
|
|
if (strncmp(optarg, "0x", 2) == 0 || strncmp(optarg, "0X", 2) == 0) {
|
|
value = strtoul(optarg, &endptr, 16);
|
|
} else {
|
|
value = strtoul(optarg, &endptr, 10);
|
|
}
|
|
|
|
if (*endptr != '\0') {
|
|
debug_print(DEBUG_ERROR, "Invalid value -o: %s\n", optarg);
|
|
break;
|
|
}
|
|
|
|
g_dw_mode = value;
|
|
if (g_dw_mode != 0 &&
|
|
g_dw_mode != 1 &&
|
|
g_dw_mode != 2 &&
|
|
g_dw_mode != 3 &&
|
|
g_dw_mode != 4 &&
|
|
g_dw_mode != 16 &&
|
|
g_dw_mode != 0x200) {
|
|
debug_print(DEBUG_ERROR, "invalid dw_mode 0x%x\n", g_dw_mode);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'p':
|
|
pix_name = optarg;
|
|
if (!strcmp(pix_name, "nv12"))
|
|
g_nv21 = 0;
|
|
else if (!strcmp(pix_name, "nv21"))
|
|
g_nv21 = 1;
|
|
else
|
|
debug_print(DEBUG_ERROR, "invalid pix_name %s\n", pix_name);
|
|
break;
|
|
case 'f':
|
|
video_type = atoi(optarg);
|
|
break;
|
|
case 'i':
|
|
filename = strdup(optarg);
|
|
break;
|
|
case 's':
|
|
frame_size_file = strdup(optarg);
|
|
break;
|
|
case 'o':
|
|
if (strncmp(optarg, "0x", 2) == 0 || strncmp(optarg, "0X", 2) == 0) {
|
|
value = strtoul(optarg, &endptr, 16);
|
|
} else {
|
|
value = strtoul(optarg, &endptr, 10);
|
|
}
|
|
|
|
if (*endptr != '\0') {
|
|
debug_print(DEBUG_ERROR, "Invalid value -o: %s\n", optarg);
|
|
break;
|
|
}
|
|
|
|
g_output_flag = value;
|
|
debug_print(DEBUG_DEF, "set output: %d\n", value);
|
|
break;
|
|
case 'n':
|
|
g_dump_dec_info_num = atoi(optarg);
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
return -1;
|
|
case 'm':
|
|
if (strncmp(optarg, "0x", 2) == 0 || strncmp(optarg, "0X", 2) == 0) {
|
|
value = strtoul(optarg, &endptr, 16);
|
|
} else {
|
|
value = strtoul(optarg, &endptr, 10);
|
|
}
|
|
|
|
if (*endptr != '\0') {
|
|
debug_print(DEBUG_ERROR, "Invalid value -m: %s\n", optarg);
|
|
break;
|
|
}
|
|
|
|
if (value & 0b1) {
|
|
g_mem_type = V4LPLAYER_MEM_TYPE_DMA;
|
|
}
|
|
|
|
if (value & 0b100) {
|
|
g_mem_type = V4LPLAYER_MEM_TYPE_USER_PTR;
|
|
g_play_mode = V4LPLAYER_FRAME_MODE; // force frame mode
|
|
}
|
|
|
|
if (value & 0b10) {
|
|
g_play_mode = V4LPLAYER_STREAM_MODE;
|
|
g_mem_type = V4LPLAYER_MEM_TYPE_DMA; // force dma
|
|
}
|
|
break;
|
|
default:
|
|
usage();
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void decode_finish()
|
|
{
|
|
sem_post(&wait_for_end);
|
|
}
|
|
|
|
static int start_decoder(enum vformat_e type)
|
|
{
|
|
int ret;
|
|
ret = v4l2_dec_init(type, decode_finish);
|
|
if (ret) {
|
|
debug_print(DEBUG_ERROR, "FATAL: start_decoder error:%d\n",ret);
|
|
exit(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
FILE* frame_size_fp = NULL;
|
|
char *buffer = NULL;
|
|
|
|
if (argc < 6) {
|
|
usage();
|
|
return -1;
|
|
}
|
|
|
|
g_dw_mode = 16;
|
|
g_nv21 = 1;
|
|
if (parse_para(argc, argv))
|
|
return -1;
|
|
|
|
buffer = malloc(BUFFER_SIZE);
|
|
if (NULL == buffer) {
|
|
debug_print(DEBUG_ERROR, "malloc write buffer fail\n");
|
|
return -1;
|
|
}
|
|
memset(buffer, 0, BUFFER_SIZE);
|
|
|
|
debug_print(DEBUG_STATE, "set dw mode:%d, nv21:%d\n", g_dw_mode, g_nv21);
|
|
if (((frame_size_fp = fopen(frame_size_file, "rb")) == NULL) &&
|
|
video_type != VFORMAT_AV1 && g_play_mode == V4LPLAYER_FRAME_MODE) {
|
|
debug_print(DEBUG_ERROR, "open file %s error!, force stream mode\n", frame_size_file);
|
|
return -1;
|
|
}
|
|
|
|
debug_print(DEBUG_DEF, "\n*********CODEC PLAYER DEMO************\n\n");
|
|
debug_print(DEBUG_DEF, "file %s to be played\n", filename);
|
|
|
|
if ((fp = fopen(filename, "rb")) == NULL) {
|
|
debug_print(DEBUG_ERROR, "open file error!\n");
|
|
goto free_buff;
|
|
}
|
|
|
|
start_decoder(video_type);
|
|
if (g_play_mode == V4LPLAYER_STREAM_MODE) {
|
|
stream_mode_write_dat(fp, buffer);
|
|
}else {
|
|
if (is_video_file_type_ivf(fp, buffer)) {
|
|
if (video_type == VFORMAT_AV1) {
|
|
debug_print(DEBUG_DEF, "input video file is ivf with av1.\n");
|
|
av1_ivf_write_dat(fp, (uint8_t *)buffer);
|
|
}
|
|
} else {
|
|
if ((video_type == VFORMAT_AV1) && (frame_size_fp == NULL))
|
|
av1_frame_mode_write_dat(fp, buffer);
|
|
else {
|
|
if (!frame_size_file) {
|
|
debug_print(DEBUG_ERROR, "open file %s error!, force stream mode\n", frame_size_file);
|
|
goto free_buff;
|
|
}
|
|
frame_mode_write_dat(fp, frame_size_fp, buffer);
|
|
}
|
|
}
|
|
}
|
|
v4l2_dec_eos();
|
|
sem_wait(&wait_for_end);
|
|
|
|
error:
|
|
v4l2_dec_destroy();
|
|
sem_destroy(&wait_for_end);
|
|
fclose(fp);
|
|
free_buff:
|
|
free(buffer);
|
|
return 0;
|
|
}
|
|
|