- move libeplayer3-sh4 code to libeplayer3-sh4 directory; ...

rename library to libeplayer3_sh4.la

Signed-off-by: Thilo Graf <dbt@novatux.de>
This commit is contained in:
svenhoefer
2018-12-23 22:33:56 +01:00
committed by Thilo Graf
parent 89e4134873
commit 335dbdd2ea
29 changed files with 8 additions and 8 deletions

View File

@@ -1,23 +0,0 @@
AUTOMAKE_OPTIONS = subdir-objects
noinst_LTLIBRARIES = libeplayer3.la
AM_CPPFLAGS = -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS
AM_CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE
AM_CPPFLAGS += -I$(srcdir)/include
AM_CXXFLAGS = -fno-rtti -fno-exceptions -fno-strict-aliasing
libeplayer3_la_SOURCES = \
input.cpp output.cpp manager.cpp player.cpp \
writer/writer.cpp \
writer/pes.cpp \
writer/misc.cpp
# writer/wmv.cpp writer/ac3.cpp writer/divx.cpp
# writer/dts.cpp writer/mpeg2.cpp writer/mp3.cpp
# writer/h264.cpp
# writer/h263.cpp writer/vc1.cpp writer/pcm.cpp writer/aac.cpp
LIBEPLAYER3_LIBS = libeplayer3.la
# -lpthread -lavformat -lavcodec -lavutil -lswresample -lm

View File

@@ -1,78 +0,0 @@
This is a revised libeplayer3 version for Neutrino, rewritten in C++, with
various code parts (e.g. subtitle processing, non-working decoders) removed.
--martii
The original libeplayer3 README follows:
/*
* SCOPE:
* -------
*
* libeplayer3 was developed to create a cleaner and more stable
* version of the libeplayer2.
* Currently the lib supports only one container, which handle all
* files by using the ffmpeg library.
*
* FEATURES:
* -----------------------
*
* - more stable than libeplayer2.
* - more multimedia files are supported than libeplayer2.
* - mms stream support.
* - new videocodec support:
* - wmv and vc1 (sti7109 & sti7111 & sti7105 only).
* - flv.
* - improved http streaming support
* - subtitle rendering (ssa / ass) by using libass
*
* STYLE GUIDELINES:
* ------------------
*
* If you decide to add some lines of code please ensure the following:
* - do not use a windows editor.
* - a tab must be emulated by 4 spaces (most editors support this).
* If you accidental break this rule use astyle to reorganize indentation,
* and dos2unix to remove windows style.
*
* Programming GUIDLINES:
* -----------------------
*
* - the compiler is intentionally set to Wall, it would be nice if all
* programmer looks for warnings and solve them.
* - make sanity checks where ever you can.
* - freeing memory is an act of solidarity, but it also increases uptime
* of your receiver. ;)
* - if you detect stuff which may be generic, then make it generic.
* - commenting code is not a bad idea.
*
* KNOWN BUGS / PROBLEMS:
* ----------------------
*
* - reverse playback needs improvement
* - some formats makes problems ?
* - getting stream info currently leads to a memory leak in e2. this is
* not a problem of this implementation its also exists in libeplayer2.
* e2 delivers a strdupped variable which is overwritten by what the container
* delivers. this is very hacky ;) -> (see comment in container_ffmpeg_get_info)
*
* License:
* --------
*
* Copyright (C) 2010 crow, schischu, hellmaster1024 and konfetti.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/

View File

@@ -1,91 +0,0 @@
/*
* input class
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __INPUT_H__
#define __INPUT_H__
#include <stdint.h>
#include <string>
#include <vector>
#include <map>
#include <OpenThreads/ScopedLock>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
class Player;
class Track;
class Input
{
friend class Player;
friend class WriterPCM; // needs calcPts()
friend int interrupt_cb(void *arg);
private:
OpenThreads::Mutex mutex;
Track *videoTrack;
Track *audioTrack;
Track *subtitleTrack;
Track *teletextTrack;
int hasPlayThreadStarted;
int64_t seek_avts_abs;
int64_t seek_avts_rel;
bool isContainerRunning;
bool abortPlayback;
Player *player;
AVFormatContext *avfc;
uint64_t readCount;
int64_t calcPts(AVStream * stream, int64_t pts);
public:
Input();
~Input();
bool ReadSubtitle(const char *filename, const char *format, int pid);
bool ReadSubtitles(const char *filename);
bool Init(const char *filename, std::string headers = "");
bool UpdateTracks();
bool Play();
bool Stop();
bool Seek(int64_t sec, bool absolute);
bool GetDuration(int64_t &duration);
bool SwitchAudio(Track *track);
bool SwitchSubtitle(Track *track);
bool SwitchTeletext(Track *track);
bool SwitchVideo(Track *track);
bool GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
bool GetReadCount(uint64_t &readcount);
AVFormatContext *GetAVFormatContext();
void ReleaseAVFormatContext();
};
#endif

View File

@@ -1,99 +0,0 @@
/*
* manager class
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __MANAGER_H__
#define __MANAGER_H__
#include <stdint.h>
#include <string>
#include <vector>
#include <map>
#include <OpenThreads/ScopedLock>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
class Player;
struct Track
{
std::string title;
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(false), hidden(false), is_static(false), ac3flags(0) {}
};
struct Program
{
int id;
std::string title;
std::vector<AVStream *> streams;
};
class Manager
{
friend class Player;
private:
Player *player;
OpenThreads::Mutex mutex;
std::map<int,Track*> videoTracks, audioTracks, subtitleTracks, teletextTracks;
std::map<int,Program> Programs;
void addTrack(std::map<int,Track*> &tracks, Track &track);
Track *getTrack(std::map<int,Track*> &tracks, int pid);
std::vector<Track> getTracks(std::map<int,Track*> &tracks);
public:
void addVideoTrack(Track &track);
void addAudioTrack(Track &track);
void addSubtitleTrack(Track &track);
void addTeletextTrack(Track &track);
void addProgram(Program &program);
std::vector<Track> getVideoTracks();
std::vector<Track> getAudioTracks();
std::vector<Track> getSubtitleTracks();
std::vector<Track> getTeletextTracks();
std::vector<Program> getPrograms();
bool selectProgram(const int id);
Track *getVideoTrack(int pid);
Track *getAudioTrack(int pid);
Track *getSubtitleTrack(int pid);
Track *getTeletextTrack(int pid);
bool initTrackUpdate();
void clearTracks();
~Manager();
};
#endif

View File

@@ -1,20 +0,0 @@
#ifndef misc_123
#define misc_123
/* some useful things needed by many files ... */
#include <stdint.h>
#define INVALID_PTS_VALUE 0x200000000ll
struct BitPacker_t
{
uint8_t *Ptr; /* write pointer */
unsigned int BitBuffer; /* bitreader shifter */
int Remaining; /* number of remaining in the shifter */
};
void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length);
void FlushBits(BitPacker_t * ld);
#endif

View File

@@ -1,80 +0,0 @@
/*
* output class
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __OUTPUT_H__
#define __OUTPUT_H__
#include <stdint.h>
#include <string>
#include <vector>
#include <map>
#include <OpenThreads/ScopedLock>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
#include "writer.h"
class Player;
class Output
{
friend class Player;
private:
int videofd;
int audiofd;
Writer *videoWriter, *audioWriter;
OpenThreads::Mutex audioMutex, videoMutex;
Track *audioTrack, *videoTrack;
Player *player;
public:
Output();
~Output();
bool Open();
bool Close();
bool Play();
bool Stop();
bool Pause();
bool Continue();
bool Mute(bool);
bool Flush();
bool FastForward(int speed);
bool SlowMotion(int speed);
bool AVSync(bool);
bool Clear();
bool ClearAudio();
bool ClearVideo();
bool GetPts(int64_t &pts);
bool GetFrameCount(int64_t &framecount);
bool SwitchAudio(Track *track);
bool SwitchVideo(Track *track);
bool Write(AVStream *stream, AVPacket *packet, int64_t Pts);
};
#endif

View File

@@ -1,34 +0,0 @@
#ifndef pes_123
#define pes_123
#include <stdint.h>
#define PES_MAX_HEADER_SIZE 64
#define PES_PRIVATE_DATA_FLAG 0x80
#define PES_PRIVATE_DATA_LENGTH 8
#define PES_LENGTH_BYTE_0 5
#define PES_LENGTH_BYTE_1 4
#define PES_FLAGS_BYTE 7
#define PES_EXTENSION_DATA_PRESENT 0x01
#define PES_HEADER_DATA_LENGTH_BYTE 8
#define PES_START_CODE_RESERVED_4 0xfd
#define PES_VERSION_FAKE_START_CODE 0x31
#define MAX_PES_PACKET_SIZE 65535
/* start codes */
#define PCM_PES_START_CODE 0xbd
#define PRIVATE_STREAM_1_PES_START_CODE 0xbd
#define H263_VIDEO_PES_START_CODE 0xfe
#define H264_VIDEO_PES_START_CODE 0xe2
#define MPEG_VIDEO_PES_START_CODE 0xe0
#define MPEG_AUDIO_PES_START_CODE 0xc0
#define VC1_VIDEO_PES_START_CODE 0xfd
#define AAC_AUDIO_PES_START_CODE 0xcf
int InsertPesHeader(uint8_t *data, int size, uint8_t stream_id, int64_t pts, int pic_start_code);
int InsertVideoPrivateDataHeader(uint8_t *data, int payload_size);
#endif

View File

@@ -1,127 +0,0 @@
/*
* player class
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include <OpenThreads/ScopedLock>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
#include <pthread.h>
#include <stdint.h>
#include <string>
#include <vector>
#include <map>
#include "input.h"
#include "output.h"
#include "manager.h"
struct Chapter
{
std::string title;
int64_t start;
int64_t end;
};
class Player {
friend class Input;
friend class Output;
friend class Manager;
friend class cPlayback;
friend class WriterPCM;
friend int interrupt_cb(void *arg);
private:
Input input;
Output output;
Manager manager;
OpenThreads::Mutex chapterMutex;
std::vector<Chapter> chapters;
pthread_t playThread;
bool abortRequested;
bool isHttp;
bool isPaused;
bool isSlowMotion;
bool hasThreadStarted;
bool isForwarding;
bool isBackWard;
bool isPlaying;
int Speed;
uint64_t readCount;
std::string url;
bool noprobe; /* hack: only minimal probing in av_find_stream_info */
void SetChapters(std::vector<Chapter> &Chapters);
static void* playthread(void*);
public:
bool SwitchAudio(int pid);
bool SwitchVideo(int pid);
bool SwitchTeletext(int pid);
bool SwitchSubtitle(int pid);
int GetAudioPid();
int GetVideoPid();
int GetSubtitlePid();
int GetTeletextPid();
bool GetPts(int64_t &pts);
bool GetFrameCount(int64_t &framecount);
bool GetDuration(int64_t &duration);
bool GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values);
bool SlowMotion(int repeats);
bool FastBackward(int speed);
bool FastForward(int speed);
bool Open(const char *Url, bool noprobe = false, std::string headers = "");
bool Close();
bool Play();
bool Pause();
bool Continue();
bool Stop();
bool Seek(int64_t pos, bool absolute);
void RequestAbort();
bool GetChapters(std::vector<int> &positions, std::vector<std::string> &titles);
AVFormatContext *GetAVFormatContext() { return input.GetAVFormatContext(); }
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();
};
#endif

View File

@@ -1,57 +0,0 @@
/*
* writer class headers
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __WRITER_H__
#define __WRITER_H__
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
#include <linux/dvb/stm_ioctls.h>
#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);
static video_encoding_t GetVideoEncoding(enum AVCodecID id);
static audio_encoding_t GetAudioEncoding(enum AVCodecID id);
static Writer *GetWriter(enum AVCodecID id, enum AVMediaType codec_type, int track_type);
virtual void Init(int _fd, AVStream * /*stream*/, Player *_player ) { fd = _fd; player = _player; }
virtual bool Write(AVPacket *packet, int64_t pts);
};
#endif

View File

@@ -1,782 +0,0 @@
/*
* input class
*
* based on libeplayer3 container_ffmpeg.c, konfetti 2010; based on code from crow
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define ENABLE_LOGGING 1
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "player.h"
#include "misc.h"
static const char *FILENAME = "eplayer/input.cpp";
#define averror(_err,_fun) ({ \
if (_err < 0) { \
char _error[512]; \
av_strerror(_err, _error, sizeof(_error)); \
fprintf(stderr, "%s %d: %s: %d (%s)\n", FILENAME, __LINE__, #_fun, _err, _error); \
} \
_err; \
})
Input::Input()
{
videoTrack = NULL;
audioTrack = NULL;
subtitleTrack = NULL;
teletextTrack = NULL;
hasPlayThreadStarted = 0;
seek_avts_abs = INT64_MIN;
seek_avts_rel = 0;
abortPlayback = false;
}
Input::~Input()
{
}
int64_t Input::calcPts(AVStream * stream, int64_t pts)
{
if (pts == AV_NOPTS_VALUE)
return INVALID_PTS_VALUE;
pts = 90000 * (double)pts * stream->time_base.num / stream->time_base.den;
if (avfc->start_time != AV_NOPTS_VALUE)
pts -= 90000 * avfc->start_time / AV_TIME_BASE;
if (pts < 0)
return INVALID_PTS_VALUE;
return pts;
}
// from neutrino-mp/lib/libdvbsubtitle/dvbsub.cpp
extern void dvbsub_write(AVSubtitle *, int64_t);
extern void dvbsub_ass_write(AVCodecContext *c, AVSubtitle *sub, int pid);
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;
int64_t showtime = 0;
bool restart_audio_resampling = false;
bool bof = false;
// 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) {
//IF MOVIE IS PAUSED, WAIT
if (player->isPaused) {
fprintf(stderr, "paused\n");
usleep(100000);
continue;
}
int seek_target_flag = 0;
int64_t seek_target = INT64_MIN; // in AV_TIME_BASE units
if (seek_avts_rel) {
if (avfc->iformat->flags & AVFMT_TS_DISCONT) {
if (avfc->bit_rate) {
seek_target_flag = AVSEEK_FLAG_BYTE;
seek_target = avio_tell(avfc->pb) + av_rescale(seek_avts_rel, avfc->bit_rate, 8 * AV_TIME_BASE);
}
} else {
int64_t pts;
if(player->output.GetPts(pts))
seek_target = av_rescale(pts, AV_TIME_BASE, 90000ll) + seek_avts_rel;
}
seek_avts_rel = 0;
} else if (seek_avts_abs != INT64_MIN) {
if (avfc->iformat->flags & AVFMT_TS_DISCONT) {
if (avfc->bit_rate) {
seek_target_flag = AVSEEK_FLAG_BYTE;
seek_target = av_rescale(seek_avts_abs, avfc->bit_rate, 8 * AV_TIME_BASE);
}
} else {
seek_target = seek_avts_abs;
}
seek_avts_abs = INT64_MIN;
} else if (player->isBackWard && av_gettime_relative() >= showtime) {
player->output.ClearVideo();
if (bof) {
showtime = av_gettime_relative();
usleep(100000);
continue;
}
seek_avts_rel = player->Speed * AV_TIME_BASE;
showtime = av_gettime_relative() + 300000; //jump back every 300ms
continue;
} else {
bof = false;
}
if (seek_target > INT64_MIN) {
int res;
if (seek_target < 0)
seek_target = 0;
res = avformat_seek_file(avfc, -1, INT64_MIN, seek_target, INT64_MAX, seek_target_flag);
if (res < 0 && player->isBackWard)
bof = true;
seek_target = INT64_MIN;
restart_audio_resampling = true;
// clear streams
for (unsigned int i = 0; i < avfc->nb_streams; i++)
if (avfc->streams[i]->codec && avfc->streams[i]->codec->codec)
avcodec_flush_buffers(avfc->streams[i]->codec);
player->output.ClearAudio();
player->output.ClearVideo();
}
AVPacket packet;
av_init_packet(&packet);
int err = av_read_frame(avfc, &packet);
if (err == AVERROR(EAGAIN)) {
#if (LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR == 25)
av_packet_unref(&packet);
#else
av_free_packet(&packet);
#endif
continue;
}
if (averror(err, av_read_frame)) // EOF?
break; // while
player->readCount += packet.size;
AVStream *stream = avfc->streams[packet.stream_index];
Track *_videoTrack = videoTrack;
Track *_audioTrack = audioTrack;
Track *_subtitleTrack = subtitleTrack;
Track *_teletextTrack = teletextTrack;
if (_videoTrack && (_videoTrack->stream == stream)) {
int64_t pts = calcPts(stream, packet.pts);
if (audioSeen && !player->output.Write(stream, &packet, pts))
logprintf("writing data to video device failed\n");
} else if (_audioTrack && (_audioTrack->stream == stream)) {
if (restart_audio_resampling) {
restart_audio_resampling = false;
player->output.Write(stream, NULL, 0);
}
if (!player->isBackWard) {
int64_t pts = calcPts(stream, packet.pts);
if (!player->output.Write(stream, &packet, _videoTrack ? pts : 0))
logprintf("writing data to audio device failed\n");
}
audioSeen = true;
} else if (_subtitleTrack && (_subtitleTrack->stream == stream)) {
if (stream->codec->codec) {
AVSubtitle sub;
memset(&sub, 0, sizeof(sub));
int got_sub_ptr = 0;
err = avcodec_decode_subtitle2(stream->codec, &sub, &got_sub_ptr, &packet);
averror(err, avcodec_decode_subtitle2);
if (got_sub_ptr && sub.num_rects > 0) {
switch (sub.rects[0]->type) {
case SUBTITLE_TEXT: // FIXME?
case SUBTITLE_ASS:
dvbsub_ass_write(stream->codec, &sub, _subtitleTrack->pid);
break;
case SUBTITLE_BITMAP: {
int64_t pts = calcPts(stream, packet.pts);
dvbsub_write(&sub, pts);
// avsubtitle_free() will be called by handler
break;
}
default:
break;
}
}
}
} else if (_teletextTrack && (_teletextTrack->stream == stream)) {
if (packet.data && packet.size > 1)
teletext_write(_teletextTrack->pid, packet.data + 1, packet.size - 1);
}
#if (LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR == 25)
av_packet_unref(&packet);
#else
av_free_packet(&packet);
#endif
} /* while */
if (player->abortRequested)
player->output.Clear();
else
player->output.Flush();
dvbsub_ass_clear();
abortPlayback = true;
hasPlayThreadStarted = false;
return true;
}
/*static*/ int interrupt_cb(void *arg)
{
Player *player = (Player *) arg;
bool res = player->input.abortPlayback || player->abortRequested;
if (res)
fprintf(stderr, "%s %s %d: abort requested (%d/%d)\n", FILENAME, __func__, __LINE__, player->input.abortPlayback, player->abortRequested);
return res;
}
static int lock_callback(void **mutex, enum AVLockOp op)
{
switch (op) {
case AV_LOCK_CREATE:
*mutex = (void *) new OpenThreads::Mutex;
return !*mutex;
case AV_LOCK_DESTROY:
delete static_cast<OpenThreads::Mutex *>(*mutex);
*mutex = NULL;
return 0;
case AV_LOCK_OBTAIN:
static_cast<OpenThreads::Mutex *>(*mutex)->lock();
return 0;
case AV_LOCK_RELEASE:
static_cast<OpenThreads::Mutex *>(*mutex)->unlock();
return 0;
default:
return -1;
}
}
bool Input::ReadSubtitle(const char *filename, const char *format, int pid)
{
const char *lastDot = strrchr(filename, '.');
if (!lastDot)
return false;
char subfile[strlen(filename) + strlen(format)];
strcpy(subfile, filename);
strcpy(subfile + (lastDot + 1 - filename), format);
if (access(subfile, R_OK))
return false;
AVFormatContext *subavfc = avformat_alloc_context();
int err = avformat_open_input(&subavfc, subfile, av_find_input_format(format), 0);
if (averror(err, avformat_open_input)) {
avformat_free_context(subavfc);
return false;
}
avformat_find_stream_info(subavfc, NULL);
if (subavfc->nb_streams != 1) {
avformat_free_context(subavfc);
return false;
}
AVCodecContext *c = subavfc->streams[0]->codec;
AVCodec *codec = avcodec_find_decoder(c->codec_id);
if (!codec) {
avformat_free_context(subavfc);
return false;
}
err = avcodec_open2(c, codec, NULL);
if (averror(err, avcodec_open2)) {
avformat_free_context(subavfc);
return false;
}
AVPacket packet;
av_init_packet(&packet);
while (av_read_frame(subavfc, &packet) > -1) {
AVSubtitle sub;
memset(&sub, 0, sizeof(sub));
int got_sub = 0;
avcodec_decode_subtitle2(c, &sub, &got_sub, &packet);
if (got_sub)
dvbsub_ass_write(c, &sub, pid);
#if (LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR == 25)
av_packet_unref(&packet);
#else
av_free_packet(&packet);
#endif
}
avcodec_close(c);
avformat_close_input(&subavfc);
avformat_free_context(subavfc);
Track track;
track.title = format;
track.is_static = 1;
track.pid = pid;
player->manager.addSubtitleTrack(track);
return true;
}
bool Input::ReadSubtitles(const char *filename) {
if (strncmp(filename, "file://", 7))
return false;
filename += 7;
bool ret = false;
ret |= ReadSubtitle(filename, "srt", 0xFFFF);
ret |= ReadSubtitle(filename, "ass", 0xFFFE);
ret |= ReadSubtitle(filename, "ssa", 0xFFFD);
return ret;
}
bool Input::Init(const char *filename, std::string headers)
{
bool find_info = true;
abortPlayback = false;
av_lockmgr_register(lock_callback);
#if ENABLE_LOGGING
av_log_set_flags(AV_LOG_SKIP_REPEATED);
av_log_set_level(AV_LOG_INFO);
/* out commented here for using ffmpeg default: av_log_default_callback
because of better log level handling */
//av_log_set_callback(log_callback);
#else
av_log_set_level(AV_LOG_PANIC);
#endif
if (!filename) {
fprintf(stderr, "filename NULL\n");
return false;
}
if (!headers.empty())
{
fprintf(stderr, "%s %s %d: %s\n%s\n", FILENAME, __func__, __LINE__, filename, headers.c_str());
headers += "\r\n";
}
else
{
fprintf(stderr, "%s %s %d: %s\n", FILENAME, __func__, __LINE__, filename);
}
avcodec_register_all();
av_register_all();
avformat_network_init();
videoTrack = NULL;
audioTrack = NULL;
subtitleTrack = NULL;
teletextTrack = NULL;
#if 0
again:
#endif
avfc = avformat_alloc_context();
avfc->interrupt_callback.callback = interrupt_cb;
avfc->interrupt_callback.opaque = (void *) player;
AVDictionary *options = NULL;
av_dict_set(&options, "auth_type", "basic", 0);
if (!headers.empty())
{
av_dict_set(&options, "headers", headers.c_str(), 0);
}
#if ENABLE_LOGGING
av_log_set_level(AV_LOG_DEBUG);
#endif
int err = avformat_open_input(&avfc, filename, NULL, &options);
#if ENABLE_LOGGING
av_log_set_level(AV_LOG_INFO);
#endif
av_dict_free(&options);
if (averror(err, avformat_open_input)) {
avformat_free_context(avfc);
return false;
}
avfc->iformat->flags |= AVFMT_SEEK_TO_PTS;
avfc->flags = AVFMT_FLAG_GENPTS;
if (player->noprobe) {
#if (LIBAVFORMAT_VERSION_MAJOR < 55) || \
(LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR < 43) || \
(LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR == 43 && LIBAVFORMAT_VERSION_MICRO < 100) || \
(LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR == 25)
avfc->max_analyze_duration = 1;
#else
avfc->max_analyze_duration2 = 1;
#endif
avfc->probesize = 131072;
}
#if 0
if (!player->isHttp)
{
for (unsigned int i = 0; i < avfc->nb_streams; i++) {
if (avfc->streams[i]->codec->codec_id == AV_CODEC_ID_AAC)
find_info = false;
}
}
#endif
if (find_info)
err = avformat_find_stream_info(avfc, NULL);
#if 0
if (averror(err, avformat_find_stream_info)) {
avformat_close_input(&avfc);
if (player->noprobe) {
player->noprobe = false;
goto again;
}
return false;
}
#endif
bool res = UpdateTracks();
if (!videoTrack && !audioTrack) {
avformat_close_input(&avfc);
return false;
}
if (videoTrack)
player->output.SwitchVideo(videoTrack);
if (audioTrack)
player->output.SwitchAudio(audioTrack);
ReadSubtitles(filename);
return res;
}
bool Input::UpdateTracks()
{
if (abortPlayback)
return true;
std::vector<Chapter> chapters;
for (unsigned int i = 0; i < avfc->nb_chapters; i++) {
AVChapter *ch = avfc->chapters[i];
AVDictionaryEntry* title = av_dict_get(ch->metadata, "title", NULL, 0);
Chapter chapter;
chapter.title = title ? title->value : "";
chapter.start = av_rescale(ch->time_base.num * AV_TIME_BASE, ch->start, ch->time_base.den);
chapter.end = av_rescale(ch->time_base.num * AV_TIME_BASE, ch->end, ch->time_base.den);
chapters.push_back(chapter);
}
player->SetChapters(chapters);
player->manager.initTrackUpdate();
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];
Track track;
track.stream = stream;
AVDictionaryEntry *lang = av_dict_get(stream->metadata, "language", NULL, 0);
track.title = lang ? lang->value : "";
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;
track.ac3flags = 0;
switch (stream->codec->codec_type) {
case AVMEDIA_TYPE_VIDEO:
player->manager.addVideoTrack(track);
if (!videoTrack)
videoTrack = player->manager.getVideoTrack(track.pid);
break;
case AVMEDIA_TYPE_AUDIO:
switch(stream->codec->codec_id) {
case AV_CODEC_ID_MP2:
track.ac3flags = 1;
break;
case AV_CODEC_ID_MP3:
track.ac3flags = 2;
break;
case AV_CODEC_ID_AC3:
track.ac3flags = 3;
break;
case AV_CODEC_ID_DTS:
track.ac3flags = 4;
break;
case AV_CODEC_ID_AAC: {
unsigned int extradata_size = stream->codec->extradata_size;
unsigned int object_type = 2;
if(extradata_size >= 2)
object_type = stream->codec->extradata[0] >> 3;
if (extradata_size <= 1 || object_type == 1 || object_type == 5) {
fprintf(stderr, "use resampling for AAC\n");
track.ac3flags = 6;
}
else
track.ac3flags = 5;
break;
}
case AV_CODEC_ID_FLAC:
track.ac3flags = 8;
break;
case AV_CODEC_ID_WMAV1:
case AV_CODEC_ID_WMAV2:
case AV_CODEC_ID_WMAVOICE:
case AV_CODEC_ID_WMAPRO:
case AV_CODEC_ID_WMALOSSLESS:
track.ac3flags = 9;
break;
default:
track.ac3flags = 0;
}
player->manager.addAudioTrack(track);
if (!audioTrack)
audioTrack = player->manager.getAudioTrack(track.pid);
break;
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;
int size = stream->codec->extradata_size;
if (size > 0 && 2 * size - 1 == (int) l.length())
for (int i = 0; i < size; i += 2) {
track.title = l.substr(i * 2, 3);
track.type = data[i] >> 3;
track.mag = data[i] & 7;
track.page = data[i + 1];
player->manager.addTeletextTrack(track);
}
} else {
if (!stream->codec->codec) {
stream->codec->codec = avcodec_find_decoder(stream->codec->codec_id);
if (!stream->codec->codec)
fprintf(stderr, "avcodec_find_decoder failed for subtitle track %d\n", n);
else {
int err = avcodec_open2(stream->codec, stream->codec->codec, NULL);
if (averror(err, avcodec_open2))
stream->codec->codec = NULL;
}
}
if (stream->codec->codec)
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;
}
bool Input::Stop()
{
abortPlayback = true;
while (hasPlayThreadStarted != 0)
usleep(100000);
av_log(NULL, AV_LOG_QUIET, "%s", "");
if (avfc) {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mutex);
for (unsigned int i = 0; i < avfc->nb_streams; i++)
avcodec_close(avfc->streams[i]->codec);
avformat_close_input(&avfc);
}
avformat_network_deinit();
return true;
}
AVFormatContext *Input::GetAVFormatContext()
{
mutex.lock();
if (avfc)
return avfc;
mutex.unlock();
return NULL;
}
void Input::ReleaseAVFormatContext()
{
if (avfc)
mutex.unlock();
}
bool Input::Seek(int64_t avts, bool absolute)
{
if (absolute)
seek_avts_abs = avts, seek_avts_rel = 0;
else
seek_avts_abs = INT64_MIN, seek_avts_rel = avts;
return true;
}
bool Input::GetDuration(int64_t &duration)
{
if (avfc) {
duration = avfc->duration;
return true;
}
duration = 0;
return false;
}
bool Input::SwitchAudio(Track *track)
{
audioTrack = track;
player->output.SwitchAudio(track ? track : NULL);
// player->Seek(-5000, false);
return true;
}
bool Input::SwitchSubtitle(Track *track)
{
subtitleTrack = track;
return true;
}
bool Input::SwitchTeletext(Track *track)
{
teletextTrack = track;
return true;
}
bool Input::SwitchVideo(Track *track)
{
videoTrack = track;
player->output.SwitchVideo(track ? track : NULL);
return true;
}
bool Input::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
{
keys.clear();
values.clear();
if (avfc) {
AVDictionaryEntry *tag = NULL;
if (avfc->metadata)
while ((tag = av_dict_get(avfc->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
keys.push_back(tag->key);
values.push_back(tag->value);
}
if (videoTrack)
while ((tag = av_dict_get(videoTrack->stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
keys.push_back(tag->key);
values.push_back(tag->value);
}
if (audioTrack)
while ((tag = av_dict_get(audioTrack->stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
keys.push_back(tag->key);
values.push_back(tag->value);
}
// find the first attached picture, if available
for(unsigned int i = 0; i < avfc->nb_streams; i++) {
if (avfc->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
AVPacket *pkt = &avfc->streams[i]->attached_pic;
FILE *cover_art = fopen("/tmp/.id3coverart", "wb");
if (cover_art) {
fwrite(pkt->data, pkt->size, 1, cover_art);
fclose(cover_art);
}
#if (LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR == 25)
av_packet_unref(pkt);
#else
av_free_packet(pkt);
#endif
break;
}
}
}
return true;
}
bool Input::GetReadCount(uint64_t &readcount)
{
readcount = readCount;
return true;
}

View File

@@ -1,263 +0,0 @@
/*
* manager class
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <string.h>
#include "manager.h"
#include "player.h"
void Manager::addTrack(std::map<int,Track*> &tracks, Track &track)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
std::map<int,Track*>::iterator it = tracks.find(track.pid);
if (it == tracks.end()) {
Track *t = new Track;
*t = track;
tracks[track.pid] = t;
} else
*it->second = track;
}
void Manager::addVideoTrack(Track &track)
{
addTrack(videoTracks, track);
}
void Manager::addAudioTrack(Track &track)
{
addTrack(audioTracks, track);
}
void Manager::addSubtitleTrack(Track &track)
{
addTrack(subtitleTracks, track);
}
void Manager::addTeletextTrack(Track &track)
{
addTrack(teletextTracks, track);
}
std::vector<Track> Manager::getTracks(std::map<int,Track*> &tracks)
{
player->input.UpdateTracks();
std::vector<Track> res;
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
for(std::map<int,Track*>::iterator it = tracks.begin(); it != tracks.end(); ++it)
if (!it->second->inactive && !it->second->hidden)
res.push_back(*it->second);
return res;
}
std::vector<Track> Manager::getVideoTracks()
{
return getTracks(videoTracks);
}
std::vector<Track> Manager::getAudioTracks()
{
return getTracks(audioTracks);
}
std::vector<Track> Manager::getSubtitleTracks()
{
return getTracks(subtitleTracks);
}
std::vector<Track> Manager::getTeletextTracks()
{
return getTracks(teletextTracks);
}
Track *Manager::getTrack(std::map<int,Track*> &tracks, int pid)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
std::map<int,Track*>::iterator it = tracks.find(pid);
if (it != tracks.end() && !it->second->inactive)
return it->second;
return NULL;
}
Track *Manager::getVideoTrack(int pid)
{
return getTrack(videoTracks, pid);
}
Track *Manager::getAudioTrack(int pid)
{
return getTrack(audioTracks, pid);
}
Track *Manager::getSubtitleTrack(int pid)
{
return getTrack(subtitleTracks, pid);
}
Track *Manager::getTeletextTrack(int pid)
{
return getTrack(teletextTracks, pid);
}
bool Manager::initTrackUpdate()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
for (std::map<int,Track*>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
it->second->inactive = !it->second->is_static;
for (std::map<int, Track*>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
it->second->inactive = !it->second->is_static;
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
it->second->inactive = !it->second->is_static;
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
it->second->inactive = !it->second->is_static;
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_NONE;
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_NONE;
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_NONE;
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_NONE;
player->input.SwitchTeletext(it->second);
}
return true;
}
return false;
}
void Manager::clearTracks()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
for (std::map<int,Track*>::iterator it = audioTracks.begin(); it != audioTracks.end(); ++it)
delete it->second;
audioTracks.clear();
for (std::map<int, Track*>::iterator it = videoTracks.begin(); it != videoTracks.end(); ++it)
delete it->second;
videoTracks.clear();
for (std::map<int,Track*>::iterator it = subtitleTracks.begin(); it != subtitleTracks.end(); ++it)
delete it->second;
subtitleTracks.clear();
for (std::map<int,Track*>::iterator it = teletextTracks.begin(); it != teletextTracks.end(); ++it)
delete it->second;
teletextTracks.clear();
Programs.clear();
}
Manager::~Manager()
{
clearTracks();
}

View File

@@ -1,368 +0,0 @@
/*
* output class
*
* based on libeplayer3 LinuxDVB Output handling.
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/dvb/video.h>
#include <linux/dvb/audio.h>
#include <linux/dvb/stm_ioctls.h>
#include <memory.h>
#include <asm/types.h>
#include <pthread.h>
#include <errno.h>
#include "player.h"
#include "output.h"
#include "writer.h"
#include "misc.h"
#include "pes.h"
static const char *FILENAME = "eplayer/output.cpp";
#define dioctl(fd,req,arg) ({ \
int _r = ioctl(fd,req,arg); \
if (_r) \
fprintf(stderr, "%s %d: ioctl '%s' failed: %d (%s)\n", FILENAME, __LINE__, #req, errno, strerror(errno)); \
_r; \
})
#define VIDEODEV "/dev/dvb/adapter0/video0"
#define AUDIODEV "/dev/dvb/adapter0/audio0"
Output::Output()
{
videofd = audiofd = -1;
videoWriter = audioWriter = NULL;
videoTrack = audioTrack = NULL;
}
Output::~Output()
{
Close();
}
bool Output::Open()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (videofd < 0)
videofd = open(VIDEODEV, O_RDWR);
if (videofd < 0)
return false;
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
dioctl(videofd, VIDEO_SELECT_SOURCE, (void *) VIDEO_SOURCE_MEMORY);
dioctl(videofd, VIDEO_SET_STREAMTYPE, (void *) STREAM_TYPE_PROGRAM);
dioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY);
if (audiofd < 0)
audiofd = open(AUDIODEV, O_RDWR);
if (audiofd < 0) {
close(videofd);
videofd = -1;
return false;
}
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
dioctl(audiofd, AUDIO_SELECT_SOURCE, (void *) AUDIO_SOURCE_MEMORY);
dioctl(audiofd, AUDIO_SET_STREAMTYPE, (void *) STREAM_TYPE_PROGRAM);
return true;
}
bool Output::Close()
{
Stop();
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (videofd > -1) {
close(videofd);
videofd = -1;
}
if (audiofd > -1) {
close(audiofd);
audiofd = -1;
}
videoTrack = NULL;
audioTrack = NULL;
return true;
}
bool Output::Play()
{
bool ret = true;
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
AVCodecContext *avcc;
if (videoTrack && videoTrack->stream && videofd > -1 && (avcc = videoTrack->stream->codec)) {
videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type, videoTrack->ac3flags);
videoWriter->Init(videofd, videoTrack->stream, player);
if (dioctl(videofd, VIDEO_SET_ENCODING, videoWriter->GetVideoEncoding(avcc->codec_id))
|| dioctl(videofd, VIDEO_PLAY, NULL))
ret = false;
}
if (audioTrack && audioTrack->stream && audiofd > -1 && (avcc = audioTrack->stream->codec)) {
audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type, audioTrack->ac3flags);
audioWriter->Init(audiofd, audioTrack->stream, player);
audio_encoding_t audioEncoding = AUDIO_ENCODING_LPCMA;
if (audioTrack->ac3flags != 6)
audioEncoding = audioWriter->GetAudioEncoding(avcc->codec_id);
if (dioctl(audiofd, AUDIO_SET_ENCODING, audioEncoding)
|| dioctl(audiofd, AUDIO_PLAY, NULL))
ret = false;
}
return ret;
}
bool Output::Stop()
{
bool ret = true;
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (videofd > -1) {
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
/* set back to normal speed (end trickmodes) */
dioctl(videofd, VIDEO_SET_SPEED, DVB_SPEED_NORMAL_PLAY);
if (dioctl(videofd, VIDEO_STOP, NULL))
ret = false;
}
if (audiofd > -1) {
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
/* set back to normal speed (end trickmodes) */
dioctl(audiofd, AUDIO_SET_SPEED, DVB_SPEED_NORMAL_PLAY);
if (dioctl(audiofd, AUDIO_STOP, NULL))
ret = false;
}
return ret;
}
bool Output::Pause()
{
bool ret = true;
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (videofd > -1) {
if (dioctl(videofd, VIDEO_FREEZE, NULL))
ret = false;
}
if (audiofd > -1) {
if (dioctl(audiofd, AUDIO_PAUSE, NULL))
ret = false;
}
return ret;
}
bool Output::Continue()
{
bool ret = true;
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (videofd > -1 && dioctl(videofd, VIDEO_CONTINUE, NULL))
ret = false;
if (audiofd > -1 && dioctl(audiofd, AUDIO_CONTINUE, NULL))
ret = false;
return ret;
}
bool Output::Mute(bool b)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
//AUDIO_SET_MUTE has no effect with new player
return audiofd > -1 && !dioctl(audiofd, b ? AUDIO_STOP : AUDIO_PLAY, NULL);
}
bool Output::Flush()
{
bool ret = true;
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (videofd > -1 && ioctl(videofd, VIDEO_FLUSH, NULL))
ret = false;
if (audiofd > -1 && audioWriter) {
// flush audio decoder
AVPacket packet;
packet.data = NULL;
packet.size = 0;
audioWriter->Write(&packet, 0);
if (ioctl(audiofd, AUDIO_FLUSH, NULL))
ret = false;
}
return ret;
}
bool Output::FastForward(int speed)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
return videofd > -1 && !dioctl(videofd, VIDEO_FAST_FORWARD, speed);
}
bool Output::SlowMotion(int speed)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
return videofd > -1 && !dioctl(videofd, VIDEO_SLOWMOTION, speed);
}
bool Output::AVSync(bool b)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
return audiofd > -1 && !dioctl(audiofd, AUDIO_SET_AV_SYNC, b);
}
bool Output::ClearAudio()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
return audiofd > -1 && !ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
}
bool Output::ClearVideo()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
return videofd > -1 && !ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
}
bool Output::Clear()
{
bool aret = ClearAudio();
bool vret = ClearVideo();
return aret && vret;
}
bool Output::GetPts(int64_t &pts)
{
pts = 0;
return ((videofd > -1 && !ioctl(videofd, VIDEO_GET_PTS, (void *) &pts)) ||
(audiofd > -1 && !ioctl(audiofd, AUDIO_GET_PTS, (void *) &pts)));
}
bool Output::GetFrameCount(int64_t &framecount)
{
dvb_play_info_t playInfo;
if ((videofd > -1 && !dioctl(videofd, VIDEO_GET_PLAY_INFO, (void *) &playInfo)) ||
(audiofd > -1 && !dioctl(audiofd, AUDIO_GET_PLAY_INFO, (void *) &playInfo))) {
framecount = playInfo.frame_count;
return true;
}
return false;
}
bool Output::SwitchAudio(Track *track)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
if (audioTrack && track->stream == audioTrack->stream)
return true;
if (audiofd > -1) {
dioctl(audiofd, AUDIO_STOP, NULL);
ioctl(audiofd, AUDIO_CLEAR_BUFFER, NULL);
}
audioTrack = track;
if (track->stream) {
AVCodecContext *avcc = track->stream->codec;
if (!avcc)
return false;
audioWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type, audioTrack->ac3flags);
audioWriter->Init(audiofd, audioTrack->stream, player);
if (audiofd > -1) {
audio_encoding_t audioEncoding = AUDIO_ENCODING_LPCMA;
if (audioTrack->ac3flags != 6)
audioEncoding = Writer::GetAudioEncoding(avcc->codec_id);
dioctl(audiofd, AUDIO_SET_ENCODING, audioEncoding);
dioctl(audiofd, AUDIO_PLAY, NULL);
}
}
return true;
}
bool Output::SwitchVideo(Track *track)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
if (videoTrack && track->stream == videoTrack->stream)
return true;
if (videofd > -1) {
dioctl(videofd, VIDEO_STOP, NULL);
ioctl(videofd, VIDEO_CLEAR_BUFFER, NULL);
}
videoTrack = track;
if (track->stream) {
AVCodecContext *avcc = track->stream->codec;
if (!avcc)
return false;
videoWriter = Writer::GetWriter(avcc->codec_id, avcc->codec_type, videoTrack->type);
videoWriter->Init(videofd, videoTrack->stream, player);
if (videofd > -1) {
dioctl(videofd, VIDEO_SET_ENCODING, Writer::GetVideoEncoding(avcc->codec_id));
dioctl(videofd, VIDEO_PLAY, NULL);
}
}
return true;
}
bool Output::Write(AVStream *stream, AVPacket *packet, int64_t pts)
{
switch (stream->codec->codec_type) {
case AVMEDIA_TYPE_VIDEO: {
OpenThreads::ScopedLock<OpenThreads::Mutex> v_lock(videoMutex);
return videofd > -1 && videoWriter && videoWriter->Write(packet, pts);
}
case AVMEDIA_TYPE_AUDIO: {
OpenThreads::ScopedLock<OpenThreads::Mutex> a_lock(audioMutex);
return audiofd > -1 && audioWriter && audioWriter->Write(packet, pts);
}
default:
return false;
}
}

View File

@@ -1,446 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 duckbox
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sstream>
#include "player.h"
#include "misc.h"
static const char *FILENAME = "eplayer/player.cpp";
#define cMaxSpeed_ff 128 /* fixme: revise */
#define cMaxSpeed_fr -320 /* fixme: revise */
Player::Player()
{
input.player = this;
output.player = this;
manager.player = this;
hasThreadStarted = false;
isPaused = false;
isPlaying = false;
isForwarding = false;
isBackWard = false;
isSlowMotion = false;
Speed = 0;
}
void *Player::playthread(void *arg)
{
char threadname[17];
strncpy(threadname, __func__, sizeof(threadname));
threadname[16] = 0;
prctl(PR_SET_NAME, (unsigned long) threadname);
Player *player = (Player *) arg;
player->hasThreadStarted = true;
player->input.Play();
player->hasThreadStarted = false;
player->Stop();
pthread_exit(NULL);
}
bool Player::Open(const char *Url, bool _noprobe, std::string headers)
{
fprintf(stderr, "URL=%s\n", Url);
isHttp = false;
noprobe = _noprobe;
abortRequested = false;
manager.clearTracks();
if (!strncmp("mms://", Url, 6)) {
url = "mmst";
url += Url + 3;
isHttp = true;
} else if (strstr(Url, "://")) {
url = Url;
isHttp = strncmp("file://", Url, 7);
} else if (!strncmp(Url, "bluray:/", 8)) {
url = Url;
} else {
fprintf(stderr, "%s %s %d: Unknown stream (%s)\n", FILENAME, __func__, __LINE__, Url);
return false;
}
return input.Init(url.c_str(), headers);
}
bool Player::Close()
{
isPaused = false;
isPlaying = false;
isForwarding = false;
isBackWard = false;
isSlowMotion = false;
Speed = 0;
url.clear();
return true;
}
bool Player::Play()
{
bool ret = true;
if (!isPlaying) {
output.AVSync(true);
ret = output.Play();
if (ret) {
isPlaying = true;
isPaused = false;
isForwarding = false;
if (isBackWard) {
isBackWard = false;
output.Mute(false);
}
isSlowMotion = false;
Speed = 1;
if (!hasThreadStarted) {
int err = pthread_create(&playThread, NULL, playthread, this);
if (err) {
fprintf(stderr, "%s %s %d: pthread_create: %d (%s)\n", FILENAME, __func__, __LINE__, err, strerror(err));
ret = false;
isPlaying = false;
} else {
pthread_detach(playThread);
}
}
}
} else {
fprintf(stderr,"playback already running\n");
ret = false;
}
return ret;
}
bool Player::Pause()
{
bool ret = true;
if (isPlaying && !isPaused) {
if (isSlowMotion)
output.Clear();
output.Pause();
isPaused = true;
//isPlaying = 1;
isForwarding = false;
if (isBackWard) {
isBackWard = false;
output.Mute(false);
}
isSlowMotion = false;
Speed = 1;
} else {
fprintf(stderr,"playback not playing or already in pause mode\n");
ret = false;
}
return ret;
}
bool Player::Continue()
{
int ret = true;
if (isPlaying && (isPaused || isForwarding || isBackWard || isSlowMotion)) {
if (isSlowMotion)
output.Clear();
output.Continue();
isPaused = false;
//isPlaying = 1;
isForwarding = false;
if (isBackWard) {
isBackWard = false;
output.Mute(false);
}
isSlowMotion = false;
Speed = 1;
} else {
fprintf(stderr,"continue not possible\n");
ret = false;
}
return ret;
}
bool Player::Stop()
{
bool ret = true;
if (isPlaying) {
isPaused = false;
isPlaying = false;
isForwarding = false;
if (isBackWard) {
isBackWard = false;
output.Mute(false);
}
isSlowMotion = false;
Speed = 0;
output.Stop();
input.Stop();
} else {
fprintf(stderr,"stop not possible\n");
ret = false;
}
while (hasThreadStarted)
usleep(100000);
return ret;
}
bool Player::FastForward(int speed)
{
int ret = true;
/* Audio only forwarding not supported */
if (input.videoTrack && !isHttp && !isBackWard && (!isPaused || isPlaying)) {
if ((speed <= 0) || (speed > cMaxSpeed_ff)) {
fprintf(stderr, "speed %d out of range (1 - %d) \n", speed, cMaxSpeed_ff);
return false;
}
isForwarding = 1;
Speed = speed;
output.FastForward(speed);
} else {
fprintf(stderr,"fast forward not possible\n");
ret = false;
}
return ret;
}
bool Player::FastBackward(int speed)
{
bool ret = true;
/* Audio only reverse play not supported */
if (input.videoTrack && !isForwarding && (!isPaused || isPlaying)) {
if ((speed > 0) || (speed < cMaxSpeed_fr)) {
fprintf(stderr, "speed %d out of range (0 - %d) \n", speed, cMaxSpeed_fr);
return false;
}
if (speed == 0) {
isBackWard = false;
Speed = 0; /* reverse end */
} else {
Speed = speed;
isBackWard = true;
}
output.Clear();
#if 0
if (output->Command(player, OUTPUT_REVERSE, NULL) < 0) {
fprintf(stderr,"OUTPUT_REVERSE failed\n");
isBackWard = false;
Speed = 1;
ret = false;
}
#endif
} else {
fprintf(stderr,"fast backward not possible\n");
ret = false;
}
if (isBackWard)
output.Mute(true);
return ret;
}
bool Player::SlowMotion(int repeats)
{
if (input.videoTrack && !isHttp && isPlaying) {
if (isPaused)
Continue();
switch (repeats) {
case 2:
case 4:
case 8:
isSlowMotion = true;
break;
default:
repeats = 0;
}
output.SlowMotion(repeats);
return true;
}
fprintf(stderr, "slowmotion not possible\n");
return false;
}
bool Player::Seek(int64_t pos, bool absolute)
{
output.Clear();
return input.Seek(pos, absolute);
}
bool Player::GetPts(int64_t &pts)
{
pts = INVALID_PTS_VALUE;
return isPlaying && output.GetPts(pts);
}
bool Player::GetFrameCount(int64_t &frameCount)
{
return isPlaying && output.GetFrameCount(frameCount);
}
bool Player::GetDuration(int64_t &duration)
{
duration = -1;
return isPlaying && input.GetDuration(duration);
}
bool Player::SwitchVideo(int pid)
{
Track *track = manager.getVideoTrack(pid);
return input.SwitchVideo(track);
}
bool Player::SwitchAudio(int pid)
{
Track *track = manager.getAudioTrack(pid);
return input.SwitchAudio(track);
}
bool Player::SwitchSubtitle(int pid)
{
Track *track = manager.getSubtitleTrack(pid);
return input.SwitchSubtitle(track);
}
bool Player::SwitchTeletext(int pid)
{
Track *track = manager.getTeletextTrack(pid);
return input.SwitchTeletext(track);
}
bool Player::GetMetadata(std::vector<std::string> &keys, std::vector<std::string> &values)
{
return input.GetMetadata(keys, values);
}
bool Player::GetChapters(std::vector<int> &positions, std::vector<std::string> &titles)
{
positions.clear();
titles.clear();
input.UpdateTracks();
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(chapterMutex);
for (std::vector<Chapter>::iterator it = chapters.begin(); it != chapters.end(); ++it) {
positions.push_back(it->start/1000);
titles.push_back(it->title);
}
return true;
}
void Player::SetChapters(std::vector<Chapter> &Chapters)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(chapterMutex);
chapters = Chapters;
}
void Player::RequestAbort()
{
abortRequested = true;
}
int Player::GetVideoPid()
{
Track *track = input.videoTrack;
return track ? track->pid : 0;
}
int Player::GetAudioPid()
{
Track *track = input.audioTrack;
return track ? track->pid : 0;
}
int Player::GetSubtitlePid()
{
Track *track = input.subtitleTrack;
return track ? track->pid : 0;
}
int Player::GetTeletextPid()
{
Track *track = input.teletextTrack;
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()));
}

View File

@@ -1,185 +0,0 @@
/*
* linuxdvb output/writer handling.
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 DboxOldie (based on code from libeplayer3)
* inspired by martii
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include <algorithm>
#include "misc.h"
#include "pes.h"
#include "writer.h"
#define AAC_HEADER_LENGTH 7
#define AAC_DEBUG 0
#if AAC_DEBUG
static inline void Hexdump(unsigned char *Data, int length)
{
int k;
for (k = 0; k < length; k++) {
printf("%02x ", Data[k]);
if (((k + 1) & 31) == 0)
printf("\n");
}
printf("\n");
}
#endif
static inline int aac_get_sample_rate_index(uint32_t sample_rate)
{
if (96000 <= sample_rate)
return 0;
else if (88200 <= sample_rate)
return 1;
else if (64000 <= sample_rate)
return 2;
else if (48000 <= sample_rate)
return 3;
else if (44100 <= sample_rate)
return 4;
else if (32000 <= sample_rate)
return 5;
else if (24000 <= sample_rate)
return 6;
else if (22050 <= sample_rate)
return 7;
else if (16000 <= sample_rate)
return 8;
else if (12000 <= sample_rate)
return 9;
else if (11025 <= sample_rate)
return 10;
else if (8000 <= sample_rate)
return 11;
else if (7350 <= sample_rate)
return 12;
else
return 13;
}
#if 0
static unsigned char DefaultAACHeader[] = {0xff,0xf1,0x50,0x80,0x00,0x1f,0xfc};
#endif
class WriterAAC : public Writer
{
private:
uint8_t aacbuf[8];
unsigned int aacbuflen;
AVStream *stream;
public:
bool Write(AVPacket *packet, int64_t pts);
void Init(int _fd, AVStream *_stream, Player *_player);
WriterAAC();
};
void WriterAAC::Init(int _fd, AVStream *_stream, Player *_player)
{
fd = _fd;
stream = _stream;
player = _player;
#if AAC_DEBUG
printf("Create AAC ExtraData\n");
printf("stream->codec->extradata_size %d\n", stream->codec->extradata_size);
Hexdump(stream->codec->extradata, stream->codec->extradata_size);
#endif
unsigned int object_type = 2; // LC
unsigned int sample_index = aac_get_sample_rate_index(stream->codec->sample_rate);
unsigned int chan_config = stream->codec->channels;
if (stream->codec->extradata_size >= 2)
{
object_type = stream->codec->extradata[0] >> 3;
sample_index = ((stream->codec->extradata[0] & 0x7) << 1) + (stream->codec->extradata[1] >> 7);
chan_config = (stream->codec->extradata[1] >> 3) && 0xf;
}
#if AAC_DEBUG
printf("aac object_type %d\n", object_type);
printf("aac sample_index %d\n", sample_index);
printf("aac chan_config %d\n", chan_config);
#endif
object_type -= 1; // Cause of ADTS
aacbuflen = AAC_HEADER_LENGTH;
aacbuf[0] = 0xFF;
aacbuf[1] = 0xF1;
aacbuf[2] = ((object_type & 0x03) << 6) | (sample_index << 2) | ((chan_config >> 2) & 0x01);
aacbuf[3] = (chan_config & 0x03) << 6;
aacbuf[4] = 0x00;
aacbuf[5] = 0x1F;
aacbuf[6] = 0xFC;
aacbuf[7] = 0x00;
#if AAC_DEBUG
printf("AAC_HEADER -> ");
Hexdump(aacbuf, 7);
#endif
}
bool WriterAAC::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
uint8_t ExtraData[AAC_HEADER_LENGTH];
for (int pos = 0; pos < packet->size + AAC_HEADER_LENGTH; )
{
int PacketLength = std::min(packet->size - pos + AAC_HEADER_LENGTH, MAX_PES_PACKET_SIZE);
memcpy(ExtraData, aacbuf, AAC_HEADER_LENGTH);
// ExtraData[3] |= (PacketLength >> 11) & 0x3;
ExtraData[4] = (PacketLength >> 3) & 0xff;
ExtraData[5] |= (PacketLength << 5) & 0xe0;
struct iovec iov[3];
iov[0].iov_base = PesHeader;
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, AAC_AUDIO_PES_START_CODE, pts, 0);
iov[1].iov_base = ExtraData;
iov[1].iov_len = AAC_HEADER_LENGTH;
iov[2].iov_base = packet->data;
iov[2].iov_len = packet->size;
ssize_t l = writev(fd, iov, 3);
#if AAC_DEBUG
// printf("Packet Size + AAC_HEADER_LENGTH= %d Packet Size= %d Written= %d\n", PacketLength, packet->size, l);
#endif
if (l < 0)
return false;
pos += PacketLength;
pts = INVALID_PTS_VALUE;
}
return true;
}
WriterAAC::WriterAAC()
{
Register(this, AV_CODEC_ID_AAC, AUDIO_ENCODING_AAC);
}
static WriterAAC writer_aac __attribute__ ((init_priority (300)));

View File

@@ -1,72 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include <algorithm>
#include "misc.h"
#include "pes.h"
#include "writer.h"
class WriterAC3 : public Writer
{
public:
bool Write(AVPacket *packet, int64_t pts);
WriterAC3();
};
bool WriterAC3::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
for (int pos = 0; pos < packet->size; ) {
int PacketLength = std::min(packet->size - pos, MAX_PES_PACKET_SIZE);
struct iovec iov[2];
iov[0].iov_base = PesHeader;
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, PRIVATE_STREAM_1_PES_START_CODE, pts, 0);
iov[1].iov_base = packet->data + pos;
iov[1].iov_len = PacketLength;
ssize_t l = writev(fd, iov, 2);
if (l < 0)
return false;
pos += PacketLength;
pts = INVALID_PTS_VALUE;
}
return true;
}
WriterAC3::WriterAC3()
{
Register(this, AV_CODEC_ID_AC3, AUDIO_ENCODING_AC3);
Register(this, AV_CODEC_ID_EAC3, AUDIO_ENCODING_AC3);
}
static WriterAC3 writer_ac3 __attribute__ ((init_priority (300)));

View File

@@ -1,110 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <errno.h>
#include "misc.h"
#include "pes.h"
#include "writer.h"
class WriterDIVX : public Writer
{
private:
bool initialHeader;
AVStream *stream;
public:
bool Write(AVPacket *packet, int64_t pts);
void Init(int fd, AVStream *_stream, Player *player);
WriterDIVX();
};
void WriterDIVX::Init(int _fd, AVStream *_stream, Player *_player)
{
fd = _fd;
stream = _stream;
player = _player;
initialHeader = true;
}
bool WriterDIVX::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
uint8_t FakeHeaders[64] = { 0 }; // 64bytes should be enough to make the fake headers
unsigned int FakeHeaderLength;
uint8_t Version = 5;
unsigned int FakeStartCode = (Version << 8) | PES_VERSION_FAKE_START_CODE;
BitPacker_t ld = { FakeHeaders, 0, 32 };
unsigned int usecPerFrame = av_rescale(AV_TIME_BASE, stream->r_frame_rate.den, stream->r_frame_rate.num);
/* Create info record for frame parser */
/* divx4 & 5
VOS
PutBits(&ld, 0x0, 8);
PutBits(&ld, 0x0, 8);
*/
PutBits(&ld, 0x1b0, 32); // startcode
PutBits(&ld, 0, 8); // profile = reserved
PutBits(&ld, 0x1b2, 32); // startcode (user data)
PutBits(&ld, 0x53545443, 32); // STTC - an embedded ST timecode from an avi file
PutBits(&ld, usecPerFrame, 32); // microseconds per frame
FlushBits(&ld);
FakeHeaderLength = (ld.Ptr - FakeHeaders);
struct iovec iov[4];
int ic = 0;
iov[ic].iov_base = PesHeader;
iov[ic++].iov_len = InsertPesHeader(PesHeader, packet->size, MPEG_VIDEO_PES_START_CODE, pts, FakeStartCode);
iov[ic].iov_base = FakeHeaders;
iov[ic++].iov_len = FakeHeaderLength;
if (initialHeader) {
iov[ic].iov_base = stream->codec->extradata;
iov[ic++].iov_len = stream->codec->extradata_size;
initialHeader = false;
}
iov[ic].iov_base = packet->data;
iov[ic++].iov_len = packet->size;
return writev(fd, iov, ic) > -1;
}
WriterDIVX::WriterDIVX()
{
Register(this, AV_CODEC_ID_MPEG4, VIDEO_ENCODING_MPEG4P2);
Register(this, AV_CODEC_ID_MSMPEG4V1, VIDEO_ENCODING_MPEG4P2);
Register(this, AV_CODEC_ID_MSMPEG4V2, VIDEO_ENCODING_MPEG4P2);
Register(this, AV_CODEC_ID_MSMPEG4V3, VIDEO_ENCODING_MPEG4P2);
}
static WriterDIVX writer_divx __attribute__ ((init_priority (300)));

View File

@@ -1,82 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include "misc.h"
#include "pes.h"
#include "writer.h"
#define PES_AUDIO_PRIVATE_HEADER_SIZE 16 // consider maximum private header size.
#define PES_AUDIO_HEADER_SIZE (32 + PES_AUDIO_PRIVATE_HEADER_SIZE)
class WriterDTS : public Writer
{
public:
bool Write(AVPacket *packet, int64_t pts);
WriterDTS();
};
bool WriterDTS::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_AUDIO_HEADER_SIZE];
// #define DO_BYTESWAP
#ifdef DO_BYTESWAP
uint8_t Data[packet->size];
memcpy(Data, packet->data, packet->size);
/* 16-bit byte swap all data before injecting it */
for (i = 0; i < packet->size; i += 2) {
uint8_t Tmp = Data[i];
Data[i] = Data[i + 1];
Data[i + 1] = Tmp;
}
#endif
struct iovec iov[2];
iov[0].iov_base = PesHeader;
iov[0].iov_len = InsertPesHeader(PesHeader, packet->size, MPEG_AUDIO_PES_START_CODE /*PRIVATE_STREAM_1_PES_START_CODE */ , pts, 0);
#ifdef DO_BYTESPWAP
iov[1].iov_base = Data;
#else
iov[1].iov_base = packet->data;
#endif
iov[1].iov_len = packet->size;
return writev(fd, iov, 2) > -1;
}
WriterDTS::WriterDTS()
{
Register(this, AV_CODEC_ID_DTS, AUDIO_ENCODING_DTS);
}
static WriterDTS writer_dts __attribute__ ((init_priority (300)));

View File

@@ -1,74 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 crow
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/uio.h>
#include "misc.h"
#include "pes.h"
#include "writer.h"
class WriterH263 : public Writer
{
public:
bool Write(AVPacket *packet, int64_t pts);
WriterH263();
};
bool WriterH263::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
int HeaderLength = InsertPesHeader(PesHeader, packet->size, H263_VIDEO_PES_START_CODE, pts, 0);
int PrivateHeaderLength = InsertVideoPrivateDataHeader(&PesHeader[HeaderLength], packet->size);
int PesLength = PesHeader[PES_LENGTH_BYTE_0] + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength;
PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff;
PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff;
PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength;
PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT;
HeaderLength += PrivateHeaderLength;
struct iovec iov[2];
iov[0].iov_base = PesHeader;
iov[0].iov_len = HeaderLength;
iov[1].iov_base = packet->data;
iov[1].iov_len = packet->size;
return writev(fd, iov, 2) > -1;
}
WriterH263::WriterH263()
{
Register(this, AV_CODEC_ID_H263, VIDEO_ENCODING_H263);
Register(this, AV_CODEC_ID_H263P, VIDEO_ENCODING_H263);
Register(this, AV_CODEC_ID_H263I, VIDEO_ENCODING_H263);
Register(this, AV_CODEC_ID_FLV1, VIDEO_ENCODING_FLV1);
}
static WriterH263 writer_h263 __attribute__ ((init_priority (300)));

View File

@@ -1,258 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include "misc.h"
#include "pes.h"
#include "writer.h"
#define NALU_TYPE_PLAYER2_CONTAINER_PARAMETERS 24 // Reference: player/standards/h264.h
#define CONTAINER_PARAMETERS_VERSION 0x00
typedef struct avcC_s {
uint8_t Version; // configurationVersion
uint8_t Profile; // AVCProfileIndication
uint8_t Compatibility; // profile_compatibility
uint8_t Level; // AVCLevelIndication
uint8_t NalLengthMinusOne; // held in bottom two bits
uint8_t NumParamSets; // held in bottom 5 bits
uint8_t Params[1]; // {length,params}{length,params}...sequence then picture
} avcC_t;
class WriterH264 : public Writer
{
private:
bool initialHeader;
unsigned int NalLengthBytes;
AVStream *stream;
public:
bool Write(AVPacket *packet, int64_t pts);
void Init(int _fd, AVStream *_stream, Player *_player);
WriterH264();
};
void WriterH264::Init(int _fd, AVStream *_stream, Player *_player)
{
fd = _fd;
stream = _stream;
player = _player;
initialHeader = true;
NalLengthBytes = 1;
}
bool WriterH264::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
struct iovec iov[512];
uint8_t *d = packet->data;
// 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;
iov[ic++].iov_len = stream->codec->extradata_size;
len += stream->codec->extradata_size;
}
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;
if (!avcCHeader) {
fprintf(stderr, "stream->codec->extradata == NULL\n");
return false;
}
if (avcCHeader->Version != 1)
fprintf(stderr, "Error unknown avcC version (%x). Expect problems.\n", avcCHeader->Version);
// 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;
ic = 0;
iov[ic++].iov_base = PesHeader;
NalLengthBytes = (avcCHeader->NalLengthMinusOne & 0x03) + 1;
unsigned int ParamOffset = 0;
len = 0;
// 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];
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;
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];
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;
len += PsLength;
ParamOffset += PsLength + 2;
}
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;
initialHeader = false;
}
uint8_t *de = d + packet->size;
do {
unsigned int len = 0;
switch (NalLengthBytes) {
case 4:
len = *d;
d++;
case 3:
len <<= 8;
len |= *d;
d++;
case 2:
len <<= 8;
len |= *d;
d++;
default:
len <<= 8;
len |= *d;
d++;
}
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;
}
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()
{
Register(this, AV_CODEC_ID_H264, VIDEO_ENCODING_H264);
}
static WriterH264 writerh264 __attribute__ ((init_priority (300)));

View File

@@ -1,88 +0,0 @@
/*
* LinuxDVB Output handling.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <memory.h>
#include <asm/types.h>
#include <errno.h>
#include "misc.h"
void PutBits(BitPacker_t * ld, unsigned int code, unsigned int length)
{
unsigned int bit_buf;
unsigned int bit_left;
bit_buf = ld->BitBuffer;
bit_left = ld->Remaining;
#ifdef DEBUG_PUTBITS
if (ld->debug)
dprintf("code = %d, length = %d, bit_buf = 0x%x, bit_left = %d\n",
code, length, bit_buf, bit_left);
#endif
if (length < bit_left) {
/* fits into current buffer */
bit_buf = (bit_buf << length) | code;
bit_left -= length;
} else {
/* doesn't fit */
bit_buf <<= bit_left;
bit_buf |= code >> (length - bit_left);
ld->Ptr[0] = (uint8_t) (bit_buf >> 24);
ld->Ptr[1] = (uint8_t) (bit_buf >> 16);
ld->Ptr[2] = (uint8_t) (bit_buf >> 8);
ld->Ptr[3] = (uint8_t) bit_buf;
ld->Ptr += 4;
length -= bit_left;
bit_buf = code & ((1 << length) - 1);
bit_left = 32 - length;
bit_buf = code;
}
#ifdef DEBUG_PUTBITS
if (ld->debug)
dprintf("bit_left = %d, bit_buf = 0x%x\n", bit_left, bit_buf);
#endif
/* writeback */
ld->BitBuffer = bit_buf;
ld->Remaining = bit_left;
}
void FlushBits(BitPacker_t * ld)
{
ld->BitBuffer <<= ld->Remaining;
while (ld->Remaining < 32) {
#ifdef DEBUG_PUTBITS
if (ld->debug)
dprintf("flushing 0x%2.2x\n", ld->BitBuffer >> 24);
#endif
*ld->Ptr++ = ld->BitBuffer >> 24;
ld->BitBuffer <<= 8;
ld->Remaining += 8;
}
ld->Remaining = 32;
ld->BitBuffer = 0;
}

View File

@@ -1,74 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include <algorithm>
#include "misc.h"
#include "pes.h"
#include "writer.h"
class WriterMP3 : public Writer
{
public:
bool Write(AVPacket *packet, int64_t pts);
WriterMP3();
};
bool WriterMP3::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
for (int pos = 0; pos < packet->size; ) {
int PacketLength = std::min(packet->size - pos, MAX_PES_PACKET_SIZE);
struct iovec iov[2];
iov[0].iov_base = PesHeader;
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, MPEG_AUDIO_PES_START_CODE, pts, 0);
iov[1].iov_base = packet->data + pos;
iov[1].iov_len = PacketLength;
ssize_t l = writev(fd, iov, 2);
if (l < 0)
return false;
pos += PacketLength;
pts = INVALID_PTS_VALUE;
}
return true;
}
WriterMP3::WriterMP3()
{
Register(this, AV_CODEC_ID_MP3, AUDIO_ENCODING_MP3);
Register(this, AV_CODEC_ID_MP2, AUDIO_ENCODING_MPEG2);
// Register(this, AV_CODEC_ID_VORBIS, AUDIO_ENCODING_VORBIS);
Register(this, AV_CODEC_ID_FLAC, AUDIO_ENCODING_LPCM);
}
static WriterMP3 writer_mp3 __attribute__ ((init_priority (300)));

View File

@@ -1,71 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include <algorithm>
#include "misc.h"
#include "pes.h"
#include "writer.h"
class WriterMPEG2 : public Writer
{
public:
bool Write(AVPacket *packet, int64_t pts);
WriterMPEG2();
};
bool WriterMPEG2::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
for (int pos = 0; pos < packet->size; ) {
int PacketLength = std::min(packet->size - pos, MAX_PES_PACKET_SIZE);
struct iovec iov[2];
iov[0].iov_base = PesHeader;
iov[0].iov_len = InsertPesHeader(PesHeader, PacketLength, MPEG_VIDEO_PES_START_CODE, pts, 0);
iov[1].iov_base = packet->data + pos;
iov[1].iov_len = PacketLength;
ssize_t l = writev(fd, iov, 2);
if (l < 0)
return false;
pos += PacketLength;
pts = INVALID_PTS_VALUE;
}
return true;
}
WriterMPEG2::WriterMPEG2()
{
Register(this, AV_CODEC_ID_MPEG2TS, VIDEO_ENCODING_AUTO);
}
static WriterMPEG2 writer_mpeg2 __attribute__ ((init_priority (300)));

View File

@@ -1,376 +0,0 @@
/*
* linuxdvb output/writer handling.
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <linux/dvb/audio.h>
#include "misc.h"
#include "pes.h"
#include "writer.h"
#include "player.h"
extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
// reference: search for TypeLpcmDVDAudio in player/frame_parser/frame_parser_audio_lpcm.cpp
static const uint8_t clpcm_prv[14] = {
0xA0, //sub_stream_id
0, 0, //resvd and UPC_EAN_ISRC stuff, unused
0x0A, //private header length
0, 9, //first_access_unit_pointer
0x00, //emph,rsvd,stereo,downmix
0x0F, //quantisation word length 1,2
0x0F, //audio sampling freqency 1,2
0, //resvd, multi channel type
0, //bit shift on channel GR2, assignment
0x80, //dynamic range control
0, 0 //resvd for copyright management
};
class WriterPCM : public Writer
{
private:
unsigned int SubFrameLen;
unsigned int SubFramesPerPES;
uint8_t lpcm_prv[14];
uint8_t injectBuffer[2048];
uint8_t breakBuffer[sizeof(injectBuffer)];
uint8_t *output;
uint8_t out_samples_max;
unsigned int breakBufferFillSize;
int uNoOfChannels;
int uSampleRate;
int uBitsPerSample;
AVStream *stream;
SwrContext *swr;
AVFrame *decoded_frame;
int out_sample_rate;
int out_channels;
uint64_t out_channel_layout;
bool initialHeader;
bool restart_audio_resampling;
public:
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, Player *_player);
WriterPCM();
};
bool WriterPCM::prepareClipPlay()
{
SubFrameLen = 0;
SubFramesPerPES = 0;
breakBufferFillSize = 0;
memcpy(lpcm_prv, clpcm_prv, sizeof(lpcm_prv));
// figure out size of subframe and set up sample rate
switch (uSampleRate) {
case 48000:
SubFrameLen = 40;
break;
case 96000:
lpcm_prv[8] |= 0x10;
SubFrameLen = 80;
break;
case 192000:
lpcm_prv[8] |= 0x20;
SubFrameLen = 160;
break;
case 44100:
lpcm_prv[8] |= 0x80;
SubFrameLen = 40;
break;
case 88200:
lpcm_prv[8] |= 0x90;
SubFrameLen = 80;
break;
case 176400:
lpcm_prv[8] |= 0xA0;
SubFrameLen = 160;
break;
default:
break;
}
SubFrameLen *= uNoOfChannels;
SubFrameLen *= uBitsPerSample / 8;
//rewrite PES size to have as many complete subframes per PES as we can
SubFramesPerPES = ((sizeof(injectBuffer) - 14) - sizeof(lpcm_prv)) / SubFrameLen;
SubFrameLen *= SubFramesPerPES;
//set number of channels
lpcm_prv[10] = uNoOfChannels - 1;
switch (uBitsPerSample) {
case 24:
lpcm_prv[7] |= 0x20;
case 16:
break;
default:
printf("inappropriate bits per sample (%d) - must be 16 or 24\n", uBitsPerSample);
return false;
}
return true;
}
bool WriterPCM::writePCM(int64_t Pts, uint8_t *data, unsigned int size)
{
bool res = true;
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
if (initialHeader) {
initialHeader = false;
prepareClipPlay();
ioctl(fd, AUDIO_CLEAR_BUFFER, NULL);
}
size += breakBufferFillSize;
while (size >= SubFrameLen) {
if (breakBufferFillSize)
memcpy(injectBuffer, breakBuffer, breakBufferFillSize);
memcpy(injectBuffer + breakBufferFillSize, data, SubFrameLen - breakBufferFillSize);
size -= SubFrameLen;
data += SubFrameLen - breakBufferFillSize;
breakBufferFillSize = 0;
//write the PCM data
if (uBitsPerSample == 16) {
for (unsigned int n = 0; n < SubFrameLen; n += 2) {
uint8_t tmp = injectBuffer[n];
injectBuffer[n] = injectBuffer[n + 1];
injectBuffer[n + 1] = tmp;
}
} else {
// 0 1 2 3 4 5 6 7 8 9 10 11
// A1c A1b A1a B1c B1b B1a A2c A2b A2a B2c B2b B2a
// to A1a A1b B1a B1b A2a A2b B2a B2b A1c B1c A2c B2c
for (unsigned int n = 0; n < SubFrameLen; n += 12) {
uint8_t t, *p = injectBuffer + n;
t = p[0];
p[0] = p[2];
p[2] = p[5];
p[5] = p[7];
p[7] = p[11];
p[11] = p[9];
p[9] = p[3];
p[3] = p[4];
p[4] = p[8];
p[8] = t;
}
}
//increment err... subframe count?
lpcm_prv[1] = ((lpcm_prv[1] + SubFramesPerPES) & 0x1F);
struct iovec iov[3];
iov[0].iov_base = PesHeader;
iov[1].iov_base = lpcm_prv;
iov[1].iov_len = sizeof(lpcm_prv);
iov[2].iov_base = injectBuffer;
iov[2].iov_len = SubFrameLen;
iov[0].iov_len = InsertPesHeader(PesHeader, iov[1].iov_len + iov[2].iov_len, PCM_PES_START_CODE, Pts, 0);
int len = writev(fd, iov, 3);
if (len < 0) {
res = false;
break;
}
}
if (size && res) {
breakBufferFillSize = size;
memcpy(breakBuffer, data, size);
}
return res;
}
void WriterPCM::Init(int _fd, AVStream *_stream, Player *_player)
{
fd = _fd;
stream = _stream;
player = _player;
initialHeader = true;
restart_audio_resampling = true;
}
bool WriterPCM::Write(AVPacket *packet, int64_t pts)
{
if (!packet) {
restart_audio_resampling = true;
return true;
}
AVCodecContext *c = stream->codec;
if (restart_audio_resampling) {
restart_audio_resampling = false;
initialHeader = true;
if (swr) {
swr_free(&swr);
swr = NULL;
}
if (decoded_frame) {
av_frame_free(&decoded_frame);
decoded_frame = NULL;
}
AVCodec *codec = avcodec_find_decoder(c->codec_id);
if (!codec) {
fprintf(stderr, "%s %d: avcodec_find_decoder(%llx)\n", __func__, __LINE__, (unsigned long long) c->codec_id);
return false;
}
avcodec_close(c);
if (avcodec_open2(c, codec, NULL)) {
fprintf(stderr, "%s %d: avcodec_open2 failed\n", __func__, __LINE__);
return false;
}
}
if (!swr) {
int in_rate = c->sample_rate;
// rates in descending order
int rates[] = {192000, 176400, 96000, 88200, 48000, 44100, 0};
int i = 0;
// find the next equal or smallest rate
while (rates[i] && in_rate < rates[i])
i++;
out_sample_rate = rates[i] ? rates[i] : 44100;
out_channels = c->channels;
if (c->channel_layout == 0) {
// FIXME -- need to guess, looks pretty much like a bug in the FFMPEG WMA decoder
c->channel_layout = AV_CH_LAYOUT_STEREO;
}
out_channel_layout = c->channel_layout;
// player2 won't play mono
if (out_channel_layout == AV_CH_LAYOUT_MONO) {
out_channel_layout = AV_CH_LAYOUT_STEREO;
out_channels = 2;
}
uSampleRate = out_sample_rate;
uNoOfChannels = av_get_channel_layout_nb_channels(out_channel_layout);
uBitsPerSample = 16;
swr = swr_alloc();
if (!swr) {
fprintf(stderr, "%s %d: swr_alloc failed\n", __func__, __LINE__);
return false;
}
av_opt_set_int(swr, "in_channel_layout", c->channel_layout, 0);
av_opt_set_int(swr, "out_channel_layout", out_channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", c->sample_rate, 0);
av_opt_set_int(swr, "out_sample_rate", out_sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", c->sample_fmt, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
int e = swr_init(swr);
if (e < 0) {
fprintf(stderr, "swr_init: %d (icl=%d ocl=%d isr=%d osr=%d isf=%d osf=%d)\n",
-e, (int) c->channel_layout,
(int) out_channel_layout, c->sample_rate, out_sample_rate, c->sample_fmt, AV_SAMPLE_FMT_S16);
restart_audio_resampling = true;
return false;
}
}
unsigned int packet_size = packet->size;
while (packet_size > 0 || (!packet_size && !packet->data)) {
int got_frame = 0;
if (!decoded_frame) {
if (!(decoded_frame = av_frame_alloc())) {
fprintf(stderr, "out of memory\n");
exit(1);
}
} else
av_frame_unref(decoded_frame);
int len = avcodec_decode_audio4(c, decoded_frame, &got_frame, packet);
if (len < 0) {
restart_audio_resampling = true;
break;
}
if (packet->data)
packet_size -= len;
if (!got_frame) {
if (!packet->data || !packet_size)
break;
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) {
if (output)
av_freep(&output);
int e = av_samples_alloc(&output, NULL, out_channels, out_samples, AV_SAMPLE_FMT_S16, 1);
if (e < 0) {
fprintf(stderr, "av_samples_alloc: %d\n", -e);
break;
}
out_samples_max = out_samples;
}
out_samples = swr_convert(swr, &output, out_samples, (const uint8_t **) &decoded_frame->data[0], in_samples);
if (!writePCM(pts, output, out_samples * sizeof(short) * out_channels)) {
restart_audio_resampling = true;
break;
}
}
return !packet_size;
}
WriterPCM::WriterPCM()
{
swr = NULL;
output = NULL;
out_samples_max = 0;
decoded_frame = av_frame_alloc();
Register(this, AV_CODEC_ID_INJECTPCM, AUDIO_ENCODING_LPCMA);
}
static WriterPCM writer_pcm __attribute__ ((init_priority (300)));

View File

@@ -1,116 +0,0 @@
/*
* linuxdvb output/writer handling.
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <memory.h>
#include <asm/types.h>
#include "misc.h"
#include "pes.h"
int InsertVideoPrivateDataHeader(uint8_t *data, int payload_size)
{
BitPacker_t ld2 = { data, 0, 32 };
int i;
PutBits(&ld2, PES_PRIVATE_DATA_FLAG, 8);
PutBits(&ld2, payload_size & 0xff, 8);
PutBits(&ld2, (payload_size >> 8) & 0xff, 8);
PutBits(&ld2, (payload_size >> 16) & 0xff, 8);
for (i = 4; i < (PES_PRIVATE_DATA_LENGTH + 1); i++)
PutBits(&ld2, 0, 8);
FlushBits(&ld2);
return PES_PRIVATE_DATA_LENGTH + 1;
}
int InsertPesHeader(uint8_t *data, int size, uint8_t stream_id, int64_t pts, int pic_start_code)
{
BitPacker_t ld2 = { data, 0, 32 };
/* if (size > MAX_PES_PACKET_SIZE)
size = 0; // unbounded */
PutBits(&ld2, 0x0, 8);
PutBits(&ld2, 0x0, 8);
PutBits(&ld2, 0x1, 8); // Start Code
PutBits(&ld2, stream_id, 8); // Stream_id = Audio Stream
//4
PutBits(&ld2, size + 3 + (pts != INVALID_PTS_VALUE ? 5 : 0) + (pic_start_code ? (5) : 0), 16); // PES_packet_length
//6 = 4+2
PutBits(&ld2, 0x2, 2); // 10
PutBits(&ld2, 0x0, 2); // PES_Scrambling_control
PutBits(&ld2, 0x0, 1); // PES_Priority
PutBits(&ld2, 0x0, 1); // data_alignment_indicator
PutBits(&ld2, 0x0, 1); // Copyright
PutBits(&ld2, 0x0, 1); // Original or Copy
//7 = 6+1
if (pts != INVALID_PTS_VALUE)
PutBits(&ld2, 0x2, 2);
else
PutBits(&ld2, 0x0, 2); // PTS_DTS flag
PutBits(&ld2, 0x0, 1); // ESCR_flag
PutBits(&ld2, 0x0, 1); // ES_rate_flag
PutBits(&ld2, 0x0, 1); // DSM_trick_mode_flag
PutBits(&ld2, 0x0, 1); // additional_copy_ingo_flag
PutBits(&ld2, 0x0, 1); // PES_CRC_flag
PutBits(&ld2, 0x0, 1); // PES_extension_flag
//8 = 7+1
if (pts != INVALID_PTS_VALUE)
PutBits(&ld2, 0x5, 8);
else
PutBits(&ld2, 0x0, 8); // PES_header_data_length
//9 = 8+1
if (pts != INVALID_PTS_VALUE) {
PutBits(&ld2, 0x2, 4);
PutBits(&ld2, (pts >> 30) & 0x7, 3);
PutBits(&ld2, 0x1, 1);
PutBits(&ld2, (pts >> 15) & 0x7fff, 15);
PutBits(&ld2, 0x1, 1);
PutBits(&ld2, pts & 0x7fff, 15);
PutBits(&ld2, 0x1, 1);
}
//14 = 9+5
if (pic_start_code) {
PutBits(&ld2, 0x0, 8);
PutBits(&ld2, 0x0, 8);
PutBits(&ld2, 0x1, 8); // Start Code
PutBits(&ld2, pic_start_code & 0xff, 8); // 00, for picture start
PutBits(&ld2, (pic_start_code >> 8) & 0xff, 8); // For any extra information (like in mpeg4p2, the pic_start_code)
//14 + 5 = 19
}
FlushBits(&ld2);
return (ld2.Ptr - data);
}

View File

@@ -1,188 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <errno.h>
#include <algorithm>
#include "misc.h"
#include "pes.h"
#include "writer.h"
#define WMV3_PRIVATE_DATA_LENGTH 4
#define METADATA_STRUCT_A_START 12
#define METADATA_STRUCT_B_START 24
#define METADATA_STRUCT_B_FRAMERATE_START 32
#define METADATA_STRUCT_C_START 8
#define VC1_SEQUENCE_LAYER_METADATA_START_CODE 0x80
#define VC1_FRAME_START_CODE 0x0d
class WriterVC1 : public Writer
{
private:
bool initialHeader;
uint8_t FrameHeaderSeen;
AVStream *stream;
public:
bool Write(AVPacket *packet, int64_t pts);
void Init(int _fd, AVStream *_stream, Player *_player);
WriterVC1();
};
void WriterVC1::Init(int _fd, AVStream *_stream, Player *_player)
{
fd = _fd;
stream = _stream;
player = _player;
initialHeader = true;
}
bool WriterVC1::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
if (initialHeader) {
initialHeader = false;
FrameHeaderSeen = false;
const uint8_t SequenceLayerStartCode[] =
{ 0x00, 0x00, 0x01, VC1_SEQUENCE_LAYER_METADATA_START_CODE };
const uint8_t Metadata[] = {
0x00, 0x00, 0x00, 0xc5,
0x04, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile */
0x00, 0x00, 0x00, 0x00, /* Struct A */
0x00, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00,
0x60, 0x00, 0x00, 0x00, /* Struct B */
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
uint8_t PesPayload[128];
uint8_t *PesPtr;
unsigned int usecPerFrame = av_rescale(AV_TIME_BASE, stream->r_frame_rate.den, stream->r_frame_rate.num);
struct iovec iov[2];
memset(PesPayload, 0, sizeof(PesPayload));
PesPtr = PesPayload;
memcpy(PesPtr, SequenceLayerStartCode, sizeof(SequenceLayerStartCode));
PesPtr += sizeof(SequenceLayerStartCode);
memcpy(PesPtr, Metadata, sizeof(Metadata));
PesPtr += METADATA_STRUCT_C_START;
PesPtr += WMV3_PRIVATE_DATA_LENGTH;
/* Metadata Header Struct A */
*PesPtr++ = (stream->codec->height >> 0) & 0xff;
*PesPtr++ = (stream->codec->height >> 8) & 0xff;
*PesPtr++ = (stream->codec->height >> 16) & 0xff;
*PesPtr++ = stream->codec->height >> 24;
*PesPtr++ = (stream->codec->width >> 0) & 0xff;
*PesPtr++ = (stream->codec->width >> 8) & 0xff;
*PesPtr++ = (stream->codec->width >> 16) & 0xff;
*PesPtr++ = stream->codec->width >> 24;
PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */
*PesPtr++ = (usecPerFrame >> 0) & 0xff;
*PesPtr++ = (usecPerFrame >> 8) & 0xff;
*PesPtr++ = (usecPerFrame >> 16) & 0xff;
*PesPtr++ = usecPerFrame >> 24;
iov[0].iov_base = PesHeader;
iov[1].iov_base = PesPayload;
iov[1].iov_len = PesPtr - PesPayload;
iov[0].iov_len = InsertPesHeader(PesHeader, iov[1].iov_len, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
if (writev(fd, iov, 2) < 0)
return false;
/* For VC1 the codec private data is a standard vc1 sequence header so we just copy it to the output */
iov[0].iov_base = PesHeader;
iov[1].iov_base = stream->codec->extradata;
iov[1].iov_len = stream->codec->extradata_size;
iov[0].iov_len = InsertPesHeader(PesHeader, iov[1].iov_len, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
if (writev(fd, iov, 2) < 0)
return false;
initialHeader = false;
}
if (packet->size > 0) {
int Position = 0;
bool insertSampleHeader = true;
while (Position < packet->size) {
int PacketLength = std::min(packet->size - Position, MAX_PES_PACKET_SIZE);
uint8_t PesHeader[PES_MAX_HEADER_SIZE];
int HeaderLength = InsertPesHeader(PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, pts, 0);
if (insertSampleHeader) {
const uint8_t Vc1FrameStartCode[] = { 0, 0, 1, VC1_FRAME_START_CODE };
if (!FrameHeaderSeen && (packet->size > 3) && (memcmp(packet->data, Vc1FrameStartCode, 4) == 0))
FrameHeaderSeen = true;
if (!FrameHeaderSeen) {
memcpy(&PesHeader[HeaderLength], Vc1FrameStartCode, sizeof(Vc1FrameStartCode));
HeaderLength += sizeof(Vc1FrameStartCode);
}
insertSampleHeader = false;
}
struct iovec iov[2];
iov[0].iov_base = PesHeader;
iov[0].iov_len = HeaderLength;
iov[1].iov_base = packet->data + Position;
iov[1].iov_len = PacketLength;
ssize_t l = writev(fd, iov, 2);
if (l < 0)
return false;
Position += PacketLength;
pts = INVALID_PTS_VALUE;
}
}
return true;
}
WriterVC1::WriterVC1()
{
Register(this, AV_CODEC_ID_VC1, VIDEO_ENCODING_VC1);
}
static WriterVC1 writer_vc1 __attribute__ ((init_priority (300)));

View File

@@ -1,171 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2010 konfetti (based on code from libeplayer2)
* Copyright (C) 2014 martii (based on code from libeplayer3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
#include <errno.h>
#include "misc.h"
#include "pes.h"
#include "writer.h"
#include <algorithm>
#define WMV3_PRIVATE_DATA_LENGTH 4
static const uint8_t Metadata[] = {
0x00, 0x00, 0x00, 0xc5,
0x04, 0x00, 0x00, 0x00,
#define METADATA_STRUCT_C_START 8
0xc0, 0x00, 0x00, 0x00, /* Struct C set for for advanced profile */
#define METADATA_STRUCT_A_START 12
0x00, 0x00, 0x00, 0x00, /* Struct A */
0x00, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00,
#define METADATA_STRUCT_B_START 24
0x60, 0x00, 0x00, 0x00, /* Struct B */
0x00, 0x00, 0x00, 0x00,
#define METADATA_STRUCT_B_FRAMERATE_START 32
0x00, 0x00, 0x00, 0x00
};
class WriterWMV : public Writer
{
private:
bool initialHeader;
AVStream *stream;
public:
bool Write(AVPacket *packet, int64_t pts);
void Init(int _fd, AVStream *_stream, Player *_player);
WriterWMV();
};
void WriterWMV::Init(int _fd, AVStream *_stream, Player *_player)
{
fd = _fd;
stream = _stream;
player = _player;
initialHeader = true;
}
bool WriterWMV::Write(AVPacket *packet, int64_t pts)
{
if (!packet || !packet->data)
return false;
if (initialHeader) {
#define PES_MIN_HEADER_SIZE 9
uint8_t PesPacket[PES_MIN_HEADER_SIZE + 128];
uint8_t *PesPtr;
unsigned int MetadataLength;
unsigned int usecPerFrame = av_rescale(AV_TIME_BASE, stream->r_frame_rate.den, stream->r_frame_rate.num);
PesPtr = &PesPacket[PES_MIN_HEADER_SIZE];
memcpy(PesPtr, Metadata, sizeof(Metadata));
PesPtr += METADATA_STRUCT_C_START;
uint8_t privateData[WMV3_PRIVATE_DATA_LENGTH] = { 0 };
memcpy(privateData, stream->codec->extradata, stream->codec->extradata_size > WMV3_PRIVATE_DATA_LENGTH ? WMV3_PRIVATE_DATA_LENGTH : stream->codec->extradata_size);
memcpy(PesPtr, privateData, WMV3_PRIVATE_DATA_LENGTH);
PesPtr += WMV3_PRIVATE_DATA_LENGTH;
/* Metadata Header Struct A */
*PesPtr++ = (stream->codec->height >> 0) & 0xff;
*PesPtr++ = (stream->codec->height >> 8) & 0xff;
*PesPtr++ = (stream->codec->height >> 16) & 0xff;
*PesPtr++ = stream->codec->height >> 24;
*PesPtr++ = (stream->codec->width >> 0) & 0xff;
*PesPtr++ = (stream->codec->width >> 8) & 0xff;
*PesPtr++ = (stream->codec->width >> 16) & 0xff;
*PesPtr++ = stream->codec->width >> 24;
PesPtr += 12; /* Skip flag word and Struct B first 8 bytes */
*PesPtr++ = (usecPerFrame >> 0) & 0xff;
*PesPtr++ = (usecPerFrame >> 8) & 0xff;
*PesPtr++ = (usecPerFrame >> 16) & 0xff;
*PesPtr++ = usecPerFrame >> 24;
MetadataLength = PesPtr - &PesPacket[PES_MIN_HEADER_SIZE];
int HeaderLength = InsertPesHeader(PesPacket, MetadataLength, VC1_VIDEO_PES_START_CODE, INVALID_PTS_VALUE, 0);
if (write(fd, PesPacket, HeaderLength + MetadataLength) < 0)
return false;
initialHeader = false;
}
if (packet->size > 0 && packet->data) {
int Position = 0;
bool insertSampleHeader = true;
while (Position < packet->size) {
int PacketLength = std::min(packet->size - Position, MAX_PES_PACKET_SIZE);
uint8_t PesHeader[PES_MAX_HEADER_SIZE] = { 0 };
int HeaderLength = InsertPesHeader(PesHeader, PacketLength, VC1_VIDEO_PES_START_CODE, pts, 0);
if (insertSampleHeader) {
unsigned int PesLength;
unsigned int PrivateHeaderLength;
PrivateHeaderLength = InsertVideoPrivateDataHeader(&PesHeader[HeaderLength], packet->size);
/* Update PesLength */
PesLength = PesHeader[PES_LENGTH_BYTE_0] + (PesHeader[PES_LENGTH_BYTE_1] << 8) + PrivateHeaderLength;
PesHeader[PES_LENGTH_BYTE_0] = PesLength & 0xff;
PesHeader[PES_LENGTH_BYTE_1] = (PesLength >> 8) & 0xff;
PesHeader[PES_HEADER_DATA_LENGTH_BYTE] += PrivateHeaderLength;
PesHeader[PES_FLAGS_BYTE] |= PES_EXTENSION_DATA_PRESENT;
HeaderLength += PrivateHeaderLength;
insertSampleHeader = false;
}
uint8_t PacketStart[packet->size + HeaderLength];
memcpy(PacketStart, PesHeader, HeaderLength);
memcpy(PacketStart + HeaderLength, packet->data + Position, PacketLength);
if (write(fd, PacketStart, PacketLength + HeaderLength) < 0)
return false;
Position += PacketLength;
pts = INVALID_PTS_VALUE;
}
}
return true;
}
WriterWMV::WriterWMV()
{
Register(this, AV_CODEC_ID_WMV1, VIDEO_ENCODING_WMV);
Register(this, AV_CODEC_ID_WMV2, VIDEO_ENCODING_WMV);
Register(this, AV_CODEC_ID_WMV3, VIDEO_ENCODING_WMV);
}
static WriterWMV writer_wmv __attribute__ ((init_priority (300)));

View File

@@ -1,106 +0,0 @@
/*
* linuxdvb output/writer handling
*
* Copyright (C) 2014 martii
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <map>
#include "pes.h"
#include "writer.h"
// This does suck ... the original idea was to just link the object files and let them register themselves.
// Alas, that didn't work as expected.
#include "ac3.cpp"
#include "divx.cpp"
#include "dts.cpp"
#include "h263.cpp"
#include "h264.cpp"
#include "mp3.cpp"
#include "mpeg2.cpp"
#include "pcm.cpp"
#include "vc1.cpp"
#include "wmv.cpp"
//#include "aac.cpp"
static std::map<enum AVCodecID,Writer *>writers __attribute__ ((init_priority (200)));
static std::map<enum AVCodecID,video_encoding_t>vencoding __attribute__ ((init_priority (200)));
static std::map<enum AVCodecID,audio_encoding_t>aencoding __attribute__ ((init_priority (200)));
void Writer::Register(Writer *w, enum AVCodecID id, video_encoding_t encoding)
{
writers[id] = w;
vencoding[id] = encoding;
}
void Writer::Register(Writer *w, enum AVCodecID id, audio_encoding_t encoding)
{
writers[id] = w;
aencoding[id] = encoding;
}
bool Writer::Write(AVPacket * /* packet */, int64_t /* pts */)
{
return false;
}
static Writer writer __attribute__ ((init_priority (300)));
Writer *Writer::GetWriter(enum AVCodecID id, enum AVMediaType codec_type, int track_type)
{
fprintf(stderr, "GETWRITER %d %d %d", id, codec_type, track_type);
if (track_type != 6) { // hack for ACC resampling
std::map<enum AVCodecID,Writer*>::iterator it = writers.find(id);
if (it != writers.end())
return it->second;
}
switch (codec_type) {
case AVMEDIA_TYPE_AUDIO:
if (id == AV_CODEC_ID_INJECTPCM) // should not happen
break;
return GetWriter(AV_CODEC_ID_INJECTPCM, codec_type, 100);
case AVMEDIA_TYPE_VIDEO:
if (id == AV_CODEC_ID_MPEG2TS) // should not happen
break;
return GetWriter(AV_CODEC_ID_MPEG2TS, codec_type, 100);
default:
break;
}
return &writer;
}
video_encoding_t Writer::GetVideoEncoding(enum AVCodecID id)
{
std::map<enum AVCodecID,video_encoding_t>::iterator it = vencoding.find(id);
if (it != vencoding.end())
return it->second;
return VIDEO_ENCODING_AUTO;
}
audio_encoding_t Writer::GetAudioEncoding(enum AVCodecID id)
{
std::map<enum AVCodecID,audio_encoding_t>::iterator it = aencoding.find(id);
if (it != aencoding.end())
return it->second;
return AUDIO_ENCODING_LPCMA;
}