mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
Merge remote-tracking branch 'martiis-libstb-hal/master'
Conflicts: libspark/playback_libeplayer3.h
This commit is contained in:
@@ -44,6 +44,7 @@ class Track;
|
|||||||
class Input
|
class Input
|
||||||
{
|
{
|
||||||
friend class Player;
|
friend class Player;
|
||||||
|
friend class WriterPCM; // needs calcPts()
|
||||||
friend int interrupt_cb(void *arg);
|
friend int interrupt_cb(void *arg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -46,10 +46,18 @@ struct Track
|
|||||||
int pid;
|
int pid;
|
||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
bool inactive;
|
bool inactive;
|
||||||
|
bool hidden; // not part of currently selected program
|
||||||
bool is_static;
|
bool is_static;
|
||||||
int ac3flags;
|
int ac3flags;
|
||||||
int type, mag, page; // for teletext
|
int type, mag, page; // for teletext
|
||||||
Track() : pid(-1), stream(NULL), inactive(0), is_static(0), ac3flags(0) {}
|
Track() : pid(-1), stream(NULL), inactive(false), hidden(false), is_static(false), ac3flags(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Program
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
std::string title;
|
||||||
|
std::vector<AVStream *> streams;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Manager
|
class Manager
|
||||||
@@ -60,6 +68,7 @@ class Manager
|
|||||||
Player *player;
|
Player *player;
|
||||||
OpenThreads::Mutex mutex;
|
OpenThreads::Mutex mutex;
|
||||||
std::map<int,Track*> videoTracks, audioTracks, subtitleTracks, teletextTracks;
|
std::map<int,Track*> videoTracks, audioTracks, subtitleTracks, teletextTracks;
|
||||||
|
std::map<int,Program> Programs;
|
||||||
void addTrack(std::map<int,Track*> &tracks, Track &track);
|
void addTrack(std::map<int,Track*> &tracks, Track &track);
|
||||||
Track *getTrack(std::map<int,Track*> &tracks, int pid);
|
Track *getTrack(std::map<int,Track*> &tracks, int pid);
|
||||||
std::vector<Track> getTracks(std::map<int,Track*> &tracks);
|
std::vector<Track> getTracks(std::map<int,Track*> &tracks);
|
||||||
@@ -68,11 +77,14 @@ class Manager
|
|||||||
void addAudioTrack(Track &track);
|
void addAudioTrack(Track &track);
|
||||||
void addSubtitleTrack(Track &track);
|
void addSubtitleTrack(Track &track);
|
||||||
void addTeletextTrack(Track &track);
|
void addTeletextTrack(Track &track);
|
||||||
|
void addProgram(Program &program);
|
||||||
|
|
||||||
std::vector<Track> getVideoTracks();
|
std::vector<Track> getVideoTracks();
|
||||||
std::vector<Track> getAudioTracks();
|
std::vector<Track> getAudioTracks();
|
||||||
std::vector<Track> getSubtitleTracks();
|
std::vector<Track> getSubtitleTracks();
|
||||||
std::vector<Track> getTeletextTracks();
|
std::vector<Track> getTeletextTracks();
|
||||||
|
std::vector<Program> getPrograms();
|
||||||
|
bool selectProgram(const int id);
|
||||||
|
|
||||||
Track *getVideoTrack(int pid);
|
Track *getVideoTrack(int pid);
|
||||||
Track *getAudioTrack(int pid);
|
Track *getAudioTrack(int pid);
|
||||||
|
@@ -56,6 +56,7 @@ class Player {
|
|||||||
friend class Output;
|
friend class Output;
|
||||||
friend class Manager;
|
friend class Manager;
|
||||||
friend class cPlayback;
|
friend class cPlayback;
|
||||||
|
friend class WriterPCM;
|
||||||
friend int interrupt_cb(void *arg);
|
friend int interrupt_cb(void *arg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -117,6 +118,10 @@ class Player {
|
|||||||
AVFormatContext *GetAVFormatContext() { return input.GetAVFormatContext(); }
|
AVFormatContext *GetAVFormatContext() { return input.GetAVFormatContext(); }
|
||||||
void ReleaseAVFormatContext() { input.ReleaseAVFormatContext(); }
|
void ReleaseAVFormatContext() { input.ReleaseAVFormatContext(); }
|
||||||
|
|
||||||
|
bool GetPrograms(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||||
|
bool SelectProgram(int key);
|
||||||
|
bool SelectProgram(std::string &key);
|
||||||
|
|
||||||
Player();
|
Player();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@@ -37,10 +37,13 @@ extern "C" {
|
|||||||
|
|
||||||
#define AV_CODEC_ID_INJECTPCM AV_CODEC_ID_PCM_S16LE
|
#define AV_CODEC_ID_INJECTPCM AV_CODEC_ID_PCM_S16LE
|
||||||
|
|
||||||
|
class Player;
|
||||||
|
|
||||||
class Writer
|
class Writer
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
int fd;
|
int fd;
|
||||||
|
Player *player;
|
||||||
public:
|
public:
|
||||||
static void Register(Writer *w, enum AVCodecID id, video_encoding_t encoding);
|
static void Register(Writer *w, enum AVCodecID id, video_encoding_t encoding);
|
||||||
static void Register(Writer *w, enum AVCodecID id, audio_encoding_t encoding);
|
static void Register(Writer *w, enum AVCodecID id, audio_encoding_t encoding);
|
||||||
@@ -48,7 +51,7 @@ class Writer
|
|||||||
static audio_encoding_t GetAudioEncoding(enum AVCodecID id);
|
static audio_encoding_t GetAudioEncoding(enum AVCodecID id);
|
||||||
static Writer *GetWriter(enum AVCodecID id, enum AVMediaType codec_type);
|
static Writer *GetWriter(enum AVCodecID id, enum AVMediaType codec_type);
|
||||||
|
|
||||||
virtual void Init(int _fd, AVStream * /*stream*/ ) { fd = _fd; }
|
virtual void Init(int _fd, AVStream * /*stream*/, Player *_player ) { fd = _fd; player = _player; }
|
||||||
virtual bool Write(AVPacket *packet, int64_t pts);
|
virtual bool Write(AVPacket *packet, int64_t pts);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ENABLE_AVLOG 0
|
#define ENABLE_LOGGING 1
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -80,6 +80,32 @@ extern void dvbsub_ass_clear(void);
|
|||||||
// from neutrino-mp/lib/lib/libtuxtxt/tuxtxt_common.h
|
// from neutrino-mp/lib/lib/libtuxtxt/tuxtxt_common.h
|
||||||
extern void teletext_write(int pid, uint8_t *data, int size);
|
extern void teletext_write(int pid, uint8_t *data, int size);
|
||||||
|
|
||||||
|
static std::string lastlog_message;
|
||||||
|
static unsigned int lastlog_repeats;
|
||||||
|
|
||||||
|
static void log_callback(void *ptr __attribute__ ((unused)), int lvl __attribute__ ((unused)), const char *format, va_list ap)
|
||||||
|
{
|
||||||
|
char m[1024];
|
||||||
|
if (sizeof(m) - 1 > (unsigned int) vsnprintf(m, sizeof(m), format, ap)) {
|
||||||
|
if (lastlog_message.compare(m) || lastlog_repeats > 999) {
|
||||||
|
if (lastlog_repeats)
|
||||||
|
fprintf(stderr, "last message repeated %u times\n", lastlog_repeats);
|
||||||
|
lastlog_message = m;
|
||||||
|
lastlog_repeats = 0;
|
||||||
|
fprintf(stderr, "%s", m);
|
||||||
|
} else
|
||||||
|
lastlog_repeats++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void logprintf(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
log_callback(NULL, 0, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
bool Input::Play()
|
bool Input::Play()
|
||||||
{
|
{
|
||||||
hasPlayThreadStarted = 1;
|
hasPlayThreadStarted = 1;
|
||||||
@@ -87,11 +113,9 @@ bool Input::Play()
|
|||||||
int64_t showtime = 0;
|
int64_t showtime = 0;
|
||||||
bool restart_audio_resampling = false;
|
bool restart_audio_resampling = false;
|
||||||
bool bof = false;
|
bool bof = false;
|
||||||
int warnAudioWrite = 0;
|
|
||||||
int warnVideoWrite = 0;
|
|
||||||
|
|
||||||
// HACK: Drop all video frames until the first audio frame was seen to keep player2 from stuttering.
|
// HACK: Dropping all video frames until the first audio frame was seen will keep player2 from stuttering.
|
||||||
// This seems to be necessary for network streaming only ...
|
// Oddly, this seems to be necessary for network streaming only ...
|
||||||
bool audioSeen = !audioTrack || !player->isHttp;
|
bool audioSeen = !audioTrack || !player->isHttp;
|
||||||
|
|
||||||
while (player->isPlaying && !player->abortRequested) {
|
while (player->isPlaying && !player->abortRequested) {
|
||||||
@@ -184,14 +208,8 @@ bool Input::Play()
|
|||||||
|
|
||||||
if (_videoTrack && (_videoTrack->stream == stream)) {
|
if (_videoTrack && (_videoTrack->stream == stream)) {
|
||||||
int64_t pts = calcPts(stream, packet.pts);
|
int64_t pts = calcPts(stream, packet.pts);
|
||||||
if (audioSeen && !player->output.Write(stream, &packet, pts)) {
|
if (audioSeen && !player->output.Write(stream, &packet, pts))
|
||||||
if (warnVideoWrite)
|
logprintf("writing data to %s device failed\n", "video");
|
||||||
warnVideoWrite--;
|
|
||||||
else {
|
|
||||||
fprintf(stderr, "writing data to %s device failed\n", "video");
|
|
||||||
warnVideoWrite = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (_audioTrack && (_audioTrack->stream == stream)) {
|
} else if (_audioTrack && (_audioTrack->stream == stream)) {
|
||||||
if (restart_audio_resampling) {
|
if (restart_audio_resampling) {
|
||||||
restart_audio_resampling = false;
|
restart_audio_resampling = false;
|
||||||
@@ -199,14 +217,8 @@ bool Input::Play()
|
|||||||
}
|
}
|
||||||
if (!player->isBackWard) {
|
if (!player->isBackWard) {
|
||||||
int64_t pts = calcPts(stream, packet.pts);
|
int64_t pts = calcPts(stream, packet.pts);
|
||||||
if (!player->output.Write(stream, &packet, _videoTrack ? pts : 0)) {
|
if (!player->output.Write(stream, &packet, _videoTrack ? pts : 0))
|
||||||
if (warnAudioWrite)
|
logprintf("writing data to %s device failed\n", "audio");
|
||||||
warnAudioWrite--;
|
|
||||||
else {
|
|
||||||
fprintf(stderr, "writing data to %s device failed\n", "audio");
|
|
||||||
warnAudioWrite = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
audioSeen = true;
|
audioSeen = true;
|
||||||
} else if (_subtitleTrack && (_subtitleTrack->stream == stream)) {
|
} else if (_subtitleTrack && (_subtitleTrack->stream == stream)) {
|
||||||
@@ -241,7 +253,7 @@ bool Input::Play()
|
|||||||
}
|
}
|
||||||
|
|
||||||
av_free_packet(&packet);
|
av_free_packet(&packet);
|
||||||
} /* while */
|
} /* while */
|
||||||
|
|
||||||
if (player->abortRequested)
|
if (player->abortRequested)
|
||||||
player->output.Clear();
|
player->output.Clear();
|
||||||
@@ -264,25 +276,26 @@ bool Input::Play()
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_AVLOG
|
static int lock_callback(void **mutex, enum AVLockOp op)
|
||||||
static std::string lastlog_message;
|
|
||||||
static unsigned int lastlog_repeats;
|
|
||||||
|
|
||||||
static void log_callback(void *ptr __attribute__ ((unused)), int lvl __attribute__ ((unused)), const char *format, va_list ap)
|
|
||||||
{
|
{
|
||||||
char m[1024];
|
switch (op) {
|
||||||
if (sizeof(m) - 1 > (unsigned int) vsnprintf(m, sizeof(m), format, ap)) {
|
case AV_LOCK_CREATE:
|
||||||
if (lastlog_message.compare(m) || lastlog_repeats > 999) {
|
*mutex = (void *) new OpenThreads::Mutex;
|
||||||
if (lastlog_repeats)
|
return !*mutex;
|
||||||
fprintf(stderr, "last message repeated %u times\n", lastlog_repeats);
|
case AV_LOCK_DESTROY:
|
||||||
lastlog_message = m;
|
delete static_cast<OpenThreads::Mutex *>(*mutex);
|
||||||
lastlog_repeats = 0;
|
*mutex = NULL;
|
||||||
fprintf(stderr, "%s", m);
|
return 0;
|
||||||
} else
|
case AV_LOCK_OBTAIN:
|
||||||
lastlog_repeats++;
|
static_cast<OpenThreads::Mutex *>(*mutex)->lock();
|
||||||
|
return 0;
|
||||||
|
case AV_LOCK_RELEASE:
|
||||||
|
static_cast<OpenThreads::Mutex *>(*mutex)->unlock();
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool Input::ReadSubtitle(const char *filename, const char *format, int pid)
|
bool Input::ReadSubtitle(const char *filename, const char *format, int pid)
|
||||||
{
|
{
|
||||||
@@ -360,7 +373,8 @@ bool Input::ReadSubtitles(const char *filename) {
|
|||||||
bool Input::Init(const char *filename)
|
bool Input::Init(const char *filename)
|
||||||
{
|
{
|
||||||
abortPlayback = false;
|
abortPlayback = false;
|
||||||
#if ENABLE_AVLOG
|
av_lockmgr_register(lock_callback);
|
||||||
|
#if ENABLE_LOGGING
|
||||||
av_log_set_callback(log_callback);
|
av_log_set_callback(log_callback);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -451,6 +465,7 @@ bool Input::UpdateTracks()
|
|||||||
|
|
||||||
av_dump_format(avfc, 0, player->url.c_str(), 0);
|
av_dump_format(avfc, 0, player->url.c_str(), 0);
|
||||||
|
|
||||||
|
bool use_index_as_pid = false;
|
||||||
for (unsigned int n = 0; n < avfc->nb_streams; n++) {
|
for (unsigned int n = 0; n < avfc->nb_streams; n++) {
|
||||||
AVStream *stream = avfc->streams[n];
|
AVStream *stream = avfc->streams[n];
|
||||||
|
|
||||||
@@ -458,18 +473,27 @@ bool Input::UpdateTracks()
|
|||||||
track.stream = stream;
|
track.stream = stream;
|
||||||
AVDictionaryEntry *lang = av_dict_get(stream->metadata, "language", NULL, 0);
|
AVDictionaryEntry *lang = av_dict_get(stream->metadata, "language", NULL, 0);
|
||||||
track.title = lang ? lang->value : "";
|
track.title = lang ? lang->value : "";
|
||||||
track.pid = stream->id;
|
|
||||||
if (!track.pid)
|
if (!use_index_as_pid)
|
||||||
track.pid = n + 1;
|
switch (stream->codec->codec_type) {
|
||||||
|
case AVMEDIA_TYPE_VIDEO:
|
||||||
|
case AVMEDIA_TYPE_AUDIO:
|
||||||
|
case AVMEDIA_TYPE_SUBTITLE:
|
||||||
|
if (!stream->id)
|
||||||
|
use_index_as_pid = true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
track.pid = use_index_as_pid ? n + 1: stream->id;
|
||||||
|
|
||||||
switch (stream->codec->codec_type) {
|
switch (stream->codec->codec_type) {
|
||||||
case AVMEDIA_TYPE_VIDEO: {
|
case AVMEDIA_TYPE_VIDEO:
|
||||||
player->manager.addVideoTrack(track);
|
player->manager.addVideoTrack(track);
|
||||||
if (!videoTrack)
|
if (!videoTrack)
|
||||||
videoTrack = player->manager.getVideoTrack(track.pid);
|
videoTrack = player->manager.getVideoTrack(track.pid);
|
||||||
break;
|
break;
|
||||||
}
|
case AVMEDIA_TYPE_AUDIO:
|
||||||
case AVMEDIA_TYPE_AUDIO: {
|
|
||||||
switch(stream->codec->codec_id) {
|
switch(stream->codec->codec_id) {
|
||||||
case AV_CODEC_ID_MP2:
|
case AV_CODEC_ID_MP2:
|
||||||
track.ac3flags = 9;
|
track.ac3flags = 9;
|
||||||
@@ -496,8 +520,7 @@ bool Input::UpdateTracks()
|
|||||||
if (!audioTrack)
|
if (!audioTrack)
|
||||||
audioTrack = player->manager.getAudioTrack(track.pid);
|
audioTrack = player->manager.getAudioTrack(track.pid);
|
||||||
break;
|
break;
|
||||||
}
|
case AVMEDIA_TYPE_SUBTITLE:
|
||||||
case AVMEDIA_TYPE_SUBTITLE: {
|
|
||||||
if (stream->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
|
if (stream->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
|
||||||
std::string l = lang ? lang->value : "";
|
std::string l = lang ? lang->value : "";
|
||||||
uint8_t *data = stream->codec->extradata;
|
uint8_t *data = stream->codec->extradata;
|
||||||
@@ -525,13 +548,25 @@ bool Input::UpdateTracks()
|
|||||||
player->manager.addSubtitleTrack(track);
|
player->manager.addSubtitleTrack(track);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "not handled or unknown codec_type %d\n", stream->codec->codec_type);
|
fprintf(stderr, "not handled or unknown codec_type %d\n", stream->codec->codec_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (unsigned int n = 0; n < avfc->nb_programs; n++) {
|
||||||
|
AVProgram *p = avfc->programs[n];
|
||||||
|
if (p->nb_stream_indexes) {
|
||||||
|
AVDictionaryEntry *name = av_dict_get(p->metadata, "name", NULL, 0);
|
||||||
|
Program program;
|
||||||
|
program.title = name ? name->value : "";
|
||||||
|
program.id = p->id;
|
||||||
|
for (unsigned m = 0; m < p->nb_stream_indexes; m++)
|
||||||
|
program.streams.push_back(avfc->streams[p->stream_index[m]]);
|
||||||
|
player->manager.addProgram(program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -61,7 +61,7 @@ std::vector<Track> Manager::getTracks(std::map<int,Track*> &tracks)
|
|||||||
std::vector<Track> res;
|
std::vector<Track> res;
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||||
for(std::map<int,Track*>::iterator it = tracks.begin(); it != tracks.end(); ++it)
|
for(std::map<int,Track*>::iterator it = tracks.begin(); it != tracks.end(); ++it)
|
||||||
if (!it->second->inactive)
|
if (!it->second->inactive && !it->second->hidden)
|
||||||
res.push_back(*it->second);
|
res.push_back(*it->second);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -133,6 +133,107 @@ bool Manager::initTrackUpdate()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Manager::addProgram(Program &program)
|
||||||
|
{
|
||||||
|
Programs[program.id] = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Program> Manager::getPrograms(void)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||||
|
std::vector<Program> res;
|
||||||
|
for (std::map<int,Program>::iterator it = Programs.begin(); it != Programs.end(); ++it)
|
||||||
|
res.push_back(it->second);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Manager::selectProgram(const int id)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||||
|
std::map<int,Program>::iterator i = Programs.find(id);
|
||||||
|
if (i != Programs.end()) {
|
||||||
|
|
||||||
|
// mark all tracks as hidden
|
||||||
|
for (std::map<int,Track*>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
|
||||||
|
it->second->hidden = true;
|
||||||
|
|
||||||
|
for (std::map<int, Track*>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
|
||||||
|
it->second->hidden = true;
|
||||||
|
|
||||||
|
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
|
||||||
|
it->second->hidden = true;
|
||||||
|
|
||||||
|
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
||||||
|
it->second->hidden = true;
|
||||||
|
|
||||||
|
// unhide tracks that are part of the selected program
|
||||||
|
for (unsigned int j = 0; j < i->second.streams.size(); j++) {
|
||||||
|
AVStream *stream = i->second.streams[j];
|
||||||
|
bool h = true;
|
||||||
|
for (std::map<int,Track*>::iterator it = audioTracks.begin(); h && (it != audioTracks.end()); ++it)
|
||||||
|
if (stream == it->second->stream)
|
||||||
|
h = it->second->hidden = false;
|
||||||
|
|
||||||
|
if (!h)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (std::map<int, Track*>::iterator it = videoTracks.begin(); h && (it != videoTracks.end()); ++it)
|
||||||
|
if (stream == it->second->stream)
|
||||||
|
h = it->second->hidden = false;
|
||||||
|
|
||||||
|
if (!h)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); h && (it != subtitleTracks.end()); ++it)
|
||||||
|
if (stream == it->second->stream)
|
||||||
|
h = it->second->hidden = false;
|
||||||
|
|
||||||
|
if (!h)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); h && (it != teletextTracks.end()); ++it)
|
||||||
|
if (stream == it->second->stream)
|
||||||
|
h = it->second->hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell ffmpeg what we're interested in
|
||||||
|
for (std::map<int,Track*>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
|
||||||
|
if (it->second->hidden || it->second->inactive) {
|
||||||
|
it->second->stream->discard = AVDISCARD_ALL;
|
||||||
|
} else {
|
||||||
|
it->second->stream->discard = AVDISCARD_DEFAULT;
|
||||||
|
player->input.SwitchAudio(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::map<int, Track*>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
|
||||||
|
if (it->second->hidden || it->second->inactive) {
|
||||||
|
it->second->stream->discard = AVDISCARD_ALL;
|
||||||
|
} else {
|
||||||
|
it->second->stream->discard = AVDISCARD_DEFAULT;
|
||||||
|
player->input.SwitchVideo(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
|
||||||
|
if (it->second->hidden || it->second->inactive) {
|
||||||
|
it->second->stream->discard = AVDISCARD_ALL;
|
||||||
|
} else {
|
||||||
|
it->second->stream->discard = AVDISCARD_DEFAULT;
|
||||||
|
player->input.SwitchSubtitle(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
||||||
|
if (it->second->hidden || it->second->inactive) {
|
||||||
|
it->second->stream->discard = AVDISCARD_ALL;
|
||||||
|
} else {
|
||||||
|
it->second->stream->discard = AVDISCARD_DEFAULT;
|
||||||
|
player->input.SwitchTeletext(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Manager::clearTracks()
|
void Manager::clearTracks()
|
||||||
{
|
{
|
||||||
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
|
||||||
@@ -152,6 +253,8 @@ void Manager::clearTracks()
|
|||||||
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
|
||||||
delete it->second;
|
delete it->second;
|
||||||
teletextTracks.clear();
|
teletextTracks.clear();
|
||||||
|
|
||||||
|
Programs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager::~Manager()
|
Manager::~Manager()
|
||||||
|
@@ -129,7 +129,7 @@ bool Output::Play()
|
|||||||
|
|
||||||
if (videoStream && videofd > -1 && (avcc = videoStream->codec)) {
|
if (videoStream && videofd > -1 && (avcc = videoStream->codec)) {
|
||||||
videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
||||||
videoWriter->Init(videofd, videoStream);
|
videoWriter->Init(videofd, videoStream, player);
|
||||||
if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(avcc->codec_id))
|
if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(avcc->codec_id))
|
||||||
|| dioctl(videofd, VIDEO_PLAY, NULL))
|
|| dioctl(videofd, VIDEO_PLAY, NULL))
|
||||||
ret = false;
|
ret = false;
|
||||||
@@ -137,7 +137,7 @@ bool Output::Play()
|
|||||||
|
|
||||||
if (audioStream && audiofd > -1 && (avcc = audioStream->codec)) {
|
if (audioStream && audiofd > -1 && (avcc = audioStream->codec)) {
|
||||||
audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
||||||
audioWriter->Init(audiofd, audioStream);
|
audioWriter->Init(audiofd, audioStream, player);
|
||||||
if (dioctl(audiofd, AUDIO_SET_ENCODING, audioWriter->GetAudioEncoding(avcc->codec_id))
|
if (dioctl(audiofd, AUDIO_SET_ENCODING, audioWriter->GetAudioEncoding(avcc->codec_id))
|
||||||
|| dioctl(audiofd, AUDIO_PLAY, NULL))
|
|| dioctl(audiofd, AUDIO_PLAY, NULL))
|
||||||
ret = false;
|
ret = false;
|
||||||
@@ -310,7 +310,7 @@ bool Output::SwitchAudio(AVStream *stream)
|
|||||||
if (!avcc)
|
if (!avcc)
|
||||||
return false;
|
return false;
|
||||||
audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
||||||
audioWriter->Init(audiofd, audioStream);
|
audioWriter->Init(audiofd, audioStream, player);
|
||||||
if (audiofd > -1) {
|
if (audiofd > -1) {
|
||||||
dioctl(audiofd, AUDIO_SET_ENCODING, Writer::GetAudioEncoding(avcc->codec_id));
|
dioctl(audiofd, AUDIO_SET_ENCODING, Writer::GetAudioEncoding(avcc->codec_id));
|
||||||
dioctl(audiofd, AUDIO_PLAY, NULL);
|
dioctl(audiofd, AUDIO_PLAY, NULL);
|
||||||
@@ -334,7 +334,7 @@ bool Output::SwitchVideo(AVStream *stream)
|
|||||||
if (!avcc)
|
if (!avcc)
|
||||||
return false;
|
return false;
|
||||||
videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type);
|
||||||
videoWriter->Init(videofd, videoStream);
|
videoWriter->Init(videofd, videoStream, player);
|
||||||
if (videofd > -1) {
|
if (videofd > -1) {
|
||||||
dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(avcc->codec_id));
|
dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(avcc->codec_id));
|
||||||
dioctl(videofd, VIDEO_PLAY, NULL);
|
dioctl(videofd, VIDEO_PLAY, NULL);
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
@@ -411,3 +412,33 @@ int Player::GetTeletextPid()
|
|||||||
Track *track = input.teletextTrack;
|
Track *track = input.teletextTrack;
|
||||||
return track ? track->pid : 0;
|
return track ? track->pid : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Player::GetPrograms(std::vector<std::string> &keys, std::vector<std::string> &values)
|
||||||
|
{
|
||||||
|
keys.clear();
|
||||||
|
values.clear();
|
||||||
|
|
||||||
|
std::vector<Program> p = manager.getPrograms();
|
||||||
|
|
||||||
|
if (p.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (std::vector<Program>::iterator it = p.begin(); it != p.end(); ++it) {
|
||||||
|
std::stringstream s;
|
||||||
|
s << it->id;
|
||||||
|
keys.push_back(s.str());
|
||||||
|
values.push_back(it->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::SelectProgram(int key)
|
||||||
|
{
|
||||||
|
return manager.selectProgram(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::SelectProgram(std::string &key)
|
||||||
|
{
|
||||||
|
return manager.selectProgram(atoi(key.c_str()));
|
||||||
|
}
|
||||||
|
@@ -40,14 +40,15 @@ class WriterDIVX : public Writer
|
|||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
public:
|
public:
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
bool Write(AVPacket *packet, int64_t pts);
|
||||||
void Init(int fd, AVStream *_stream);
|
void Init(int fd, AVStream *_stream, Player *player);
|
||||||
WriterDIVX();
|
WriterDIVX();
|
||||||
};
|
};
|
||||||
|
|
||||||
void WriterDIVX::Init(int _fd, AVStream *_stream)
|
void WriterDIVX::Init(int _fd, AVStream *_stream, Player *_player)
|
||||||
{
|
{
|
||||||
fd = _fd;
|
fd = _fd;
|
||||||
stream = _stream;
|
stream = _stream;
|
||||||
|
player = _player;
|
||||||
initialHeader = true;
|
initialHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@
|
|||||||
#include "pes.h"
|
#include "pes.h"
|
||||||
#include "writer.h"
|
#include "writer.h"
|
||||||
|
|
||||||
#define NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24
|
#define NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24 // Reference: player/standards/h264.h
|
||||||
#define CONTAINER_PARAMETERS_VERSION 0x00
|
#define CONTAINER_PARAMETERS_VERSION 0x00
|
||||||
|
|
||||||
typedef struct avcC_s {
|
typedef struct avcC_s {
|
||||||
@@ -43,8 +43,6 @@ typedef struct avcC_s {
|
|||||||
uint8_t Params[1]; // {length,params}{length,params}...sequence then picture
|
uint8_t Params[1]; // {length,params}{length,params}...sequence then picture
|
||||||
} avcC_t;
|
} avcC_t;
|
||||||
|
|
||||||
static uint8_t Head[] = { 0, 0, 0, 1 };
|
|
||||||
|
|
||||||
class WriterH264 : public Writer
|
class WriterH264 : public Writer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -53,14 +51,15 @@ class WriterH264 : public Writer
|
|||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
public:
|
public:
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
bool Write(AVPacket *packet, int64_t pts);
|
||||||
void Init(int _fd, AVStream *_stream);
|
void Init(int _fd, AVStream *_stream, Player *_player);
|
||||||
WriterH264();
|
WriterH264();
|
||||||
};
|
};
|
||||||
|
|
||||||
void WriterH264::Init(int _fd, AVStream *_stream)
|
void WriterH264::Init(int _fd, AVStream *_stream, Player *_player)
|
||||||
{
|
{
|
||||||
fd = _fd;
|
fd = _fd;
|
||||||
stream = _stream;
|
stream = _stream;
|
||||||
|
player = _player;
|
||||||
initialHeader = true;
|
initialHeader = true;
|
||||||
NalLengthBytes = 1;
|
NalLengthBytes = 1;
|
||||||
}
|
}
|
||||||
@@ -70,20 +69,66 @@ bool WriterH264::Write(AVPacket *packet, int64_t pts)
|
|||||||
if (!packet || !packet->data)
|
if (!packet || !packet->data)
|
||||||
return false;
|
return false;
|
||||||
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
|
||||||
unsigned int TimeDelta;
|
struct iovec iov[512];
|
||||||
unsigned int TimeScale;
|
|
||||||
int len = 0;
|
|
||||||
int ic = 0;
|
|
||||||
struct iovec iov[128];
|
|
||||||
|
|
||||||
TimeDelta = av_rescale(1000ll, stream->r_frame_rate.num, stream->r_frame_rate.den);
|
uint8_t *d = packet->data;
|
||||||
TimeScale = (TimeDelta < 23970) ? 1001 : 1000; /* fixme: revise this */
|
|
||||||
|
|
||||||
if ((packet->size > 3)
|
if (initialHeader) {
|
||||||
&& ((packet->data[0] == 0x00 && packet->data[1] == 0x00 && packet->data[2] == 0x00 && packet->data[3] == 0x01)
|
// The player will use FrameRate and TimeScale to calculate the default frame rate.
|
||||||
|| (packet->data[0] == 0xff && packet->data[1] == 0xff && packet->data[2] == 0xff && packet->data[3] == 0xff))) {
|
// FIXME: TimeDelta should be used instead of FrameRate. This is a historic implementation bug.
|
||||||
|
// Reference: player/frame_parser/frame_parser_video_h264.cpp FrameParser_VideoH264_c::ReadPlayer2ContainerParameters()
|
||||||
|
unsigned int FrameRate = av_rescale(1000ll, stream->r_frame_rate.num, stream->r_frame_rate.den);
|
||||||
|
unsigned int TimeScale = (FrameRate < 23970) ? 1001 : 1000; /* FIXME: revise this */
|
||||||
|
|
||||||
|
uint8_t Header[20];
|
||||||
|
unsigned int len = 0;
|
||||||
|
Header[len++] = 0x00; // Start code, 00 00 00 01 for first NAL unit
|
||||||
|
Header[len++] = 0x00;
|
||||||
|
Header[len++] = 0x00;
|
||||||
|
Header[len++] = 0x01;
|
||||||
|
Header[len++] = NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS; // NAL unit header
|
||||||
|
// Container message version - changes when/if we vary the format of the message
|
||||||
|
Header[len++] = CONTAINER_PARAMETERS_VERSION;
|
||||||
|
Header[len++] = 0xff; // marker bits
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (FrameRate == 0xffffffff)
|
||||||
|
FrameRate = (TimeScale > 1000) ? 1001 : 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Header[len++] = (TimeScale >> 24) & 0xff; // Output the timescale
|
||||||
|
Header[len++] = (TimeScale >> 16) & 0xff;
|
||||||
|
Header[len++] = 0xff; // marker bits
|
||||||
|
Header[len++] = (TimeScale >> 8) & 0xff;
|
||||||
|
Header[len++] = (TimeScale ) & 0xff;
|
||||||
|
Header[len++] = 0xff; // marker bits
|
||||||
|
|
||||||
|
Header[len++] = (FrameRate >> 24) & 0xff; // Output frame period (should be: time delta)
|
||||||
|
Header[len++] = (FrameRate >> 16) & 0xff;
|
||||||
|
Header[len++] = 0xff; // marker bits
|
||||||
|
Header[len++] = (FrameRate >> 8) & 0xff;
|
||||||
|
Header[len++] = (FrameRate ) & 0xff;
|
||||||
|
Header[len++] = 0xff; // marker bits
|
||||||
|
|
||||||
|
Header[len++] = 0x80; // Rsbp trailing bits
|
||||||
|
|
||||||
|
int ic = 0;
|
||||||
|
iov[ic].iov_base = PesHeader;
|
||||||
|
iov[ic++].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||||
|
iov[ic].iov_base = Header;
|
||||||
|
iov[ic++].iov_len = len;
|
||||||
|
if (writev(fd, iov, ic) < 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte-stream format
|
||||||
|
if ((packet->size > 3) && ( (d[0] == 0x00 && d[1] == 0x00 && d[2] == 0x00 && d[3] == 0x01) // first NAL unit
|
||||||
|
|| (d[0] == 0xff && d[1] == 0xff && d[2] == 0xff && d[3] == 0xff) // FIXME, needed???
|
||||||
|
)) {
|
||||||
unsigned int FakeStartCode = /* (call->Version << 8) | */ PES_VERSION_FAKE_START_CODE;
|
unsigned int FakeStartCode = /* (call->Version << 8) | */ PES_VERSION_FAKE_START_CODE;
|
||||||
|
int ic = 0;
|
||||||
iov[ic++].iov_base = PesHeader;
|
iov[ic++].iov_base = PesHeader;
|
||||||
|
unsigned int len = 0;
|
||||||
if (initialHeader) {
|
if (initialHeader) {
|
||||||
initialHeader = false;
|
initialHeader = false;
|
||||||
iov[ic].iov_base = stream->codec->extradata;
|
iov[ic].iov_base = stream->codec->extradata;
|
||||||
@@ -93,15 +138,19 @@ bool WriterH264::Write(AVPacket *packet, int64_t pts)
|
|||||||
iov[ic].iov_base = packet->data;
|
iov[ic].iov_base = packet->data;
|
||||||
iov[ic++].iov_len = packet->size;
|
iov[ic++].iov_len = packet->size;
|
||||||
len += packet->size;
|
len += packet->size;
|
||||||
|
#if 1 // FIXME: needed?
|
||||||
// Hellmaster1024:
|
// Hellmaster1024:
|
||||||
// some packets will only be accepted by the player if we send one byte more than data is available.
|
// some packets will only be accepted by the player if we send one byte more than data is available.
|
||||||
// The content of this byte does not matter. It will be ignored by the player
|
// The content of this byte does not matter. It will be ignored by the player
|
||||||
iov[ic].iov_base = (void *) "";
|
iov[ic].iov_base = (void *) "";
|
||||||
iov[ic++].iov_len = 1;
|
iov[ic++].iov_len = 1;
|
||||||
|
len++;
|
||||||
|
#endif
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode);
|
iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode);
|
||||||
return writev(fd, iov, ic) > -1;
|
return writev(fd, iov, ic) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convert NAL units without sync byte sequence to byte-stream format
|
||||||
if (initialHeader) {
|
if (initialHeader) {
|
||||||
avcC_t *avcCHeader = (avcC_t *) stream->codec->extradata;
|
avcC_t *avcCHeader = (avcC_t *) stream->codec->extradata;
|
||||||
|
|
||||||
@@ -113,138 +162,94 @@ bool WriterH264::Write(AVPacket *packet, int64_t pts)
|
|||||||
if (avcCHeader->Version != 1)
|
if (avcCHeader->Version != 1)
|
||||||
fprintf(stderr, "Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version);
|
fprintf(stderr, "Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version);
|
||||||
|
|
||||||
uint8_t Header[19];
|
int ic = 0;
|
||||||
unsigned int HeaderLen = 0;
|
iov[ic++].iov_base = PesHeader;
|
||||||
Header[HeaderLen++] = 0x00; // Start code
|
|
||||||
Header[HeaderLen++] = 0x00;
|
|
||||||
Header[HeaderLen++] = 0x01;
|
|
||||||
Header[HeaderLen++] = NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS;
|
|
||||||
// Container message version - changes when/if we vary the format of the message
|
|
||||||
Header[HeaderLen++] = CONTAINER_PARAMETERS_VERSION;
|
|
||||||
Header[HeaderLen++] = 0xff; // Field separator
|
|
||||||
|
|
||||||
if (TimeDelta == 0xffffffff)
|
|
||||||
TimeDelta = (TimeScale > 1000) ? 1001 : 1;
|
|
||||||
|
|
||||||
Header[HeaderLen++] = (TimeScale >> 24) & 0xff; // Output the timescale
|
|
||||||
Header[HeaderLen++] = (TimeScale >> 16) & 0xff;
|
|
||||||
Header[HeaderLen++] = 0xff;
|
|
||||||
Header[HeaderLen++] = (TimeScale >> 8) & 0xff;
|
|
||||||
Header[HeaderLen++] = TimeScale & 0xff;
|
|
||||||
Header[HeaderLen++] = 0xff;
|
|
||||||
|
|
||||||
Header[HeaderLen++] = (TimeDelta >> 24) & 0xff; // Output frame period
|
|
||||||
Header[HeaderLen++] = (TimeDelta >> 16) & 0xff;
|
|
||||||
Header[HeaderLen++] = 0xff;
|
|
||||||
Header[HeaderLen++] = (TimeDelta >> 8) & 0xff;
|
|
||||||
Header[HeaderLen++] = TimeDelta & 0xff;
|
|
||||||
Header[HeaderLen++] = 0xff;
|
|
||||||
Header[HeaderLen++] = 0x80; // Rsbp trailing bits
|
|
||||||
|
|
||||||
ic = 0;
|
|
||||||
iov[ic].iov_base = PesHeader;
|
|
||||||
iov[ic++].iov_len = InsertPesHeader(PesHeader, HeaderLen, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
|
||||||
iov[ic].iov_base = Header;
|
|
||||||
iov[ic++].iov_len = HeaderLen;
|
|
||||||
len = writev(fd, iov, ic);
|
|
||||||
if (len < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1;
|
NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1;
|
||||||
unsigned int ParamSets = avcCHeader->NumParamSets & 0x1f;
|
|
||||||
unsigned int ParamOffset = 0;
|
unsigned int ParamOffset = 0;
|
||||||
unsigned int InitialHeaderLength = 0;
|
unsigned int len = 0;
|
||||||
|
|
||||||
ic = 0;
|
// sequence parameter set
|
||||||
iov[ic++].iov_base = PesHeader;
|
unsigned int ParamSets = avcCHeader->NumParamSets & 0x1f;
|
||||||
for (unsigned int i = 0; i < ParamSets; i++) {
|
for (unsigned int i = 0; i < ParamSets; i++) {
|
||||||
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset + 1];
|
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) | avcCHeader->Params[ParamOffset + 1];
|
||||||
|
|
||||||
iov[ic].iov_base = (char *) Head;
|
iov[ic].iov_base = (uint8_t *) "\0\0\0\1";
|
||||||
iov[ic++].iov_len = sizeof(Head);
|
iov[ic++].iov_len = 4;
|
||||||
InitialHeaderLength += sizeof(Head);
|
len += 4;
|
||||||
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
||||||
iov[ic++].iov_len = PsLength;
|
iov[ic++].iov_len = PsLength;
|
||||||
InitialHeaderLength += PsLength;
|
len += PsLength;
|
||||||
ParamOffset += PsLength + 2;
|
ParamOffset += PsLength + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// picture parameter set
|
||||||
ParamSets = avcCHeader->Params[ParamOffset++];
|
ParamSets = avcCHeader->Params[ParamOffset++];
|
||||||
|
|
||||||
for (unsigned int i = 0; i < ParamSets; i++) {
|
for (unsigned int i = 0; i < ParamSets; i++) {
|
||||||
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) + avcCHeader->Params[ParamOffset + 1];
|
unsigned int PsLength = (avcCHeader->Params[ParamOffset] << 8) | avcCHeader->Params[ParamOffset + 1];
|
||||||
|
|
||||||
iov[ic].iov_base = (char *) Head;
|
iov[ic].iov_base = (uint8_t *) "\0\0\0\1";
|
||||||
iov[ic++].iov_len = sizeof(Head);
|
iov[ic++].iov_len = 4;
|
||||||
InitialHeaderLength += sizeof(Head);
|
len += 4;
|
||||||
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2];
|
||||||
iov[ic++].iov_len = PsLength;
|
iov[ic++].iov_len = PsLength;
|
||||||
InitialHeaderLength += PsLength;
|
len += PsLength;
|
||||||
ParamOffset += PsLength + 2;
|
ParamOffset += PsLength + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, InitialHeaderLength, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
|
||||||
ssize_t l = writev(fd, iov, ic);
|
ssize_t l = writev(fd, iov, ic);
|
||||||
if (l < 0)
|
if (l < 0)
|
||||||
return false;
|
return false;
|
||||||
len += l;
|
|
||||||
|
|
||||||
initialHeader = 0;
|
initialHeader = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int SampleSize = packet->size;
|
uint8_t *de = d + packet->size;
|
||||||
unsigned int NalStart = 0;
|
|
||||||
unsigned int VideoPosition = 0;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
unsigned int NalLength;
|
unsigned int len = 0;
|
||||||
uint8_t NalData[4];
|
|
||||||
|
|
||||||
memcpy(NalData, packet->data + VideoPosition, NalLengthBytes);
|
|
||||||
VideoPosition += NalLengthBytes;
|
|
||||||
NalStart += NalLengthBytes;
|
|
||||||
switch (NalLengthBytes) {
|
switch (NalLengthBytes) {
|
||||||
case 1:
|
case 4:
|
||||||
NalLength = (NalData[0]);
|
len = *d;
|
||||||
break;
|
d++;
|
||||||
case 2:
|
|
||||||
NalLength = (NalData[0] << 8) | (NalData[1]);
|
|
||||||
break;
|
|
||||||
case 3:
|
case 3:
|
||||||
NalLength = (NalData[0] << 16) | (NalData[1] << 8) | (NalData[2]);
|
len <<= 8;
|
||||||
break;
|
len |= *d;
|
||||||
|
d++;
|
||||||
|
case 2:
|
||||||
|
len <<= 8;
|
||||||
|
len |= *d;
|
||||||
|
d++;
|
||||||
default:
|
default:
|
||||||
NalLength = (NalData[0] << 24) | (NalData[1] << 16) | (NalData[2] << 8) | (NalData[3]);
|
len <<= 8;
|
||||||
break;
|
len |= *d;
|
||||||
|
d++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NalStart + NalLength > SampleSize) {
|
if (d + len > de) {
|
||||||
fprintf(stderr, "nal length past end of buffer - size %u frame offset %u left %u\n", NalLength, NalStart, SampleSize - NalStart);
|
fprintf(stderr, "NAL length past end of buffer - size %u frame offset %d left %d\n", len, (int) (d - packet->data), (int) (de - d));
|
||||||
NalStart = SampleSize;
|
break;
|
||||||
} else {
|
|
||||||
NalStart += NalLength;
|
|
||||||
ic = 0;
|
|
||||||
iov[ic++].iov_base = PesHeader;
|
|
||||||
|
|
||||||
iov[ic].iov_base = Head;
|
|
||||||
iov[ic++].iov_len = sizeof(Head);
|
|
||||||
|
|
||||||
iov[ic].iov_base = packet->data + VideoPosition;
|
|
||||||
iov[ic++].iov_len = NalLength;
|
|
||||||
|
|
||||||
VideoPosition += NalLength;
|
|
||||||
|
|
||||||
iov[0].iov_len = InsertPesHeader(PesHeader, NalLength, MPEG_VIDEO_PES_START_CODE, pts, 0);
|
|
||||||
ssize_t l = writev(fd, iov, ic);
|
|
||||||
if (l < 0)
|
|
||||||
return false;
|
|
||||||
len += l;
|
|
||||||
|
|
||||||
pts = INVALID_PTS_VALUE;
|
|
||||||
}
|
}
|
||||||
} while (NalStart < SampleSize);
|
|
||||||
|
|
||||||
return len > -1;
|
int ic = 0;
|
||||||
|
iov[ic++].iov_base = PesHeader;
|
||||||
|
iov[ic].iov_base = (uint8_t *) "\0\0\0\1";
|
||||||
|
iov[ic++].iov_len = 4;
|
||||||
|
|
||||||
|
iov[ic].iov_base = d;
|
||||||
|
iov[ic++].iov_len = len;
|
||||||
|
iov[0].iov_len = InsertPesHeader(PesHeader, len + 3, MPEG_VIDEO_PES_START_CODE, pts, 0);
|
||||||
|
ssize_t l = writev(fd, iov, ic);
|
||||||
|
if (l < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
d += len;
|
||||||
|
pts = INVALID_PTS_VALUE;
|
||||||
|
|
||||||
|
} while (d < de);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WriterH264::WriterH264()
|
WriterH264::WriterH264()
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "pes.h"
|
#include "pes.h"
|
||||||
#include "writer.h"
|
#include "writer.h"
|
||||||
|
#include "player.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavutil/avutil.h>
|
#include <libavutil/avutil.h>
|
||||||
@@ -84,7 +85,7 @@ class WriterPCM : public Writer
|
|||||||
bool Write(AVPacket *packet, int64_t pts);
|
bool Write(AVPacket *packet, int64_t pts);
|
||||||
bool prepareClipPlay();
|
bool prepareClipPlay();
|
||||||
bool writePCM(int64_t Pts, uint8_t *data, unsigned int size);
|
bool writePCM(int64_t Pts, uint8_t *data, unsigned int size);
|
||||||
void Init(int _fd, AVStream *_stream);
|
void Init(int _fd, AVStream *_stream, Player *_player);
|
||||||
WriterPCM();
|
WriterPCM();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,10 +220,11 @@ bool WriterPCM::writePCM(int64_t Pts, uint8_t *data, unsigned int size)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriterPCM::Init(int _fd, AVStream *_stream)
|
void WriterPCM::Init(int _fd, AVStream *_stream, Player *_player)
|
||||||
{
|
{
|
||||||
fd = _fd;
|
fd = _fd;
|
||||||
stream = _stream;
|
stream = _stream;
|
||||||
|
player = _player;
|
||||||
initialHeader = true;
|
initialHeader = true;
|
||||||
restart_audio_resampling = true;
|
restart_audio_resampling = true;
|
||||||
}
|
}
|
||||||
@@ -320,6 +322,8 @@ bool WriterPCM::Write(AVPacket *packet, int64_t pts)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pts = player->input.calcPts(stream, av_frame_get_best_effort_timestamp(decoded_frame));
|
||||||
|
|
||||||
int in_samples = decoded_frame->nb_samples;
|
int in_samples = decoded_frame->nb_samples;
|
||||||
int out_samples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + in_samples, out_sample_rate, c->sample_rate, AV_ROUND_UP);
|
int out_samples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + in_samples, out_sample_rate, c->sample_rate, AV_ROUND_UP);
|
||||||
if (out_samples > out_samples_max) {
|
if (out_samples > out_samples_max) {
|
||||||
@@ -339,8 +343,6 @@ bool WriterPCM::Write(AVPacket *packet, int64_t pts)
|
|||||||
restart_audio_resampling = true;
|
restart_audio_resampling = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pts = 0;
|
|
||||||
}
|
}
|
||||||
return !packet_size;
|
return !packet_size;
|
||||||
}
|
}
|
||||||
|
@@ -51,14 +51,15 @@ class WriterVC1 : public Writer
|
|||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
public:
|
public:
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
bool Write(AVPacket *packet, int64_t pts);
|
||||||
void Init(int _fd, AVStream *_stream);
|
void Init(int _fd, AVStream *_stream, Player *_player);
|
||||||
WriterVC1();
|
WriterVC1();
|
||||||
};
|
};
|
||||||
|
|
||||||
void WriterVC1::Init(int _fd, AVStream *_stream)
|
void WriterVC1::Init(int _fd, AVStream *_stream, Player *_player)
|
||||||
{
|
{
|
||||||
fd = _fd;
|
fd = _fd;
|
||||||
stream = _stream;
|
stream = _stream;
|
||||||
|
player = _player;
|
||||||
initialHeader = true;
|
initialHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -58,14 +58,15 @@ class WriterWMV : public Writer
|
|||||||
AVStream *stream;
|
AVStream *stream;
|
||||||
public:
|
public:
|
||||||
bool Write(AVPacket *packet, int64_t pts);
|
bool Write(AVPacket *packet, int64_t pts);
|
||||||
void Init(int _fd, AVStream *_stream);
|
void Init(int _fd, AVStream *_stream, Player *_player);
|
||||||
WriterWMV();
|
WriterWMV();
|
||||||
};
|
};
|
||||||
|
|
||||||
void WriterWMV::Init(int _fd, AVStream *_stream)
|
void WriterWMV::Init(int _fd, AVStream *_stream, Player *_player)
|
||||||
{
|
{
|
||||||
fd = _fd;
|
fd = _fd;
|
||||||
stream = _stream;
|
stream = _stream;
|
||||||
|
player = _player;
|
||||||
initialHeader = true;
|
initialHeader = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -80,10 +80,27 @@ bool cPlayback::Start(char *filename, int vpid, int vtype, int apid, int ac3, in
|
|||||||
videoDecoder->Stop(false);
|
videoDecoder->Stop(false);
|
||||||
audioDecoder->Stop();
|
audioDecoder->Stop();
|
||||||
} else {
|
} else {
|
||||||
|
std::vector<std::string> keys, values;
|
||||||
|
int selected_program = 0;
|
||||||
|
if (GetPrograms(keys, values) && (keys.size() > 1) && ProgramSelectionCallback) {
|
||||||
|
const char *key = ProgramSelectionCallback(ProgramSelectionCallbackData, keys, values);
|
||||||
|
if (!key) {
|
||||||
|
player->Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
selected_program = atoi(key);
|
||||||
|
|
||||||
|
} else if (keys.size() > 0)
|
||||||
|
selected_program = atoi(keys[0].c_str());
|
||||||
|
|
||||||
|
if (!keys.size() || !player->SelectProgram(selected_program)) {
|
||||||
|
if (apid)
|
||||||
|
SetAPid(apid);
|
||||||
|
if (vpid)
|
||||||
|
SetVPid(vpid);
|
||||||
|
}
|
||||||
playing = true;
|
playing = true;
|
||||||
player->output.Open();
|
player->output.Open();
|
||||||
if (apid)
|
|
||||||
SetAPid(apid, 0);
|
|
||||||
ret = player->Play();
|
ret = player->Play();
|
||||||
if (ret && !isHTTP)
|
if (ret && !isHTTP)
|
||||||
playing = ret = player->Pause();
|
playing = ret = player->Pause();
|
||||||
@@ -109,6 +126,11 @@ bool cPlayback::SetAPid(int pid, bool /* ac3 */)
|
|||||||
return player->SwitchAudio(pid);
|
return player->SwitchAudio(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SetVPid(int pid)
|
||||||
|
{
|
||||||
|
return player->SwitchVideo(pid);
|
||||||
|
}
|
||||||
|
|
||||||
bool cPlayback::SetSubtitlePid(int pid)
|
bool cPlayback::SetSubtitlePid(int pid)
|
||||||
{
|
{
|
||||||
return player->SwitchSubtitle(pid);
|
return player->SwitchSubtitle(pid);
|
||||||
@@ -343,6 +365,9 @@ cPlayback::cPlayback(int num __attribute__((unused)))
|
|||||||
{
|
{
|
||||||
playing = false;
|
playing = false;
|
||||||
decoders_closed = false;
|
decoders_closed = false;
|
||||||
|
ProgramSelectionCallback = NULL;
|
||||||
|
ProgramSelectionCallbackData = NULL;
|
||||||
|
|
||||||
player = new Player();
|
player = new Player();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,6 +415,24 @@ AVFormatContext *cPlayback::GetAVFormatContext()
|
|||||||
return player ? player->GetAVFormatContext() : NULL;
|
return player ? player->GetAVFormatContext() : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPlayback::ReleaseAVFormatContext() { if (player)
|
void cPlayback::ReleaseAVFormatContext()
|
||||||
player->ReleaseAVFormatContext();
|
{
|
||||||
|
if (player)
|
||||||
|
player->ReleaseAVFormatContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::GetPrograms(std::vector<std::string> &keys, std::vector<std::string> &values)
|
||||||
|
{
|
||||||
|
return player->GetPrograms(keys, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cPlayback::SelectProgram(std::string &key)
|
||||||
|
{
|
||||||
|
return player->SelectProgram(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cPlayback::SetProgramSelectionCallback(const char *(*fun)(void *, std::vector<std::string> &keys, std::vector<std::string> &values), void *opaque)
|
||||||
|
{
|
||||||
|
ProgramSelectionCallback = fun;
|
||||||
|
ProgramSelectionCallbackData = opaque;
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,11 @@ class cPlayback
|
|||||||
off_t last_size;
|
off_t last_size;
|
||||||
int init_jump;
|
int init_jump;
|
||||||
Player *player;
|
Player *player;
|
||||||
|
const char *(*ProgramSelectionCallback)(void *, std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||||
|
void *ProgramSelectionCallbackData;
|
||||||
|
|
||||||
|
bool GetPrograms(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||||
|
bool SelectProgram(std::string &key);
|
||||||
public:
|
public:
|
||||||
cPlayback(int num = 0);
|
cPlayback(int num = 0);
|
||||||
~cPlayback();
|
~cPlayback();
|
||||||
@@ -36,7 +41,8 @@ class cPlayback
|
|||||||
bool Open(playmode_t PlayMode);
|
bool Open(playmode_t PlayMode);
|
||||||
void Close(void);
|
void Close(void);
|
||||||
bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration);
|
bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration);
|
||||||
bool SetAPid(int pid, bool ac3);
|
bool SetAPid(int pid, bool ac3 = false);
|
||||||
|
bool SetVPid(int pid);
|
||||||
bool SetSubtitlePid(int pid);
|
bool SetSubtitlePid(int pid);
|
||||||
bool SetTeletextPid(int pid);
|
bool SetTeletextPid(int pid);
|
||||||
int GetAPid(void);
|
int GetAPid(void);
|
||||||
@@ -58,13 +64,19 @@ class cPlayback
|
|||||||
void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language);
|
void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language);
|
||||||
bool SelectSubtitles(int pid);
|
bool SelectSubtitles(int pid);
|
||||||
void GetTitles(std::vector<int> &playlists, std::vector<std::string> &titles, int ¤t);
|
void GetTitles(std::vector<int> &playlists, std::vector<std::string> &titles, int ¤t);
|
||||||
void SetTitle(int title);
|
void SetTitle(int title)
|
||||||
|
|
||||||
void GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
|
void GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
|
||||||
void GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
|
void GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
|
||||||
|
|
||||||
|
void SetProgramSelectionCallback(const char *(*fun)(void *, std::vector<std::string> &keys, std::vector<std::string> &values), void *opaque);
|
||||||
|
|
||||||
AVFormatContext *GetAVFormatContext();
|
AVFormatContext *GetAVFormatContext();
|
||||||
void ReleaseAVFormatContext();
|
void ReleaseAVFormatContext();
|
||||||
#if 0
|
#if 0
|
||||||
|
void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language);
|
||||||
|
bool SelectSubtitles(int pid);
|
||||||
|
|
||||||
// Functions that are not used by movieplayer.cpp:
|
// Functions that are not used by movieplayer.cpp:
|
||||||
bool GetOffset(off64_t &offset);
|
bool GetOffset(off64_t &offset);
|
||||||
bool IsPlaying(void) const;
|
bool IsPlaying(void) const;
|
||||||
|
Reference in New Issue
Block a user