Seek WIP in player

This commit is contained in:
Lekensteyn 2012-06-17 15:44:10 +02:00 committed by Peter Wu
parent 21f6a1fe9d
commit 923d116afa
2 changed files with 102 additions and 23 deletions

View file

@ -2,7 +2,9 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <algorithm> #include <algorithm>
#include <limits.h>
#include <errno.h>
#include <cstdio>
#include <sys/time.h> #include <sys/time.h>
#include <mist/dtsc.h> #include <mist/dtsc.h>
#include "player.h" #include "player.h"
@ -16,10 +18,21 @@ namespace Player{
return t.tv_sec * 1000 + t.tv_usec/1000; return t.tv_sec * 1000 + t.tv_usec/1000;
}//getNowMS }//getNowMS
void setBlocking(int fd, bool blocking){
int flags = fcntl(fd, F_GETFL);
if (blocking){
flags &= ~O_NONBLOCK;
}else{
flags |= O_NONBLOCK;
}
fcntl(fd, F_SETFL, flags);
}
File::File(std::string filename){ File::File(std::string filename){
stream = new DTSC::Stream(5); stream = new DTSC::Stream(5);
ring = NULL;// ring will be initialized when necessary ring = NULL;// ring will be initialized when necessary
fileSrc.open(filename.c_str(), std::ifstream::in | std::ifstream::binary); fileSrc.open(filename.c_str(), std::ifstream::in | std::ifstream::binary);
setBlocking(STDIN_FILENO, false);//prevent reading from stdin from blocking
std::cout.setf(std::ios::unitbuf);//do not choke std::cout.setf(std::ios::unitbuf);//do not choke
nextPacket();// initial read always returns nothing nextPacket();// initial read always returns nothing
if (!nextPacket()){//parse metadata if (!nextPacket()){//parse metadata
@ -35,40 +48,89 @@ namespace Player{
} }
delete stream; delete stream;
} }
/// \returns Number of read bytes or -1 on EOF
int File::fillBuffer(std::string & buffer){
char buff[1024 * 10];
if (fileSrc.good()){
fileSrc.read(buff, sizeof(buff));
inBuffer.append(buff, fileSrc.gcount());
return fileSrc.gcount();
}
return -1;
}
// \returns True if there is a packet available for pull. // \returns True if there is a packet available for pull.
bool File::nextPacket(){ bool File::nextPacket(){
if (fileSrc.good()){
if (stream->parsePacket(inBuffer)){ if (stream->parsePacket(inBuffer)){
return true; return true;
} else { } else {
char buffer[1024 * 10]; fillBuffer(inBuffer);
fileSrc.read(buffer, sizeof(buffer));
inBuffer.append(buffer, fileSrc.gcount());
}
} }
return false; return false;
} }
void File::seek(int position){ void File::seek(unsigned int miliseconds){
// XXX: implement seek. DTSC::Stream * tmpStream = new DTSC::Stream(1);
int leftByte = 1, rightByte = INT_MAX;
int leftMS = 0, rightMS = INT_MAX;
/// \todo set last packet as last byte, consider metadata
while (rightMS - leftMS >= 100){
std::string buffer;
// binary search: pick the first packet on the right
unsigned int medByte = leftByte + (rightByte - leftByte) / 2;
fileSrc.clear();// clear previous IO errors
fileSrc.seekg(medByte);
do{ // find first occurrence of packet
unsigned int header_pos, read_bytes;
read_bytes = fillBuffer(buffer);
/// \todo handle EOF
header_pos = buffer.find(DTSC::Magic_Packet);
if (header_pos == std::string::npos){
// it is possible that the magic packet is partially shown, e.g. "DTP"
if (read_bytes > strlen(DTSC::Magic_Packet) - 1){
read_bytes -= strlen(DTSC::Magic_Packet) - 1;
buffer.erase(0, read_bytes);
medByte += read_bytes;
}
continue;// continue filling the buffer without parsing packet
}
}while (!tmpStream->parsePacket(buffer));
JSON::Value & medPacket = tmpStream->getPacket(0);
/// \todo What if time does not exist? Currently it just crashes.
// assumes that the time does not get over 49 days (on a 32-bit system)
unsigned int medMS = (unsigned int)medPacket["time"].asInt();
if (medMS > miliseconds){
rightByte = medByte;
rightMS = medMS;
}else if (medMS < miliseconds){
leftByte = medByte;
leftMS = medMS;
}
}
// clear the buffer and adjust file pointer
inBuffer.clear();
fileSrc.seekg(leftByte);
delete tmpStream;
}; };
std::string * File::getPacket(){ std::string & File::getPacket(){
static std::string emptystring;
if (ring->waiting){ if (ring->waiting){
return NULL; return emptystring;
}//still waiting for next buffer? }//still waiting for next buffer?
if (ring->starved){ if (ring->starved){
//if corrupt data, warn and get new DTSC::Ring //if corrupt data, warn and get new DTSC::Ring
std::cerr << "Warning: User was send corrupt video data and send to the next keyframe!" << std::endl; std::cerr << "Warning: User was send corrupt video data and send to the next keyframe!" << std::endl;
stream->dropRing(ring); stream->dropRing(ring);
ring = stream->getRing(); ring = stream->getRing();
return NULL; return emptystring;
} }
//switch to next buffer //switch to next buffer
if (ring->b < 0){ if (ring->b < 0){
ring->waiting = true; ring->waiting = true;
return NULL; return emptystring;
}//no next buffer? go in waiting mode. }//no next buffer? go in waiting mode.
// get current packet // get current packet
std::string * packet = &stream->outPacket(ring->b); std::string & packet = stream->outPacket(ring->b);
// make next request take a different packet // make next request take a different packet
ring->b--; ring->b--;
return packet; return packet;
@ -76,7 +138,24 @@ namespace Player{
/// Reads a command from stdin. Returns true if a command was read. /// Reads a command from stdin. Returns true if a command was read.
bool File::readCommand() { bool File::readCommand() {
// XXX: implement seek. char line[512];
if (fgets(line, sizeof(line), stdin) == NULL){
return false;
}
{
int position = INT_MAX;// special value that says "invalid"
if (!strncmp("seek ", line, sizeof("seek ") - 1)){
position = atoi(line + sizeof("seek ") - 1);
}
if (!strncmp("relseek ", line, sizeof("relseek " - 1))){
/// \todo implement relseek in a smart way
//position = atoi(line + sizeof("relseek "));
}
if (position != INT_MAX){
File::seek(position);
return true;
}
}
return false; return false;
} }
@ -89,17 +168,16 @@ namespace Player{
now = getNowMS(); now = getNowMS();
if (now - timeDiff >= lastTime || lastTime - (now - timeDiff) > 5000) { if (now - timeDiff >= lastTime || lastTime - (now - timeDiff) > 5000) {
if (nextPacket()) { if (nextPacket()) {
std::string * packet;
if (!ring){ring = stream->getRing();}//get ring after reading first non-metadata if (!ring){ring = stream->getRing();}//get ring after reading first non-metadata
packet = getPacket(); std::string & packet = getPacket();
if (!packet){ if (packet.empty()){
continue; continue;
} }
lastTime = stream->getTime(); lastTime = stream->getTime();
if (std::abs(now - timeDiff - lastTime) > 5000) { if (std::abs(now - timeDiff - lastTime) > 5000) {
timeDiff = now - lastTime; timeDiff = now - lastTime;
} }
std::cout.write(packet->c_str(), packet->length()); std::cout.write(packet.c_str(), packet.length());
} }
} else { } else {
usleep(std::min(999LL, lastTime - (now - timeDiff)) * 1000); usleep(std::min(999LL, lastTime - (now - timeDiff)) * 1000);

View file

@ -15,11 +15,12 @@ namespace Player{
bool nextPacket(); ///<Pulls the next packet into the queue. bool nextPacket(); ///<Pulls the next packet into the queue.
bool getPacketFromInput(); ///<Attempts to retrieve a packet from input. bool getPacketFromInput(); ///<Attempts to retrieve a packet from input.
bool readCommand(); bool readCommand();
int fillBuffer(std::string & buffer);
public: public:
File(std::string filename); ///<Attempts to open a DTSC file File(std::string filename); ///<Attempts to open a DTSC file
void Play(); void Play();
~File(); ~File();
void seek(int position); void seek(unsigned int miliseconds);
std::string * getPacket(); std::string & getPacket();
}; };
}; };