mirror of
				https://projects.vdr-developer.org/git/vdr-plugin-streamdev.git
				synced 2023-10-10 17:16:51 +00:00 
			
		
		
		
	Select start position for replaying a recording by parameter pos=
Based on offset_5.diff from hivdr@vdrportal with the following modifications: - indenting - replaced isyslog with Dprintf - left out HTTP header "Server:" for the moment
This commit is contained in:
		@@ -217,3 +217,6 @@ macmenot
 | 
			
		||||
 | 
			
		||||
thomasjfox
 | 
			
		||||
  for fixing cSuspendCtl preventing idle shutdown
 | 
			
		||||
 | 
			
		||||
hivdr
 | 
			
		||||
  for adding the pos= parameter for replaying recordings from a certain position
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								HISTORY
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								HISTORY
									
									
									
									
									
								
							@@ -1,6 +1,9 @@
 | 
			
		||||
VDR Plugin 'streamdev' Revision History
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 
 | 
			
		||||
- Select start position for replaying a recording by parameter pos=. Supported
 | 
			
		||||
  values are resume, mark.#, time.#, frame.# or a plain # representing a
 | 
			
		||||
  percentage if < 100 or a byte position otherwise (thanks to hivdr)
 | 
			
		||||
- Start cSuspendCtl hidden or it will prevent idle shutdown (thanks to
 | 
			
		||||
  thomasjfox)
 | 
			
		||||
- Fixed recordings menu inode numbers: ino_t is a long long on some systems
 | 
			
		||||
 
 | 
			
		||||
@@ -199,19 +199,51 @@ bool cConnectionHTTP::ProcessRequest(void)
 | 
			
		||||
		}
 | 
			
		||||
		else if (m_Recording != NULL) {
 | 
			
		||||
			Dprintf("GET recording\n");
 | 
			
		||||
			cStreamdevRecStreamer* recStreamer = new cStreamdevRecStreamer(m_Recording, this);
 | 
			
		||||
			cStreamdevRecStreamer* recStreamer = new cStreamdevRecStreamer(m_Recording, this, m_ReplayPos);
 | 
			
		||||
			m_Streamer = recStreamer;
 | 
			
		||||
			int64_t from, to;
 | 
			
		||||
			uint64_t total = recStreamer->GetLength();
 | 
			
		||||
			if (ParseRange(from, to)) {
 | 
			
		||||
				Dprintf("parsed from-to: %lld - %lld\n", (long long)from, (long long)to);
 | 
			
		||||
				int64_t length = recStreamer->SetRange(from, to);
 | 
			
		||||
				int64_t fromByPos = recStreamer->GetFromByPos();
 | 
			
		||||
				if (fromByPos > 0) {
 | 
			
		||||
					if (from == 0) {
 | 
			
		||||
						from = fromByPos;
 | 
			
		||||
						to = total - 1;
 | 
			
		||||
						Dprintf("from byte: %lld\n", (long long)from);
 | 
			
		||||
						}
 | 
			
		||||
					else if (m_ReplayPos.find("full_") != 0) {
 | 
			
		||||
						from += fromByPos;
 | 
			
		||||
						to += fromByPos;
 | 
			
		||||
					}
 | 
			
		||||
					Dprintf("part of recording: %lld-%lld/%lld\n", (long long)from, (long long)to, (long long)total);
 | 
			
		||||
					length = recStreamer->SetRange(from, to);
 | 
			
		||||
					Dprintf("part of recording: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length);
 | 
			
		||||
				}
 | 
			
		||||
				if (m_ReplayPos.find("full_") != 0 && fromByPos > 0) {
 | 
			
		||||
					from -= fromByPos;
 | 
			
		||||
					to -= fromByPos;
 | 
			
		||||
					total -= fromByPos;
 | 
			
		||||
				}
 | 
			
		||||
				Dprintf("range response: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length);
 | 
			
		||||
				if (length < 0L)
 | 
			
		||||
					return HttpResponse(416, true, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes */%llu", (unsigned long long) total);
 | 
			
		||||
				else
 | 
			
		||||
					return HttpResponse(206, false, "video/mpeg", "Accept-Ranges: bytes\r\nContent-Range: bytes %lld-%lld/%llu\r\nContent-Length: %lld", (long long) from, (long long) to, (unsigned long long) total, (long long) length);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			else {
 | 
			
		||||
				int64_t fromByPos = recStreamer->GetFromByPos();
 | 
			
		||||
				if (fromByPos > 0) {
 | 
			
		||||
					from = fromByPos;
 | 
			
		||||
					to = total - 1;
 | 
			
		||||
					Dprintf("from byte: %lld\n", (long long)from);
 | 
			
		||||
					int64_t length = recStreamer->SetRange(from, to);
 | 
			
		||||
					Dprintf("part of recording: %lld-%lld/%lld, len %lld\n", (long long)from, (long long)to, (long long)total, (long long)length);
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
				return HttpResponse(200, false, "video/mpeg", "Accept-Ranges: bytes");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			return HttpResponse(404, true);
 | 
			
		||||
@@ -240,7 +272,7 @@ bool cConnectionHTTP::ProcessRequest(void)
 | 
			
		||||
		}
 | 
			
		||||
		else if (m_Recording != NULL) {
 | 
			
		||||
			Dprintf("HEAD recording\n");
 | 
			
		||||
			cStreamdevRecStreamer *recStreamer = new cStreamdevRecStreamer(m_Recording, this);
 | 
			
		||||
			cStreamdevRecStreamer *recStreamer = new cStreamdevRecStreamer(m_Recording, this, m_ReplayPos);
 | 
			
		||||
			m_Streamer = recStreamer;
 | 
			
		||||
			int64_t from, to;
 | 
			
		||||
			uint64_t total = recStreamer->GetLength();
 | 
			
		||||
@@ -330,6 +362,7 @@ bool cConnectionHTTP::ParseRange(int64_t &From, int64_t &To) const
 | 
			
		||||
	From = To = 0L;
 | 
			
		||||
	tStrStrMap::const_iterator it = Headers().find(RANGE);
 | 
			
		||||
	if (it != Headers().end()) {
 | 
			
		||||
		Dprintf("%s: %s\n", it->first.c_str(), it->second.c_str());
 | 
			
		||||
		size_t b = it->second.find("bytes=");
 | 
			
		||||
		if (b != std::string::npos) {
 | 
			
		||||
			char* e = NULL;
 | 
			
		||||
@@ -521,6 +554,14 @@ bool cConnectionHTTP::ProcessURI(const std::string& PathInfo)
 | 
			
		||||
		return true;
 | 
			
		||||
	} else if ((m_Recording = RecordingFromString(filespec.c_str(), fileext.c_str())) != NULL) {
 | 
			
		||||
		Dprintf("Recording %s found\n", m_Recording->Name());
 | 
			
		||||
		tStrStrMap::iterator it = m_Params.begin();
 | 
			
		||||
		while (it != m_Params.end()) {
 | 
			
		||||
			if (it->first == "pos") {
 | 
			
		||||
				m_ReplayPos = it->second;
 | 
			
		||||
				Dprintf("pos: %s\n", m_ReplayPos.c_str());
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	} else if ((m_Channel = ChannelFromString(filespec.c_str(), &m_Apid[0], &m_Dpid[0])) != NULL) {
 | 
			
		||||
		Dprintf("Channel found. Apid/Dpid is %d/%d\n", m_Apid[0], m_Dpid[0]);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ private:
 | 
			
		||||
	int                               m_Dpid[2];
 | 
			
		||||
	// job: replay
 | 
			
		||||
	cRecording                       *m_Recording;
 | 
			
		||||
	std::string                       m_ReplayPos;
 | 
			
		||||
	// job: listing
 | 
			
		||||
	cMenuList                        *m_MenuList;
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +70,7 @@ public:
 | 
			
		||||
 | 
			
		||||
	virtual bool Abort(void) const;
 | 
			
		||||
	virtual void Flushed(void);
 | 
			
		||||
	inline std::string GetReplayPos() { return m_ReplayPos; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
inline bool cConnectionHTTP::Abort(void) const
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,10 @@
 | 
			
		||||
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "recplayer.h"
 | 
			
		||||
 | 
			
		||||
// for TSPLAY patch detection
 | 
			
		||||
@@ -210,6 +214,47 @@ cRecording* RecPlayer::getCurrentRecording()
 | 
			
		||||
  return recording;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RecPlayer::frameFromResume()
 | 
			
		||||
{
 | 
			
		||||
  int frame = 0;
 | 
			
		||||
  char fileName[2048];
 | 
			
		||||
  snprintf(fileName, 2047, "%s/resume", recording->FileName());
 | 
			
		||||
  std::ifstream ifs;
 | 
			
		||||
  ifs.open(fileName);
 | 
			
		||||
  if (!ifs.is_open()) return 0;
 | 
			
		||||
  std::string sFrame;
 | 
			
		||||
  getline(ifs, sFrame);
 | 
			
		||||
  ifs.close();
 | 
			
		||||
  sFrame=sFrame.substr(2);
 | 
			
		||||
  frame=atoi(sFrame.c_str());
 | 
			
		||||
  return frame;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RecPlayer::frameFromMark(int index)
 | 
			
		||||
{
 | 
			
		||||
  char fileName[2048];
 | 
			
		||||
  snprintf(fileName, 2047, "%s/marks", recording->FileName());
 | 
			
		||||
  std::ifstream ifs;
 | 
			
		||||
  ifs.open(fileName);
 | 
			
		||||
  if (!ifs.is_open()) return 0;
 | 
			
		||||
  std::string sTime;
 | 
			
		||||
  for (int i=0; i<=index; i++) {
 | 
			
		||||
	getline(ifs, sTime);
 | 
			
		||||
	if (ifs.eof()) break;
 | 
			
		||||
  }
 | 
			
		||||
  ifs.close();
 | 
			
		||||
  int seconds = 0, minutes = 0, hours = 0;
 | 
			
		||||
  hours = atoi(sTime.substr(0, sTime.find(":")).c_str());
 | 
			
		||||
  minutes = atoi(sTime.substr(sTime.find(":") + 1, 2).c_str());
 | 
			
		||||
  seconds = atoi(sTime.substr(sTime.rfind(":") + 1, 2).c_str());
 | 
			
		||||
  return frameFromSeconds(seconds + minutes * 60 + hours * 3600);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int RecPlayer::frameFromSeconds(int seconds)
 | 
			
		||||
{
 | 
			
		||||
	return 25 * seconds; // 25fps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
 | 
			
		||||
{
 | 
			
		||||
  if (!indexFile) return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,9 @@ class RecPlayer
 | 
			
		||||
    cRecording* getCurrentRecording();
 | 
			
		||||
    void scan();
 | 
			
		||||
    uint64_t positionFromFrameNumber(uint32_t frameNumber);
 | 
			
		||||
    int frameFromResume();
 | 
			
		||||
    int frameFromMark(int index);
 | 
			
		||||
    int frameFromSeconds(int seconds);
 | 
			
		||||
    uint32_t frameNumberFromPosition(uint64_t position);
 | 
			
		||||
    bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,13 +12,14 @@ using namespace Streamdev;
 | 
			
		||||
 | 
			
		||||
// --- cStreamdevRecStreamer -------------------------------------------------
 | 
			
		||||
 | 
			
		||||
cStreamdevRecStreamer::cStreamdevRecStreamer(cRecording *Rec, const cServerConnection *Connection):
 | 
			
		||||
cStreamdevRecStreamer::cStreamdevRecStreamer(cRecording *Rec, const cServerConnection *Connection, std::string pos):
 | 
			
		||||
		cStreamdevStreamer("streamdev-recstreaming", Connection),
 | 
			
		||||
		m_RecPlayer(Rec),
 | 
			
		||||
		m_From(0L)
 | 
			
		||||
{
 | 
			
		||||
	Dprintf("New rec streamer\n");
 | 
			
		||||
	m_To = (int64_t) m_RecPlayer.getLengthBytes() - 1;
 | 
			
		||||
	m_Pos = pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cStreamdevRecStreamer::~cStreamdevRecStreamer() 
 | 
			
		||||
@@ -52,6 +53,69 @@ int64_t cStreamdevRecStreamer::SetRange(int64_t &From, int64_t &To)
 | 
			
		||||
	return m_To - m_From + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t cStreamdevRecStreamer::getIFrameBeforeFrame(int32_t frame)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t iframe, len;
 | 
			
		||||
	uint64_t pos;
 | 
			
		||||
	m_RecPlayer.getNextIFrame(frame + 1, 0, &pos, &iframe, &len);
 | 
			
		||||
	Dprintf("pos: frame %i -> start at iFrame %i\n", frame, iframe);
 | 
			
		||||
	return iframe;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t cStreamdevRecStreamer::GetFromByPos()
 | 
			
		||||
{
 | 
			
		||||
	if (m_Pos.empty()) return 0;
 | 
			
		||||
	
 | 
			
		||||
	std::string pos = m_Pos;
 | 
			
		||||
	
 | 
			
		||||
	// cut prefix (if any)
 | 
			
		||||
	if (pos.find('_') != std::string::npos) {
 | 
			
		||||
		pos = pos.substr(pos.find('_') + 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// resume file
 | 
			
		||||
	if (pos == "resume") {
 | 
			
		||||
		int frame = getIFrameBeforeFrame(m_RecPlayer.frameFromResume());
 | 
			
		||||
		Dprintf("pos: frame from resume: %i\n", frame);
 | 
			
		||||
		return m_RecPlayer.positionFromFrameNumber(frame);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// mark
 | 
			
		||||
	if (pos.find("mark.") == 0) {
 | 
			
		||||
		int index = atoi(pos.substr(5).c_str());
 | 
			
		||||
		int frame = getIFrameBeforeFrame(m_RecPlayer.frameFromMark(index));
 | 
			
		||||
		Dprintf("pos: mark %i - frame %i\n", index, frame);
 | 
			
		||||
		return m_RecPlayer.positionFromFrameNumber(frame);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// time
 | 
			
		||||
	if (pos.find("time.") == 0) {
 | 
			
		||||
		int seconds = atoi(pos.substr(5).c_str());
 | 
			
		||||
		int frame = getIFrameBeforeFrame(m_RecPlayer.frameFromSeconds(seconds));
 | 
			
		||||
		Dprintf("pos: %i seconds - frame %i\n", seconds, frame);
 | 
			
		||||
		return m_RecPlayer.positionFromFrameNumber(frame);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// frame number
 | 
			
		||||
	if (pos.find("frame.") == 0) {
 | 
			
		||||
		int frame = getIFrameBeforeFrame(atoi(pos.substr(6).c_str()));
 | 
			
		||||
		Dprintf("pos: frame %i\n", frame);
 | 
			
		||||
		return m_RecPlayer.positionFromFrameNumber(frame);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// default: byte index or percent
 | 
			
		||||
	// as "%" is the url escape character, interpret <100 as percent
 | 
			
		||||
	// if (pos.find("%") != std::string::npos) {
 | 
			
		||||
	// int percent = atoi(pos.substr(0, pos.find("%")).c_str());
 | 
			
		||||
	int64_t number = atol(pos.c_str());
 | 
			
		||||
	if (number < 100) {
 | 
			
		||||
		Dprintf("pos: %lld percent\n", (long long)number);
 | 
			
		||||
		int64_t offset = m_RecPlayer.getLengthBytes() * number / 100;
 | 
			
		||||
		return offset;
 | 
			
		||||
	}
 | 
			
		||||
	return number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uchar* cStreamdevRecStreamer::GetFromReceiver(int &Count)
 | 
			
		||||
{
 | 
			
		||||
	if (m_From <= m_To) {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ private:
 | 
			
		||||
	RecPlayer               m_RecPlayer;
 | 
			
		||||
	int64_t                 m_From;
 | 
			
		||||
	int64_t                 m_To;
 | 
			
		||||
	std::string		m_Pos;
 | 
			
		||||
	uchar                   m_Buffer[RECBUFSIZE];
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
@@ -25,7 +26,9 @@ public:
 | 
			
		||||
	inline uint64_t GetLength() { return m_RecPlayer.getLengthBytes(); }
 | 
			
		||||
	int64_t SetRange(int64_t &From, int64_t &To);
 | 
			
		||||
	virtual cString ToText() const;
 | 
			
		||||
	cStreamdevRecStreamer(cRecording *Recording, const cServerConnection *Connection);
 | 
			
		||||
	int64_t GetFromByPos();
 | 
			
		||||
	int32_t getIFrameBeforeFrame(int32_t frame);
 | 
			
		||||
	cStreamdevRecStreamer(cRecording *Recording, const cServerConnection *Connection, std::string pos);
 | 
			
		||||
	virtual ~cStreamdevRecStreamer();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user