diff --git a/libeplayer3/include/input.h b/libeplayer3/include/input.h index 8239019..9f58f92 100644 --- a/libeplayer3/include/input.h +++ b/libeplayer3/include/input.h @@ -44,6 +44,7 @@ class Track; class Input { friend class Player; + friend class WriterPCM; // needs calcPts() friend int interrupt_cb(void *arg); private: diff --git a/libeplayer3/include/manager.h b/libeplayer3/include/manager.h index 8c30568..364595b 100644 --- a/libeplayer3/include/manager.h +++ b/libeplayer3/include/manager.h @@ -46,10 +46,18 @@ struct Track int pid; AVStream *stream; bool inactive; + bool hidden; // not part of currently selected program bool is_static; int ac3flags; 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 streams; }; class Manager @@ -60,6 +68,7 @@ class Manager Player *player; OpenThreads::Mutex mutex; std::map videoTracks, audioTracks, subtitleTracks, teletextTracks; + std::map Programs; void addTrack(std::map &tracks, Track &track); Track *getTrack(std::map &tracks, int pid); std::vector getTracks(std::map &tracks); @@ -68,11 +77,14 @@ class Manager void addAudioTrack(Track &track); void addSubtitleTrack(Track &track); void addTeletextTrack(Track &track); + void addProgram(Program &program); std::vector getVideoTracks(); std::vector getAudioTracks(); std::vector getSubtitleTracks(); std::vector getTeletextTracks(); + std::vector getPrograms(); + bool selectProgram(const int id); Track *getVideoTrack(int pid); Track *getAudioTrack(int pid); diff --git a/libeplayer3/include/player.h b/libeplayer3/include/player.h index 0b53047..9926d1e 100644 --- a/libeplayer3/include/player.h +++ b/libeplayer3/include/player.h @@ -56,6 +56,7 @@ class Player { friend class Output; friend class Manager; friend class cPlayback; + friend class WriterPCM; friend int interrupt_cb(void *arg); private: @@ -117,6 +118,10 @@ class Player { AVFormatContext *GetAVFormatContext() { return input.GetAVFormatContext(); } void ReleaseAVFormatContext() { input.ReleaseAVFormatContext(); } + bool GetPrograms(std::vector &keys, std::vector &values); + bool SelectProgram(int key); + bool SelectProgram(std::string &key); + Player(); }; #endif diff --git a/libeplayer3/include/writer.h b/libeplayer3/include/writer.h index bd699ff..4d6ee2e 100644 --- a/libeplayer3/include/writer.h +++ b/libeplayer3/include/writer.h @@ -37,10 +37,13 @@ extern "C" { #define AV_CODEC_ID_INJECTPCM AV_CODEC_ID_PCM_S16LE +class Player; + class Writer { protected: int fd; + Player *player; public: static void Register(Writer *w, enum AVCodecID id, video_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 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); }; #endif diff --git a/libeplayer3/input.cpp b/libeplayer3/input.cpp index 5fc48a5..52085a0 100644 --- a/libeplayer3/input.cpp +++ b/libeplayer3/input.cpp @@ -20,7 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#define ENABLE_AVLOG 0 +#define ENABLE_LOGGING 1 #include #include @@ -80,6 +80,32 @@ extern void dvbsub_ass_clear(void); // from neutrino-mp/lib/lib/libtuxtxt/tuxtxt_common.h 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() { hasPlayThreadStarted = 1; @@ -87,11 +113,9 @@ bool Input::Play() int64_t showtime = 0; bool restart_audio_resampling = 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. - // This seems to be necessary for network streaming only ... + // HACK: Dropping all video frames until the first audio frame was seen will keep player2 from stuttering. + // Oddly, this seems to be necessary for network streaming only ... bool audioSeen = !audioTrack || !player->isHttp; while (player->isPlaying && !player->abortRequested) { @@ -184,14 +208,8 @@ bool Input::Play() if (_videoTrack && (_videoTrack->stream == stream)) { int64_t pts = calcPts(stream, packet.pts); - if (audioSeen && !player->output.Write(stream, &packet, pts)) { - if (warnVideoWrite) - warnVideoWrite--; - else { - fprintf(stderr, "writing data to %s device failed\n", "video"); - warnVideoWrite = 100; - } - } + if (audioSeen && !player->output.Write(stream, &packet, pts)) + logprintf("writing data to %s device failed\n", "video"); } else if (_audioTrack && (_audioTrack->stream == stream)) { if (restart_audio_resampling) { restart_audio_resampling = false; @@ -199,14 +217,8 @@ bool Input::Play() } if (!player->isBackWard) { int64_t pts = calcPts(stream, packet.pts); - if (!player->output.Write(stream, &packet, _videoTrack ? pts : 0)) { - if (warnAudioWrite) - warnAudioWrite--; - else { - fprintf(stderr, "writing data to %s device failed\n", "audio"); - warnAudioWrite = 100; - } - } + if (!player->output.Write(stream, &packet, _videoTrack ? pts : 0)) + logprintf("writing data to %s device failed\n", "audio"); } audioSeen = true; } else if (_subtitleTrack && (_subtitleTrack->stream == stream)) { @@ -241,7 +253,7 @@ bool Input::Play() } av_free_packet(&packet); - } /* while */ + } /* while */ if (player->abortRequested) player->output.Clear(); @@ -264,25 +276,26 @@ bool Input::Play() return res; } -#if ENABLE_AVLOG -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) +static int lock_callback(void **mutex, enum AVLockOp op) { - 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++; + switch (op) { + case AV_LOCK_CREATE: + *mutex = (void *) new OpenThreads::Mutex; + return !*mutex; + case AV_LOCK_DESTROY: + delete static_cast(*mutex); + *mutex = NULL; + return 0; + case AV_LOCK_OBTAIN: + static_cast(*mutex)->lock(); + return 0; + case AV_LOCK_RELEASE: + static_cast(*mutex)->unlock(); + return 0; + default: + return -1; } } -#endif 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) { abortPlayback = false; -#if ENABLE_AVLOG + av_lockmgr_register(lock_callback); +#if ENABLE_LOGGING av_log_set_callback(log_callback); #endif @@ -451,6 +465,7 @@ bool Input::UpdateTracks() 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++) { AVStream *stream = avfc->streams[n]; @@ -458,18 +473,27 @@ bool Input::UpdateTracks() track.stream = stream; AVDictionaryEntry *lang = av_dict_get(stream->metadata, "language", NULL, 0); track.title = lang ? lang->value : ""; - track.pid = stream->id; - if (!track.pid) - track.pid = n + 1; + + if (!use_index_as_pid) + 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) { - case AVMEDIA_TYPE_VIDEO: { + case AVMEDIA_TYPE_VIDEO: player->manager.addVideoTrack(track); if (!videoTrack) videoTrack = player->manager.getVideoTrack(track.pid); break; - } - case AVMEDIA_TYPE_AUDIO: { + case AVMEDIA_TYPE_AUDIO: switch(stream->codec->codec_id) { case AV_CODEC_ID_MP2: track.ac3flags = 9; @@ -496,8 +520,7 @@ bool Input::UpdateTracks() if (!audioTrack) audioTrack = player->manager.getAudioTrack(track.pid); break; - } - case AVMEDIA_TYPE_SUBTITLE: { + case AVMEDIA_TYPE_SUBTITLE: if (stream->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) { std::string l = lang ? lang->value : ""; uint8_t *data = stream->codec->extradata; @@ -525,13 +548,25 @@ bool Input::UpdateTracks() player->manager.addSubtitleTrack(track); } break; - } default: fprintf(stderr, "not handled or unknown codec_type %d\n", stream->codec->codec_type); 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; } diff --git a/libeplayer3/manager.cpp b/libeplayer3/manager.cpp index bb163a4..9dc1d38 100644 --- a/libeplayer3/manager.cpp +++ b/libeplayer3/manager.cpp @@ -61,7 +61,7 @@ std::vector Manager::getTracks(std::map &tracks) std::vector res; OpenThreads::ScopedLock m_lock(mutex); for(std::map::iterator it = tracks.begin(); it != tracks.end(); ++it) - if (!it->second->inactive) + if (!it->second->inactive && !it->second->hidden) res.push_back(*it->second); return res; } @@ -133,6 +133,107 @@ bool Manager::initTrackUpdate() return true; } +void Manager::addProgram(Program &program) +{ + Programs[program.id] = program; +} + +std::vector Manager::getPrograms(void) +{ + OpenThreads::ScopedLock m_lock(mutex); + std::vector res; + for (std::map::iterator it = Programs.begin(); it != Programs.end(); ++it) + res.push_back(it->second); + return res; +} + +bool Manager::selectProgram(const int id) +{ + OpenThreads::ScopedLock m_lock(mutex); + std::map::iterator i = Programs.find(id); + if (i != Programs.end()) { + + // mark all tracks as hidden + for (std::map::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it) + it->second->hidden = true; + + for (std::map::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it) + it->second->hidden = true; + + for (std::map::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it) + it->second->hidden = true; + + for (std::map::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::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::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::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::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::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::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::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::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() { OpenThreads::ScopedLock m_lock(mutex); @@ -152,6 +253,8 @@ void Manager::clearTracks() for (std::map::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it) delete it->second; teletextTracks.clear(); + + Programs.clear(); } Manager::~Manager() diff --git a/libeplayer3/output.cpp b/libeplayer3/output.cpp index e812afa..6855824 100644 --- a/libeplayer3/output.cpp +++ b/libeplayer3/output.cpp @@ -129,7 +129,7 @@ bool Output::Play() if (videoStream && videofd > -1 && (avcc = videoStream->codec)) { 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)) || dioctl(videofd, VIDEO_PLAY, NULL)) ret = false; @@ -137,7 +137,7 @@ bool Output::Play() if (audioStream && audiofd > -1 && (avcc = audioStream->codec)) { 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)) || dioctl(audiofd, AUDIO_PLAY, NULL)) ret = false; @@ -310,7 +310,7 @@ bool Output::SwitchAudio(AVStream *stream) if (!avcc) return false; audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type); - audioWriter->Init(audiofd, audioStream); + audioWriter->Init(audiofd, audioStream, player); if (audiofd > -1) { dioctl(audiofd, AUDIO_SET_ENCODING, Writer::GetAudioEncoding(avcc->codec_id)); dioctl(audiofd, AUDIO_PLAY, NULL); @@ -334,7 +334,7 @@ bool Output::SwitchVideo(AVStream *stream) if (!avcc) return false; videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type); - videoWriter->Init(videofd, videoStream); + videoWriter->Init(videofd, videoStream, player); if (videofd > -1) { dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(avcc->codec_id)); dioctl(videofd, VIDEO_PLAY, NULL); diff --git a/libeplayer3/player.cpp b/libeplayer3/player.cpp index 0dab1c4..21d3bad 100644 --- a/libeplayer3/player.cpp +++ b/libeplayer3/player.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "player.h" #include "misc.h" @@ -411,3 +412,33 @@ int Player::GetTeletextPid() Track *track = input.teletextTrack; return track ? track->pid : 0; } + +bool Player::GetPrograms(std::vector &keys, std::vector &values) +{ + keys.clear(); + values.clear(); + + std::vector p = manager.getPrograms(); + + if (p.empty()) + return false; + + for (std::vector::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())); +} diff --git a/libeplayer3/writer/divx.cpp b/libeplayer3/writer/divx.cpp index 185665c..2138dc1 100644 --- a/libeplayer3/writer/divx.cpp +++ b/libeplayer3/writer/divx.cpp @@ -40,14 +40,15 @@ class WriterDIVX : public Writer AVStream *stream; public: bool Write(AVPacket *packet, int64_t pts); - void Init(int fd, AVStream *_stream); + void Init(int fd, AVStream *_stream, Player *player); WriterDIVX(); }; -void WriterDIVX::Init(int _fd, AVStream *_stream) +void WriterDIVX::Init(int _fd, AVStream *_stream, Player *_player) { fd = _fd; stream = _stream; + player = _player; initialHeader = true; } diff --git a/libeplayer3/writer/h264.cpp b/libeplayer3/writer/h264.cpp index 639a083..ab66ce0 100644 --- a/libeplayer3/writer/h264.cpp +++ b/libeplayer3/writer/h264.cpp @@ -30,7 +30,7 @@ #include "pes.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 typedef struct avcC_s { @@ -43,8 +43,6 @@ typedef struct avcC_s { uint8_t Params[1]; // {length,params}{length,params}...sequence then picture } avcC_t; -static uint8_t Head[] = { 0, 0, 0, 1 }; - class WriterH264 : public Writer { private: @@ -53,14 +51,15 @@ class WriterH264 : public Writer AVStream *stream; public: bool Write(AVPacket *packet, int64_t pts); - void Init(int _fd, AVStream *_stream); + void Init(int _fd, AVStream *_stream, Player *_player); WriterH264(); }; -void WriterH264::Init(int _fd, AVStream *_stream) +void WriterH264::Init(int _fd, AVStream *_stream, Player *_player) { fd = _fd; stream = _stream; + player = _player; initialHeader = true; NalLengthBytes = 1; } @@ -70,20 +69,66 @@ bool WriterH264::Write(AVPacket *packet, int64_t pts) if (!packet || !packet->data) return false; uint8_t PesHeader[PES_MAX_HEADER_SIZE]; - unsigned int TimeDelta; - unsigned int TimeScale; - int len = 0; - int ic = 0; - struct iovec iov[128]; + struct iovec iov[512]; - TimeDelta = av_rescale(1000ll, stream->r_frame_rate.num, stream->r_frame_rate.den); - TimeScale = (TimeDelta < 23970) ? 1001 : 1000; /* fixme: revise this */ + uint8_t *d = packet->data; - if ((packet->size > 3) - && ((packet->data[0] == 0x00 && packet->data[1] == 0x00 && packet->data[2] == 0x00 && packet->data[3] == 0x01) - || (packet->data[0] == 0xff && packet->data[1] == 0xff && packet->data[2] == 0xff && packet->data[3] == 0xff))) { + if (initialHeader) { + // The player will use FrameRate and TimeScale to calculate the default frame rate. + // 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; + int ic = 0; iov[ic++].iov_base = PesHeader; + unsigned int len = 0; if (initialHeader) { initialHeader = false; 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_len = packet->size; len += packet->size; +#if 1 // FIXME: needed? // Hellmaster1024: // 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 iov[ic].iov_base = (void *) ""; iov[ic++].iov_len = 1; + len++; +#endif iov[0].iov_len = InsertPesHeader(PesHeader, len, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode); return writev(fd, iov, ic) > -1; } + // convert NAL units without sync byte sequence to byte-stream format if (initialHeader) { avcC_t *avcCHeader = (avcC_t *) stream->codec->extradata; @@ -113,138 +162,94 @@ bool WriterH264::Write(AVPacket *packet, int64_t pts) if (avcCHeader->Version != 1) fprintf(stderr, "Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version); - uint8_t Header[19]; - unsigned int HeaderLen = 0; - 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; + int ic = 0; + iov[ic++].iov_base = PesHeader; NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1; - unsigned int ParamSets = avcCHeader->NumParamSets & 0x1f; unsigned int ParamOffset = 0; - unsigned int InitialHeaderLength = 0; + unsigned int len = 0; - ic = 0; - iov[ic++].iov_base = PesHeader; + // sequence parameter set + unsigned int ParamSets = avcCHeader->NumParamSets & 0x1f; 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_len = sizeof(Head); - InitialHeaderLength += sizeof(Head); + iov[ic].iov_base = (uint8_t *) "\0\0\0\1"; + iov[ic++].iov_len = 4; + len += 4; iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2]; iov[ic++].iov_len = PsLength; - InitialHeaderLength += PsLength; + len += PsLength; ParamOffset += PsLength + 2; } + // picture parameter set ParamSets = avcCHeader->Params[ParamOffset++]; 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_len = sizeof(Head); - InitialHeaderLength += sizeof(Head); + iov[ic].iov_base = (uint8_t *) "\0\0\0\1"; + iov[ic++].iov_len = 4; + len += 4; iov[ic].iov_base = &avcCHeader->Params[ParamOffset + 2]; iov[ic++].iov_len = PsLength; - InitialHeaderLength += PsLength; + len += PsLength; 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); if (l < 0) return false; - len += l; - initialHeader = 0; + initialHeader = false; } - unsigned int SampleSize = packet->size; - unsigned int NalStart = 0; - unsigned int VideoPosition = 0; - + uint8_t *de = d + packet->size; do { - unsigned int NalLength; - uint8_t NalData[4]; - - memcpy(NalData, packet->data + VideoPosition, NalLengthBytes); - VideoPosition += NalLengthBytes; - NalStart += NalLengthBytes; + unsigned int len = 0; switch (NalLengthBytes) { - case 1: - NalLength = (NalData[0]); - break; - case 2: - NalLength = (NalData[0] << 8) | (NalData[1]); - break; + case 4: + len = *d; + d++; case 3: - NalLength = (NalData[0] << 16) | (NalData[1] << 8) | (NalData[2]); - break; + len <<= 8; + len |= *d; + d++; + case 2: + len <<= 8; + len |= *d; + d++; default: - NalLength = (NalData[0] << 24) | (NalData[1] << 16) | (NalData[2] << 8) | (NalData[3]); - break; + len <<= 8; + len |= *d; + d++; } - if (NalStart + NalLength > SampleSize) { - fprintf(stderr, "nal length past end of buffer - size %u frame offset %u left %u\n", NalLength, NalStart, SampleSize - NalStart); - NalStart = SampleSize; - } 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; + if (d + len > de) { + fprintf(stderr, "NAL length past end of buffer - size %u frame offset %d left %d\n", len, (int) (d - packet->data), (int) (de - d)); + break; } - } 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() diff --git a/libeplayer3/writer/pcm.cpp b/libeplayer3/writer/pcm.cpp index 413b7d6..cb84f27 100644 --- a/libeplayer3/writer/pcm.cpp +++ b/libeplayer3/writer/pcm.cpp @@ -32,6 +32,7 @@ #include "misc.h" #include "pes.h" #include "writer.h" +#include "player.h" extern "C" { #include @@ -84,7 +85,7 @@ class WriterPCM : public Writer bool Write(AVPacket *packet, int64_t pts); bool prepareClipPlay(); 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(); }; @@ -219,10 +220,11 @@ bool WriterPCM::writePCM(int64_t Pts, uint8_t *data, unsigned int size) return res; } -void WriterPCM::Init(int _fd, AVStream *_stream) +void WriterPCM::Init(int _fd, AVStream *_stream, Player *_player) { fd = _fd; stream = _stream; + player = _player; initialHeader = true; restart_audio_resampling = true; } @@ -320,6 +322,8 @@ bool WriterPCM::Write(AVPacket *packet, int64_t pts) continue; } + pts = player->input.calcPts(stream, av_frame_get_best_effort_timestamp(decoded_frame)); + 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); if (out_samples > out_samples_max) { @@ -339,8 +343,6 @@ bool WriterPCM::Write(AVPacket *packet, int64_t pts) restart_audio_resampling = true; break; } - - pts = 0; } return !packet_size; } diff --git a/libeplayer3/writer/vc1.cpp b/libeplayer3/writer/vc1.cpp index 94d845d..34ef182 100644 --- a/libeplayer3/writer/vc1.cpp +++ b/libeplayer3/writer/vc1.cpp @@ -51,14 +51,15 @@ class WriterVC1 : public Writer AVStream *stream; public: bool Write(AVPacket *packet, int64_t pts); - void Init(int _fd, AVStream *_stream); + void Init(int _fd, AVStream *_stream, Player *_player); WriterVC1(); }; -void WriterVC1::Init(int _fd, AVStream *_stream) +void WriterVC1::Init(int _fd, AVStream *_stream, Player *_player) { fd = _fd; stream = _stream; + player = _player; initialHeader = true; } diff --git a/libeplayer3/writer/wmv.cpp b/libeplayer3/writer/wmv.cpp index c20a0d6..ecc516e 100644 --- a/libeplayer3/writer/wmv.cpp +++ b/libeplayer3/writer/wmv.cpp @@ -58,14 +58,15 @@ class WriterWMV : public Writer AVStream *stream; public: bool Write(AVPacket *packet, int64_t pts); - void Init(int _fd, AVStream *_stream); + void Init(int _fd, AVStream *_stream, Player *_player); WriterWMV(); }; -void WriterWMV::Init(int _fd, AVStream *_stream) +void WriterWMV::Init(int _fd, AVStream *_stream, Player *_player) { fd = _fd; stream = _stream; + player = _player; initialHeader = true; } diff --git a/libspark/playback_libeplayer3.cpp b/libspark/playback_libeplayer3.cpp index de0182e..3416e36 100644 --- a/libspark/playback_libeplayer3.cpp +++ b/libspark/playback_libeplayer3.cpp @@ -80,10 +80,27 @@ bool cPlayback::Start(char *filename, int vpid, int vtype, int apid, int ac3, in videoDecoder->Stop(false); audioDecoder->Stop(); } else { + std::vector 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; player->output.Open(); - if (apid) - SetAPid(apid, 0); ret = player->Play(); if (ret && !isHTTP) playing = ret = player->Pause(); @@ -109,6 +126,11 @@ bool cPlayback::SetAPid(int pid, bool /* ac3 */) return player->SwitchAudio(pid); } +bool cPlayback::SetVPid(int pid) +{ + return player->SwitchVideo(pid); +} + bool cPlayback::SetSubtitlePid(int pid) { return player->SwitchSubtitle(pid); @@ -343,6 +365,9 @@ cPlayback::cPlayback(int num __attribute__((unused))) { playing = false; decoders_closed = false; + ProgramSelectionCallback = NULL; + ProgramSelectionCallbackData = NULL; + player = new Player(); } @@ -390,6 +415,24 @@ AVFormatContext *cPlayback::GetAVFormatContext() return player ? player->GetAVFormatContext() : NULL; } -void cPlayback::ReleaseAVFormatContext() { if (player) - player->ReleaseAVFormatContext(); +void cPlayback::ReleaseAVFormatContext() +{ + if (player) + player->ReleaseAVFormatContext(); +} + +bool cPlayback::GetPrograms(std::vector &keys, std::vector &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 &keys, std::vector &values), void *opaque) +{ + ProgramSelectionCallback = fun; + ProgramSelectionCallbackData = opaque; } diff --git a/libspark/playback_libeplayer3.h b/libspark/playback_libeplayer3.h index be8975e..1e8850c 100644 --- a/libspark/playback_libeplayer3.h +++ b/libspark/playback_libeplayer3.h @@ -29,6 +29,11 @@ class cPlayback off_t last_size; int init_jump; Player *player; + const char *(*ProgramSelectionCallback)(void *, std::vector &keys, std::vector &values); + void *ProgramSelectionCallbackData; + + bool GetPrograms(std::vector &keys, std::vector &values); + bool SelectProgram(std::string &key); public: cPlayback(int num = 0); ~cPlayback(); @@ -36,7 +41,8 @@ class cPlayback bool Open(playmode_t PlayMode); void Close(void); 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 SetTeletextPid(int pid); int GetAPid(void); @@ -58,13 +64,19 @@ class cPlayback void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language); bool SelectSubtitles(int pid); void GetTitles(std::vector &playlists, std::vector &titles, int ¤t); - void SetTitle(int title); + void SetTitle(int title) + void GetChapters(std::vector &positions, std::vector &titles); void GetMetadata(std::vector &keys, std::vector &values); + void SetProgramSelectionCallback(const char *(*fun)(void *, std::vector &keys, std::vector &values), void *opaque); + AVFormatContext *GetAVFormatContext(); void ReleaseAVFormatContext(); #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: bool GetOffset(off64_t &offset); bool IsPlaying(void) const;