Exemple #1
0
Type Parser::parse_y_symbol(char& c, std::string& str)
{
    Type token = lexer->get_token(c, str);
    if(token == HASH)
    {
        token = lexer->get_token(c, str);
        if(token == MINUS)
        {
            token = lexer->get_token(c, str);
        }
        if(token != DEC && token != HEX)
            print_fatal("Syntax error: expected hex or dec but got %s\n", str.c_str());    
    }
    else if(token == REGISTER)
    {
        token = lexer->get_token(c, str);
        if(token == RIGHT_SQ_BRACKET)
            return token;
        if(token == COMMA)
        {
            check_next_token(INSTRUCTION, c, str);
            check_next_token(WHITESPACE, c, str);
            check_next_token(HASH, c, str);
            check_next_token(DEC, c, str);
        }
        else
            print_fatal("Syntax error: expected ']' or ',' but got %s\n", str.c_str());
    }
    else
        print_fatal("Syntax error: expected hash or register but got %s\n", str.c_str());
    return lexer->get_token(c, str);
}
UrRealtimeCommunication::UrRealtimeCommunication(
		std::condition_variable& msg_cond, std::string host,
		unsigned int safety_count_max) {
	robot_state_ = new RobotStateRT(msg_cond);
	bzero((char *) &serv_addr_, sizeof(serv_addr_));
	sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd_ < 0) {
		print_fatal("ERROR opening socket");
	}
	server_ = gethostbyname(host.c_str());
	if (server_ == NULL) {
		print_fatal("ERROR, no such host");
	}
	serv_addr_.sin_family = AF_INET;
	bcopy((char *) server_->h_addr, (char *)&serv_addr_.sin_addr.s_addr, server_->h_length);
	serv_addr_.sin_port = htons(30003);
	flag_ = 1;
	setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_, sizeof(int));
	fcntl(sockfd_, F_SETFL, O_NONBLOCK);
	connected_ = false;
	keepalive_ = false;
	safety_count_ = safety_count_max + 1;
	safety_count_max_ = safety_count_max;
}
UrRealtimeCommunication::UrRealtimeCommunication(
		std::condition_variable& msg_cond, const std::string& host,
		const unsigned int port /*=30003*/,
		unsigned int safety_count_max/*=12*/) {
    robot_state_ = new RobotStateRT(msg_cond);
	memset((char *) &serv_addr_, 0, sizeof(serv_addr_));
	sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd_ < 0) {
		print_fatal("ERROR opening socket");
	}
	server_ = gethostbyname(host.c_str());
	if (server_ == NULL) {
		print_fatal("ERROR, no such host");
	}
	serv_addr_.sin_family = AF_INET;
	memcpy((char *)&serv_addr_.sin_addr.s_addr, (char *)server_->h_addr, server_->h_length);
	serv_addr_.sin_port = htons(port);
	flag_ = 1;
	setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_, sizeof(int));
	SetNonBlocking(sockfd_, true);
	connected_ = false;
	keepalive_ = false;
	safety_count_ = safety_count_max + 1;
	safety_count_max_ = safety_count_max;
}
Exemple #4
0
void Lexer::initialize_set(char* filename, std::set <uint64_t>* s)
{
    FILE* file = fopen(filename, "r+");
    if(!file)
    {
        print_fatal("Couldn't open set file!\n");
    }
    print_log("Set file opened\n");

    char* line = NULL;
    size_t line_len = 0;
    while(getline(&line, &line_len, file) != -1)
    {
        line_len = strlen(line);
        if(line[line_len-1] == '\n')
            line[--line_len] = '\0';
        unsigned it = 0;
        uint64_t hash = 0;
        while(it < line_len)
        {
            hash <<= 8;
            hash += line[it++];
        }
        if(s->find(hash) != s->end())
        {
            print_fatal("TWO STRINGS WITH SAME HASH!\n");
        }
        s->insert(hash);
        line = NULL;
        line_len = 0;
    }
    print_log("Filled set\n");
    fclose(file);
}
bool UrRealtimeCommunication::start() {
	fd_set writefds;
	struct timeval timeout;

	keepalive_ = true;
	print_debug("Realtime port: Connecting...");

	connect(sockfd_, (struct sockaddr *) &serv_addr_, sizeof(serv_addr_));
	FD_ZERO(&writefds);
	FD_SET(sockfd_, &writefds);
	timeout.tv_sec = 10;
	timeout.tv_usec = 0;
	select(sockfd_ + 1, NULL, &writefds, NULL, &timeout);
	int flag_len;
	getsockopt(sockfd_, SOL_SOCKET, SO_ERROR, (char*) &flag_, &flag_len);
	if (flag_ < 0) {
		print_fatal("Error connecting to RT port 30003");
		return false;
	}
	sockaddr_in name;
	socklen_t namelen = sizeof(name);
	int err = getsockname(sockfd_, (sockaddr*) &name, &namelen);
	if (err < 0) {
		print_fatal("Could not get local IP");
		CloseSocket(sockfd_);
		return false;
	}
	char str[18];
	inet_ntop(AF_INET, &name.sin_addr, str, 18);
	local_ip_ = str;
	comThread_ = std::thread(&UrRealtimeCommunication::run, this);
	return true;
}
Exemple #6
0
Instruction* Parser::parse_start_symbol(Type token, char& c, std::string& str)
{
    Instruction* instruction = new Instruction();
    if(token == HEX || token == DEC)
    {
        check_next_token(WHITESPACE, c, str);
        check_next_token(LESS_THAN, c, str);
        check_next_token(STRING, c, str);
        check_next_token(AT, c, str);
        token = lexer->get_token(c, str);
        if(token == AT)
        {
            token = lexer->get_token(c, str);
        }
        parse_x_symbol(token, c, str);
        check_next_token(COLON, c, str);
        check_next_token(NEWLINE, c, str);
        return NULL;
    }
    else if(token == WHITESPACE)
    {
        check_next_token(HEX, c, str);
        sscanf(str.c_str(), " %x", &instruction->address);
        check_next_token(COLON, c, str);
        check_next_token(WHITESPACE, c, str);
        check_next_token(HEX, c, str);
        sscanf(str.c_str(), " %x", &instruction->opcode);
        check_next_token(WHITESPACE, c, str);
        token = parse_instruction_symbol(c, str, *instruction);
        if(token == WHITESPACE)
        {
            parse_comment_symbol(c, str);
            check_next_token(NEWLINE, c, str);
        }
        else if(token != NEWLINE)
        {
            print_fatal("Syntax error: expected newline after instruction, got: %s\n", str.c_str());       
        }
        print_log("Got instruction, addr: %x \t opcode: %x \t %s\n", instruction->address, instruction->opcode, instruction->name.c_str());
        return instruction;
    }
    else if(token != NEWLINE)
    {
        print_fatal("Syntax error: expected starting symbol, got: %s\n", str.c_str());
        return NULL;
    }
    return NULL;
    //print_log("Syntax accepted, back to starting symbol\n");
}
Exemple #7
0
void Parser::parse_x_symbol(Type token, char& c, std::string& str)
{
    if(token != STRING)
        print_fatal("Syntax error: expected string but got %s\n", str.c_str());
    token = lexer->get_token(c, str);
    if(token == MINUS || token == PLUS)
    {
        check_next_token(HEX, c, str);
        check_next_token(MORE_THAN, c, str);
    }
    else if(token != MORE_THAN)
    {
        print_fatal("Syntax error: expected '>' but got %s which is %s\n", str.c_str(), type_to_string(token));
    }
}
Exemple #8
0
void
free_object (object *obj)
{
    if (NULL == obj)
    {
        return;
    }
    else if (SCM_VOID == obj->type)
        free (obj);
    else if (SCM_ERROR == obj->type)
    {
        free (((error_object *) obj)->msg);
        free (obj);
    }
    else if (SCM_ATOM == obj->type)
    {
        free (((atom_object *) obj)->name);
        free (obj);
    }
    else if (SCM_BOOL == obj->type)
    {
        free (obj);
    }
    else if (SCM_VARIABLE == obj->type)
    {
        free (((variable_object *) obj)->name);
        free_object (((variable_object *) obj)->value);
        free (obj);
    }
    else if (SCM_NUMBER == obj->type)
    {
        free (obj);
    }
    else if (SCM_STRING == obj->type)
    {
        free (((string_object *) obj)->str);
        free (obj);
    }
    else if (SCM_PAIR == obj->type)
    {
        free_object (car (obj));
        free_object (cdr (obj));
        free (obj);
    }
    else if (SCM_FUNC == obj->type)
    {
        free (obj);
    }
    else if (SCM_LAMBDA == obj->type)
    {
        free_object (((lambda_object *) obj)->args);
        free_object (((lambda_object *) obj)->sexp);
        free (obj);
    }
    else
    {
        print_fatal (ETYPE);
        exit (1);
    }
}
bool UrCommunication::start() {
	keepalive_ = true;
	uint8_t buf[512];
	unsigned int bytes_read;
	std::string cmd;
	bzero(buf, 512);
	print_debug("Acquire firmware version: Connecting...");
	if (connect(pri_sockfd_, (struct sockaddr *) &pri_serv_addr_,
			sizeof(pri_serv_addr_)) < 0) {
		print_fatal("Error connecting to get firmware version");
		return false;
	}
	print_debug("Acquire firmware version: Got connection");
	bytes_read = read(pri_sockfd_, buf, 512);
	setsockopt(pri_sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
			sizeof(int));
	robot_state_->unpack(buf, bytes_read);
	//wait for some traffic so the UR socket doesn't die in version 3.1.
	std::this_thread::sleep_for(std::chrono::milliseconds(500));
	char tmp[64];
	sprintf(tmp, "Firmware version detected: %.7f", robot_state_->getVersion());
	print_debug(tmp);
	close(pri_sockfd_);

	print_debug(
			"Switching to secondary interface for masterboard data: Connecting...");

	fd_set writefds;
	struct timeval timeout;

	connect(sec_sockfd_, (struct sockaddr *) &sec_serv_addr_,
			sizeof(sec_serv_addr_));
	FD_ZERO(&writefds);
	FD_SET(sec_sockfd_, &writefds);
	timeout.tv_sec = 10;
	timeout.tv_usec = 0;
	select(sec_sockfd_ + 1, NULL, &writefds, NULL, &timeout);
	unsigned int flag_len;
	getsockopt(sec_sockfd_, SOL_SOCKET, SO_ERROR, &flag_, &flag_len);
	if (flag_ < 0) {
		print_fatal("Error connecting to secondary interface");
		return false;
	}
	print_debug("Secondary interface: Got connection");
	comThread_ = std::thread(&UrCommunication::run, this);
	return true;
}
Exemple #10
0
void Parser::open_file(char* filepath)
{
    input_file = fopen(filepath, "r");
    if(input_file == NULL)
    {
        print_fatal("Error while reading file: %s\n", filepath);
    }
    print_log("File \"%s\" opened\n", filepath);
}
Exemple #11
0
void Parser::check_next_token(Type token, char& c, std::string& str)
{
    Type read_token = lexer->get_token(c, str);
    if(read_token == HEX_OR_INSTRUCTION && (token == HEX || token == INSTRUCTION))
        return;
    if(read_token == DEC && token == HEX)
        return;
    if(read_token != token)
    {
        print_fatal("Syntax error: expected %s but got %s which is %s\n", type_to_string(token), str.c_str(), type_to_string(read_token));
    }
}
UrCommunication::UrCommunication(std::condition_variable& msg_cond,
		std::string host) {
	robot_state_ = new RobotState(msg_cond);
	bzero((char *) &pri_serv_addr_, sizeof(pri_serv_addr_));
	bzero((char *) &sec_serv_addr_, sizeof(sec_serv_addr_));
	pri_sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
	if (pri_sockfd_ < 0) {
		print_fatal("ERROR opening socket pri_sockfd");
	}
	sec_sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
	if (sec_sockfd_ < 0) {
		print_fatal("ERROR opening socket sec_sockfd");
	}
	server_ = gethostbyname(host.c_str());
	if (server_ == NULL) {
		print_fatal("ERROR, unknown host");
	}
	pri_serv_addr_.sin_family = AF_INET;
	sec_serv_addr_.sin_family = AF_INET;
	bcopy((char *) server_->h_addr, (char *)&pri_serv_addr_.sin_addr.s_addr, server_->h_length);
	bcopy((char *) server_->h_addr, (char *)&sec_serv_addr_.sin_addr.s_addr, server_->h_length);
	pri_serv_addr_.sin_port = htons(30001);
	sec_serv_addr_.sin_port = htons(30002);
	flag_ = 1;
	setsockopt(pri_sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_,
			sizeof(int));
	setsockopt(pri_sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
			sizeof(int));
	setsockopt(pri_sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_,
			sizeof(int));
	setsockopt(sec_sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_,
			sizeof(int));
	setsockopt(sec_sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
			sizeof(int));
	setsockopt(sec_sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_,
			sizeof(int));
	fcntl(sec_sockfd_, F_SETFL, O_NONBLOCK);
	connected_ = false;
	keepalive_ = false;
}
Exemple #13
0
void Parser::parse_comment_symbol(char& c, std::string& str)
{
    Type token = lexer->get_token(c, str);
    if(token == DOUBLE_SLASH)
    {
        check_next_token(WHITESPACE, c, str);
        check_next_token(HASH, c, str);
        token = lexer->get_token(c, str);
        if(token == MINUS)
            check_next_token(DEC, c, str);
        else if(token != DEC)
            print_fatal("Syntax error: expected DEC in comment but got %s\n", str.c_str());
    }
    else if(token == SEMICOLON)
    {
        check_next_token(WHITESPACE, c, str);
        check_next_token(STRING, c, str);
    }
    else
    {
        print_fatal("Syntax error: expected comment but got %s\n", str.c_str());
    }
}
Exemple #14
0
void
print (object *obj)
{
    if (NULL == obj)
    {
        printf ("()");
        return;
    }

    if (SCM_VOID == obj->type);
    else if (SCM_ERROR == obj->type)
        printf ("ERROR: %s", ((error_object *) obj)->msg);
    else if (SCM_ATOM == obj->type)
        printf ("%s", ((atom_object *) obj)->name);
    else if (SCM_BOOL == obj->type)
        printf ("%s", ((bool_object *) obj)->value ? "#t" : "#f");
    else if (SCM_VARIABLE == obj->type)
        print (((variable_object *) obj)->value);
    else if (SCM_NUMBER == obj->type)
        printf ("%lf", ((number_object *) obj)->num);
    else if (SCM_STRING == obj->type)
        printf ("\"%s\"", ((string_object *) obj)->str);
    else if (SCM_PAIR == obj->type)
    {
        printf ("(");
        print (car (obj));
        printf (" . ");
        print (cdr (obj));
        printf (")");
    }
    else if (SCM_FUNC == obj->type)
    {
        printf ("#<procedure builtin>");
    }
    else if (SCM_LAMBDA == obj->type)
    {
        printf ("#<procedure lambda ");
        print (((lambda_object *) obj)->args);
        printf (">");
    }
    else
    {
        print_fatal (ETYPE);
        exit (1);
    }
}
Exemple #15
0
object *
copy_object (object *obj)
{
    object *ret = NULL;
    if (NULL == obj)
        return NULL;
    else if (SCM_VOID == obj->type)
        ret = SCM_void ();
    else if (SCM_ERROR == obj->type)
        ret = SCM_error (((error_object *) obj)->error,
                         ((error_object *) obj)->msg);
    else if (SCM_ATOM == obj->type)
        ret = SCM_atom (((atom_object *) obj)->name);
    else if (SCM_BOOL == obj->type)
        ret = SCM_bool (((bool_object *) obj)->value);
    else if (SCM_VARIABLE == obj->type)
        ret = SCM_variable (((variable_object *) obj)->name,
                            copy_object (((variable_object *) obj)->value));
    else if (SCM_NUMBER == obj->type)
        ret = SCM_number_from_double (((number_object *) obj)->num);
    else if (SCM_STRING == obj->type)
        ret = SCM_string (((string_object *) obj)->str);
    else if (SCM_PAIR == obj->type)
        ret = SCM_cons (copy_object (car (obj)), copy_object (cdr (obj)));
    else if (SCM_FUNC == obj->type)
        ret = SCM_func (((func_object *) obj)->fn);
    else if (SCM_LAMBDA == obj->type)
        ret = SCM_lambda (copy_object (((lambda_object *) obj)->args),
                          copy_object (((lambda_object *) obj)->sexp));
    else
    {
        print_fatal (ETYPE);
        exit (1);
    }

    return ret;
}
void UrRealtimeCommunication::run() {
	uint8_t buf[2048];
	int bytes_read;
	memset(buf, 0, 2048);
	struct timeval timeout;
	fd_set readfds;
	FD_ZERO(&readfds);
	FD_SET(sockfd_, &readfds);
	print_debug("Realtime port: Got connection");
	connected_ = true;
	while (keepalive_) {
		while (connected_ && keepalive_) {
			timeout.tv_sec = 0; //do this each loop as selects modifies timeout
			timeout.tv_usec = 500000; // timeout of 0.5 sec
			select(sockfd_ + 1, &readfds, NULL, NULL, &timeout);
			bytes_read = recv(sockfd_, (char*) buf, 2048, 0);
			if (bytes_read > 0) {
				setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, sizeof(int));
				robot_state_->unpack(buf);
				if (safety_count_ == safety_count_max_) {
					setSpeed(0., 0., 0., 0., 0., 0.);
				}
				safety_count_ += 1;
			} else {
				connected_ = false;
				CloseSocket(sockfd_);
			}
		}
		if (keepalive_) {
			//reconnect
            ofLog()<<"Realtime port: No connection. Is controller crashed? Will try to reconnect in 10 seconds..."<<endl;
			sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
			if (sockfd_ < 0) {
				print_fatal("ERROR opening socket");
			}
			flag_ = 1;
			setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_,
					sizeof(int));
			setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, 
					sizeof(int));
	
			setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_,
					sizeof(int));
			SetNonBlocking(sockfd_, true);
			while (keepalive_ && !connected_) {
				std::this_thread::sleep_for(std::chrono::seconds(10));
				fd_set writefds;

				connect(sockfd_, (struct sockaddr *) &serv_addr_,
						sizeof(serv_addr_));
				FD_ZERO(&writefds);
				FD_SET(sockfd_, &writefds);
				select(sockfd_ + 1, NULL, &writefds, NULL, NULL);
				int flag_len;
				getsockopt(sockfd_, SOL_SOCKET, SO_ERROR, (char*)&flag_, &flag_len);
				if (flag_ < 0) {
					print_error("Error re-connecting to RT port 30003. Is controller started? Will try to reconnect in 10 seconds...");
				} else {
					connected_ = true;
					print_info("Realtime port: Reconnected");
				}
			}
		}
	}
	setSpeed(0., 0., 0., 0., 0., 0.);
	CloseSocket(sockfd_);
}
Exemple #17
0
Type Parser::parse_operands_symbol(char& c, std::string& str)
{
    Type token = lexer->get_token(c, str);
    for(int i = 0; i < 4; ++i)
    {
        if(token == REGISTER)
        {
            token = lexer->get_token(c, str);
        }
        else if(token == HASH)
        {
            token = lexer->get_token(c, str);
            if(token == MINUS)
                token = lexer->get_token(c, str);
            if(token != DEC && token != HEX)
                print_fatal("Syntax error: expected hex or dec but got %s\n", str.c_str());
            token = lexer->get_token(c, str);
        }
        else if(token == LEFT_SQ_BRACKET)
        {
            check_next_token(REGISTER, c, str);
            token = lexer->get_token(c, str);
            if(token == COMMA)
            {
                token = parse_y_symbol(c, str);
            }
            if(token != RIGHT_SQ_BRACKET)
                print_fatal("Syntax error: expected ',' or ']' but got %s\n", str.c_str());
            token = lexer->get_token(c, str);
            if(token == EXCLAMATION)
                token = lexer->get_token(c, str);
        }
        else if(token == HEX || token == DEC)
        {
            parse_z_symbol(c, str);
            token = lexer->get_token(c, str);
        }
        else if(token == STRING || token == CONDITION)
        {
            token = lexer->get_token(c, str);
        }
        else if(token == INSTRUCTION)
        {
            token = lexer->get_token(c, str);
            if(token == NEWLINE)
                return token;
            if(token != WHITESPACE)
                print_fatal("Syntax error: expected WHITESPACE but got %s\n", str.c_str());
            check_next_token(HASH, c, str);
            check_next_token(DEC, c, str);
            token = lexer->get_token(c, str);   
        }
        else
            break;
        if(token == COMMA)
        {
            token = lexer->get_token(c, str);
            if(token == WHITESPACE)
                token = lexer->get_token(c, str);
        }
    }
    return token;
}
void UrCommunication::run() {
	uint8_t buf[2048];
	int bytes_read;
	bzero(buf, 2048);
	struct timeval timeout;
	fd_set readfds;
	FD_ZERO(&readfds);
	FD_SET(sec_sockfd_, &readfds);
	connected_ = true;
	while (keepalive_) {
		while (connected_ && keepalive_) {
			timeout.tv_sec = 0; //do this each loop as selects modifies timeout
			timeout.tv_usec = 500000; // timeout of 0.5 sec
			select(sec_sockfd_ + 1, &readfds, NULL, NULL, &timeout);
			bytes_read = read(sec_sockfd_, buf, 2048); // usually only up to 1295 bytes
			if (bytes_read > 0) {
				setsockopt(sec_sockfd_, IPPROTO_TCP, TCP_QUICKACK,
						(char *) &flag_, sizeof(int));
				robot_state_->unpack(buf, bytes_read);
			} else {
				connected_ = false;
				robot_state_->setDisconnected();
				close(sec_sockfd_);
			}
		}
		if (keepalive_) {
			//reconnect
			print_warning("Secondary port: No connection. Is controller crashed? Will try to reconnect in 10 seconds...");
			sec_sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
			if (sec_sockfd_ < 0) {
				print_fatal("ERROR opening secondary socket");
			}
			flag_ = 1;
			setsockopt(sec_sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_,
					sizeof(int));
			setsockopt(sec_sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
					sizeof(int));
			setsockopt(sec_sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_,
					sizeof(int));
			fcntl(sec_sockfd_, F_SETFL, O_NONBLOCK);
			while (keepalive_ && !connected_) {
				std::this_thread::sleep_for(std::chrono::seconds(10));
				fd_set writefds;

				connect(sec_sockfd_, (struct sockaddr *) &sec_serv_addr_,
						sizeof(sec_serv_addr_));
				FD_ZERO(&writefds);
				FD_SET(sec_sockfd_, &writefds);
				select(sec_sockfd_ + 1, NULL, &writefds, NULL, NULL);
				unsigned int flag_len;
				getsockopt(sec_sockfd_, SOL_SOCKET, SO_ERROR, &flag_,
						&flag_len);
				if (flag_ < 0) {
					print_error("Error re-connecting to port 30002. Is controller started? Will try to reconnect in 10 seconds...");
				} else {
					connected_ = true;
					print_info("Secondary port: Reconnected");
				}
			}
		}
	}

	//wait for some traffic so the UR socket doesn't die in version 3.1.
	std::this_thread::sleep_for(std::chrono::milliseconds(500));
	close(sec_sockfd_);
}
Exemple #19
0
/* check FLV file validity */
int check_flv_file(const flvmeta_opts * opts) {
    flv_stream * flv_in;
    flv_header header;
    int errors, warnings;
    int result;
    char message[256];
    uint32 prev_tag_size, tag_number;
    uint32 last_timestamp, last_video_timestamp, last_audio_timestamp;
    struct stat file_stats;
    int have_audio, have_video;
    flvmeta_opts opts_loc;
    flv_info info;
    int have_desync;
    int have_on_metadata;
    file_offset_t on_metadata_offset;
    amf_data * on_metadata;
    int have_on_last_second;
    uint32 on_last_second_timestamp;

    int have_prev_audio_tag;
    flv_audio_tag prev_audio_tag;
    int have_prev_video_tag;
    flv_video_tag prev_video_tag;

    int video_frames_number, keyframes_number;

    prev_audio_tag = 0;
    prev_video_tag = 0;
    
    have_audio = have_video = 0;
    tag_number = 0;
    last_timestamp = last_video_timestamp = last_audio_timestamp = 0;
    have_desync = 0;
    have_prev_audio_tag = have_prev_video_tag = 0;
    video_frames_number = keyframes_number = 0;
    have_on_metadata = 0;
    on_metadata_offset = 0;
    on_metadata = NULL;
    have_on_last_second = 0;
    on_last_second_timestamp = 0;

    /* file stats */
    if (stat(opts->input_file, &file_stats) != 0) {
        return ERROR_OPEN_READ;
    }

    /* open file for reading */
    flv_in = flv_open(opts->input_file);
    if (flv_in == NULL) {
        return ERROR_OPEN_READ;
    }
    
    errors = warnings = 0;

    report_start(opts);

    /** check header **/

    /* check signature */
    result = flv_read_header(flv_in, &header);
    if (result == FLV_ERROR_EOF) {
        print_fatal("F11001", 0, "unexpected end of file in header");
        goto end;
    }
    else if (result == FLV_ERROR_NO_FLV) {
        print_fatal("F11002", 0, "FLV signature not found in header");
        goto end;
    }

    /* version */
    if (header.version != FLV_VERSION) {
        sprintf(message, "header version should be 1, %d found instead", header.version);
        print_error("E11003", 3, message);
    }

    /* video and audio flags */
    if (!flv_header_has_audio(header) && !flv_header_has_video(header)) {
        print_error("E11004", 4, "header signals the file does not contain video tags or audio tags");
    }
    else if (!flv_header_has_audio(header)) {
        print_info("I11005", 4, "header signals the file does not contain audio tags");
    }
    else if (!flv_header_has_video(header)) {
        print_warning("W11006", 4, "header signals the file does not contain video tags");
    }

    /* reserved flags */
    if (header.flags & 0xFA) {
        print_error("E11007", 4, "header reserved flags are not zero");
    }

    /* offset */
    if (flv_header_get_offset(header) != 9) {
        sprintf(message, "header offset should be 9, %d found instead", flv_header_get_offset(header));
        print_error("E11008", 5, message);
    }

    /** check first previous tag size **/

    result = flv_read_prev_tag_size(flv_in, &prev_tag_size);
    if (result == FLV_ERROR_EOF) {
        print_fatal("F12009", 9, "unexpected end of file in previous tag size");
        goto end;
    }
    else if (prev_tag_size != 0) {
        sprintf(message, "first previous tag size should be 0, %d found instead", prev_tag_size);
        print_error("E12010", 9, message);
    }

    /* we reached the end of file: no tags in file */
    if (flv_get_offset(flv_in) == file_stats.st_size) {
        print_fatal("F10011", 13, "file does not contain tags");
        goto end;
    }

    /** read tags **/
    while (flv_get_offset(flv_in) < file_stats.st_size) {
        flv_tag tag;
        file_offset_t offset;
        uint32 body_length, timestamp, stream_id;
        int decr_timestamp_signaled;

        result = flv_read_tag(flv_in, &tag);
        if (result != FLV_OK) {
            print_fatal("F20012", flv_get_offset(flv_in), "unexpected end of file in tag");
            goto end;
        }

        ++tag_number;

        offset = flv_get_current_tag_offset(flv_in);
        body_length = flv_tag_get_body_length(tag);
        timestamp = flv_tag_get_timestamp(tag);
        stream_id = flv_tag_get_stream_id(tag);

        /* check tag type */
        if (tag.type != FLV_TAG_TYPE_AUDIO
            && tag.type != FLV_TAG_TYPE_VIDEO
            && tag.type != FLV_TAG_TYPE_META
        ) {
            sprintf(message, "unknown tag type %hhd", tag.type);
            print_error("E30013", offset, message);
        }

        /* check consistency with global header */
        if (!have_video && tag.type == FLV_TAG_TYPE_VIDEO) {
            if (!flv_header_has_video(header)) {
                print_warning("W11014", offset, "video tag found despite header signaling the file contains no video");
            }
            have_video = 1;
        }
        if (!have_audio && tag.type == FLV_TAG_TYPE_AUDIO) {
            if (!flv_header_has_audio(header)) {
                print_warning("W11015", offset, "audio tag found despite header signaling the file contains no audio");
            }
            have_audio = 1;
        }

        /* check body length */
        if (body_length > (file_stats.st_size - flv_get_offset(flv_in))) {
            sprintf(message, "tag body length (%d bytes) exceeds file size", body_length);
            print_fatal("F20016", offset + 1, message);
            goto end;
        }
        else if (body_length > MAX_ACCEPTABLE_TAG_BODY_LENGTH) {
            sprintf(message, "tag body length (%d bytes) is abnormally large", body_length);
            print_warning("W20017", offset + 1, message);
        }
        else if (body_length == 0) {
            print_warning("W20018", offset + 1, "tag body length is zero");
        }

        /** check timestamp **/
        decr_timestamp_signaled = 0;

        /* check whether first timestamp is zero */
        if (tag_number == 1 && timestamp != 0) {
            sprintf(message, "first timestamp should be zero, %d found instead", timestamp);
            print_error("E40019", offset + 4, message);
        }

        /* check whether timestamps decrease in a given stream */
        if (tag.type == FLV_TAG_TYPE_AUDIO) {
            if (last_audio_timestamp > timestamp) {
                sprintf(message, "audio tag timestamps are decreasing from %d to %d", last_audio_timestamp, timestamp);
                print_error("E40020", offset + 4, message);
            }
            last_audio_timestamp = timestamp;
            decr_timestamp_signaled = 1;
        }
        if (tag.type == FLV_TAG_TYPE_VIDEO) {
            if (last_video_timestamp > timestamp) {
                sprintf(message, "video tag timestamps are decreasing from %d to %d", last_video_timestamp, timestamp);
                print_error("E40021", offset + 4, message);
            }
            last_video_timestamp = timestamp;
            decr_timestamp_signaled = 1;
        }

        /* check for overflow error */
        if (last_timestamp > timestamp && last_timestamp - timestamp > 0xF00000) {
            print_error("E40022", offset + 4, "extended bits not used after timestamp overflow");
        }

        /* check whether timestamps decrease globally */
        else if (!decr_timestamp_signaled && last_timestamp > timestamp && last_timestamp - timestamp >= 1000) {
            sprintf(message, "timestamps are decreasing from %d to %d", last_timestamp, timestamp);
            print_error("E40023", offset + 4, message);
        }

        last_timestamp = timestamp;

        /* check for desyncs between audio and video: one second or more is suspicious */
        if (have_video && have_audio && !have_desync && labs(last_video_timestamp - last_audio_timestamp) >= 1000) {
            sprintf(message, "audio and video streams are desynchronized by %ld ms",
                labs(last_video_timestamp - last_audio_timestamp));
            print_warning("W40024", offset + 4, message);
            have_desync = 1; /* do not repeat */
        }

        /** stream id must be zero **/
        if (stream_id != 0) {
            sprintf(message, "tag stream id must be zero, %d found instead", stream_id);
            print_error("E20025", offset + 8, message);
        }

        /* check tag body contents only if not empty */
        if (body_length > 0) {

            /** check audio info **/
            if (tag.type == FLV_TAG_TYPE_AUDIO) {
                flv_audio_tag at;
                uint8_bitmask audio_format;

                result = flv_read_audio_tag(flv_in, &at);
                if (result == FLV_ERROR_EOF) {
                    print_fatal("F20012", offset + 11, "unexpected end of file in tag");
                    goto end;
                }

                /* check whether the format varies between tags */
                if (have_prev_audio_tag && prev_audio_tag != at) {
                    print_warning("W51026", offset + 11, "audio format changed since last tag");
                }

                /* check format */
                audio_format = flv_audio_tag_sound_format(at);
                if (audio_format == 12 || audio_format == 13) {
                    sprintf(message, "unknown audio format %d", audio_format);
                    print_warning("W51027", offset + 11, message);
                }
                else if (audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_G711_A
                    || audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_G711_MU
                    || audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_RESERVED
                    || audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_MP3_8
                    || audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_DEVICE_SPECIFIC
                ) {
                    sprintf(message, "audio format %d is reserved for internal use", audio_format);
                    print_warning("W51028", offset + 11, message);
                }

                /* check consistency, see flash video spec */
                if (flv_audio_tag_sound_rate(at) != FLV_AUDIO_TAG_SOUND_RATE_44
                    && audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_AAC
                ) {
                    print_warning("W51029", offset + 11, "audio data in AAC format should have a 44KHz rate, field will be ignored");
                }

                if (flv_audio_tag_sound_type(at) == FLV_AUDIO_TAG_SOUND_TYPE_STEREO
                    && (audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_NELLYMOSER
                        || audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_NELLYMOSER_16_MONO
                        || audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_NELLYMOSER_8_MONO)
                ) {
                    print_warning("W51030", offset + 11, "audio data in Nellymoser format cannot be stereo, field will be ignored");
                }

                else if (flv_audio_tag_sound_type(at) == FLV_AUDIO_TAG_SOUND_TYPE_MONO
                    && audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_AAC
                ) {
                    print_warning("W51031", offset + 11, "audio data in AAC format should be stereo, field will be ignored");
                }

                else if (audio_format == FLV_AUDIO_TAG_SOUND_FORMAT_LINEAR_PCM) {
                    print_warning("W51032", offset + 11, "audio data in Linear PCM, platform endian format should not be used because of non-portability");
                }

                prev_audio_tag = at;
                have_prev_audio_tag = 1;
            }
            /** check video info **/
            else if (tag.type == FLV_TAG_TYPE_VIDEO) {
                flv_video_tag vt;
                uint8_bitmask video_frame_type, video_codec;

                video_frames_number++;

                result = flv_read_video_tag(flv_in, &vt);
                if (result == FLV_ERROR_EOF) {
                    print_fatal("F20012", offset + 11, "unexpected end of file in tag");
                    goto end;
                }

                /* check whether the format varies between tags */
                if (have_prev_video_tag && flv_video_tag_codec_id(prev_video_tag) != flv_video_tag_codec_id(vt)) {
                    print_warning("W60033", offset + 11, "video format changed since last tag");
                }

                /* check video frame type */
                video_frame_type = flv_video_tag_frame_type(vt);
                if (video_frame_type != FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME
                    && video_frame_type != FLV_VIDEO_TAG_FRAME_TYPE_INTERFRAME
                    && video_frame_type != FLV_VIDEO_TAG_FRAME_TYPE_DISPOSABLE_INTERFRAME
                    && video_frame_type != FLV_VIDEO_TAG_FRAME_TYPE_GENERATED_KEYFRAME
                    && video_frame_type != FLV_VIDEO_TAG_FRAME_TYPE_COMMAND_FRAME
                ) {
                    sprintf(message, "unknown video frame type %d", video_frame_type);
                    print_error("E60034", offset + 11, message);
                }

                if (video_frame_type == FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME) {
                    keyframes_number++;
                }

                /* check whether first frame is a keyframe */
                if (!have_prev_video_tag && video_frame_type != FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME) {
                    print_warning("W60035", offset + 11, "first video frame is not a keyframe, playback will suffer");
                }

                /* check video codec */
                video_codec = flv_video_tag_codec_id(vt);
                if (video_codec != FLV_VIDEO_TAG_CODEC_JPEG
                    && video_codec != FLV_VIDEO_TAG_CODEC_SORENSEN_H263
                    && video_codec != FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO
                    && video_codec != FLV_VIDEO_TAG_CODEC_ON2_VP6
                    && video_codec != FLV_VIDEO_TAG_CODEC_ON2_VP6_ALPHA
                    && video_codec != FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO_V2
                    && video_codec != FLV_VIDEO_TAG_CODEC_AVC
                ) {
                    sprintf(message, "unknown video codec id %d", video_codec);
                    print_error("E61034", offset + 11, message);
                }

                /* according to spec, JPEG codec is not currently used */
                if (video_codec == FLV_VIDEO_TAG_CODEC_JPEG) {
                    print_warning("W61035", offset + 11, "JPEG codec not currently used");
                }

                prev_video_tag = vt;
                have_prev_video_tag = 1;
            }
            /** check script data info **/
            else if (tag.type == FLV_TAG_TYPE_META) {
                amf_data * name;
                amf_data * data;

                result = flv_read_metadata(flv_in, &name, &data);

                if (result == FLV_ERROR_EOF) {
                    print_fatal("F20012", offset + 11, "unexpected end of file in tag");
                    amf_data_free(name);
                    amf_data_free(data);
                    goto end;
                }
                else if (result == FLV_ERROR_EMPTY_TAG) {
                    print_warning("W70038", offset + 11, "empty metadata tag");
                }
                else if (result == FLV_ERROR_INVALID_METADATA_NAME) {
                    print_error("E70039", offset + 11, "invalid metadata name");
                }
                else if (result == FLV_ERROR_INVALID_METADATA) {
                    print_error("E70039", offset + 11, "invalid metadata");
                }
                else if (amf_data_get_type(name) != AMF_TYPE_STRING) {
                    /* name type checking */
                    sprintf(message, "invalid metadata name type: %d, should be a string (2)", amf_data_get_type(name));
                    print_error("E70038", offset, message);
                }
                else {
                    /* empty name checking */
                    if (amf_string_get_size(name) == 0) {
                        print_warning("W70038", offset, "empty metadata name");
                    }

                    /* check whether all body size has been read */
                    if (flv_in->current_tag_body_length > 0) {
                        sprintf(message, "%d bytes not read in tag body after metadata end", body_length - flv_in->current_tag_body_length);
                        print_warning("W70040", flv_get_offset(flv_in), message);
                    }
                    else if (flv_in->current_tag_body_length < 0) {
                        sprintf(message, "%d bytes missing from tag body after metadata end", flv_in->current_tag_body_length - body_length);
                        print_warning("W70041", flv_get_offset(flv_in), message);
                    }

                    /* onLastSecond checking */
                    if (!strcmp((char*)amf_string_get_bytes(name), "onLastSecond")) {
                        if (have_on_last_second == 0) {
                            have_on_last_second = 1;
                            on_last_second_timestamp = timestamp;
                        }
                        else {
                            print_warning("W70038", offset, "duplicate onLastSecond event");
                        }
                    }

                    /* onMetaData checking */
                    if (!strcmp((char*)amf_string_get_bytes(name), "onMetaData")) {
                        if (have_on_metadata == 0) {
                            have_on_metadata = 1;
                            on_metadata_offset = offset;
                            on_metadata = amf_data_clone(data);

                            /* check onMetadata type */
                            if (amf_data_get_type(on_metadata) != AMF_TYPE_ASSOCIATIVE_ARRAY) {
                                sprintf(message, "invalid onMetaData data type: %d, should be an associative array (8)", amf_data_get_type(on_metadata));
                                print_error("E70038", offset, message);
                            }

                            /* onMetaData must be the first tag at 0 timestamp */
                            if (tag_number != 1) {
                                print_warning("W70038", offset, "onMetadata event found after the first tag");
                            }
                            if (timestamp != 0) {
                                print_warning("W70038", offset, "onMetadata event found after timestamp zero");
                            }
                        }
                        else {
                            print_warning("W70038", offset, "duplicate onMetaData event");
                        }
                    }

                    /* unknown metadata name */
                    if (strcmp((char*)amf_string_get_bytes(name), "onMetaData")
                    && strcmp((char*)amf_string_get_bytes(name), "onCuePoint")
                    && strcmp((char*)amf_string_get_bytes(name), "onLastSecond")) {
                        sprintf(message, "unknown metadata event name: '%s'", (char*)amf_string_get_bytes(name));
                        print_info("I70039", flv_get_offset(flv_in), message);
                    }
                }

                amf_data_free(name);
                amf_data_free(data);
            }
        }

        /* check body length against previous tag size */
        result = flv_read_prev_tag_size(flv_in, &prev_tag_size);
        if (result != FLV_OK) {
            print_fatal("F12036", flv_get_offset(flv_in), "unexpected end of file after tag");
            goto end;
        }

        if (prev_tag_size != FLV_TAG_SIZE + body_length) {
            sprintf(message, "previous tag size should be %d, %d found instead", FLV_TAG_SIZE + body_length, prev_tag_size);
            print_error("E12037", flv_get_offset(flv_in), message);
            goto end;
        }
    }

    /** final checks */

    /* check consistency with global header */
    if (!have_video && flv_header_has_video(header)) {
        print_warning("W11038", 4, "no video tag found despite header signaling the file contains video");
    }
    if (!have_audio && flv_header_has_audio(header)) {
        print_warning("W11039", 4, "no audio tag found despite header signaling the file contains audio");
    }

    /* check last timestamps */
    if (have_video && have_audio && labs(last_audio_timestamp - last_video_timestamp) >= 1000) {
        if (last_audio_timestamp > last_video_timestamp) {
            sprintf(message, "video stops %d ms before audio", last_audio_timestamp - last_video_timestamp);
            print_warning("W40040", file_stats.st_size, message);
        }
        else {
            sprintf(message, "audio stops %d ms before video", last_video_timestamp - last_audio_timestamp);
            print_warning("W40041", file_stats.st_size, message);
        }
    }

    /* check video keyframes */
    if (have_video && keyframes_number == 0) {
        print_warning("W60042", file_stats.st_size, "no keyframe detected, file is probably broken or incomplete");
    }
    if (have_video && keyframes_number == video_frames_number) {
        print_warning("W60043", file_stats.st_size, "only keyframes detected, probably inefficient compression scheme used");
    }

    /* only keyframes + onLastSecond bug */
    if (have_video && have_on_last_second && keyframes_number == video_frames_number) {
        print_warning("W60044", file_stats.st_size, "only keyframes detected and onLastSecond event present, file is probably not playable");
    }

    /* check onLastSecond timestamp */
    if (have_on_last_second && (last_timestamp - on_last_second_timestamp) >= 2000) {
        sprintf(message, "onLastSecond event located %d ms before the last tag", last_timestamp - on_last_second_timestamp);
        print_warning("W70050", file_stats.st_size, message);
    }

    /* check onMetaData presence */
    if (!have_on_metadata) {
        print_warning("W70044", file_stats.st_size, "onMetaData event not found, file might not be playable");
    }
    else {
        amf_node * n;
        int have_width, have_height;

        have_width = 0;
        have_height = 0;

        /* compute metadata */
        opts_loc.verbose = 0;
        opts_loc.reset_timestamps = 0;
        opts_loc.preserve_metadata = 0;
        opts_loc.all_keyframes = 0;
        opts_loc.error_handling = FLVMETA_IGNORE_ERRORS;

        flv_reset(flv_in);
        if (get_flv_info(flv_in, &info, &opts_loc) != OK) {
            print_fatal("F10042", 0, "unable to compute metadata");
            goto end;
        }

        /* more metadata checks */
        for (n = amf_associative_array_first(on_metadata); n != NULL; n = amf_associative_array_next(n)) {
            byte * name;
            amf_data * data;
            byte type;

            name = amf_string_get_bytes(amf_associative_array_get_name(n));
            data = amf_associative_array_get_data(n);
            type = amf_data_get_type(data);

            /* hasMetadata (bool): true */
            if (!strcmp((char*)name, "hasMetadata")) {
                if (type == AMF_TYPE_BOOLEAN) {
                    if (amf_boolean_get_value(data) == 0) {
                        print_warning("W70045", on_metadata_offset, "hasMetadata should be set to true");
                    }
                }
                else {
                    sprintf(message, "Invalid type for hasMetadata: expected %s, got %s",
                        get_amf_type_string(AMF_TYPE_BOOLEAN),
                        get_amf_type_string(type));
                    print_warning("W70046", on_metadata_offset, message);
                }
            }

            /* hasVideo (bool) */
            if (!strcmp((char*)name, "hasVideo")) {
                if (type == AMF_TYPE_BOOLEAN) {
                    if (amf_boolean_get_value(data) != info.have_video) {
                        sprintf(message, "hasVideo should be set to %s", info.have_video ? "true" : "false");
                        print_warning("W70045", on_metadata_offset, message);
                    }
                }
                else {
                    sprintf(message, "Invalid type for hasVideo: expected %s, got %s",
                        get_amf_type_string(AMF_TYPE_BOOLEAN),
                        get_amf_type_string(type));
                    print_warning("W70046", on_metadata_offset, message);
                }
            }

            /* hasAudio (bool) */
            if (!strcmp((char*)name, "hasAudio")) {
                if (type == AMF_TYPE_BOOLEAN) {
                    if (amf_boolean_get_value(data) != info.have_audio) {
                        sprintf(message, "hasAudio should be set to %s", info.have_audio ? "true" : "false");
                        print_warning("W70045", on_metadata_offset, message);
                    }
                }
                else {
                    sprintf(message, "Invalid type for hasAudio: expected %s, got %s",
                        get_amf_type_string(AMF_TYPE_BOOLEAN),
                        get_amf_type_string(type));
                    print_warning("W70046", on_metadata_offset, message);
                }
            }

            /* duration (number) */
            if (!strcmp((char*)name, "duration")) {
                if (type == AMF_TYPE_NUMBER) {
                    number64 duration, file_duration;
                    if (info.have_audio) {
                        duration = (info.last_timestamp - info.first_timestamp + info.audio_frame_duration) / 1000.0;
                    }
                    else {
                        duration = (info.last_timestamp - info.first_timestamp + info.video_frame_duration) / 1000.0;
                    }
                    file_duration = amf_number_get_value(data);

                    if (fabs(file_duration - duration) > 1.0) {
                        sprintf(message, "duration should be %.12g, got %.12g", duration, file_duration);
                        print_warning("W70045", on_metadata_offset, message);
                    }
                }
                else {
                    sprintf(message, "Invalid type for duration: expected %s, got %s",
                        get_amf_type_string(AMF_TYPE_NUMBER),
                        get_amf_type_string(type));
                    print_warning("W70046", on_metadata_offset, message);
                }
            }

            /* lasttimestamp: (number) */
            /* lastkeyframetimestamp: (number) */
            /* width: (number) */
            /* height: (number) */
            /* videodatarate: (number)*/
            /* framerate: (number) */
            /* audiodatarate: (number) */
            /* audiosamplerate: (number) */
            /* audiosamplesize: (number) */
            /* stereo: (boolean) */
            /* filesize: (number) */
            /* videosize: (number) */
            /* audiosize: (number) */
            /* datasize: (number) */
            /* audiocodecid: (number) */
            /* videocodecid: (number) */
            /* audiodelay: (number) */
            /* canSeekToEnd: (boolean) */
            /* hasKeyframes: (boolean) */
            /* keyframes: (object) */
        }

        /* missing width or height can cause size problem in various players */
        if (!have_width) {
            print_error("E60047", on_metadata_offset, "width information not found in metadata, problems might occur in some players");
        }
        if (!have_height) {
            print_error("E60047", on_metadata_offset, "height information not found in metadata, problems might occur in some players");
        }
    }

    /* could we compute video resolution ? */
    if (info.video_width == 0 && info.video_height == 0) {
        print_warning("W60044", file_stats.st_size, "unable to determine video resolution");
    }

end:
    report_end(opts, errors, warnings);
    
    amf_data_free(on_metadata);
    flv_close(flv_in);
    
    return (errors > 0) ? ERROR_INVALID_FLV_FILE : OK;
}