* Do no manually allocate the buffer for the
ByteIOContext. libavformat may reallocate it on demand, we need to use the matching allocation methods. * Init the ByteIOContext with the proper "write flag". This solves a busy loop when writing the trailer of MKV files, since the first buffer was initially skipped and the MKV muxer can not seek back in the stream where it wants. * Get rid of the fCalculatePTS member, and calculate PTS of audio packets as well. I don't remember why I prevented that, however VLC complains about audio packets having wrong PTS (with or without this change) Our own MediaPlayer plays videos generated by (a modified) Clockwerk at least once, but seeking subsequently fails. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38851 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -75,7 +75,6 @@ private:
|
|||||||
AVFormatContext* fContext;
|
AVFormatContext* fContext;
|
||||||
AVStream* fStream;
|
AVStream* fStream;
|
||||||
AVPacket fPacket;
|
AVPacket fPacket;
|
||||||
bool fCalculatePTS;
|
|
||||||
// Since different threads may write to the target,
|
// Since different threads may write to the target,
|
||||||
// we need to protect the file position and I/O by a lock.
|
// we need to protect the file position and I/O by a lock.
|
||||||
BLocker* fStreamLock;
|
BLocker* fStreamLock;
|
||||||
@@ -88,7 +87,6 @@ AVFormatWriter::StreamCookie::StreamCookie(AVFormatContext* context,
|
|||||||
:
|
:
|
||||||
fContext(context),
|
fContext(context),
|
||||||
fStream(NULL),
|
fStream(NULL),
|
||||||
fCalculatePTS(false),
|
|
||||||
fStreamLock(streamLock)
|
fStreamLock(streamLock)
|
||||||
{
|
{
|
||||||
av_init_packet(&fPacket);
|
av_init_packet(&fPacket);
|
||||||
@@ -160,8 +158,6 @@ AVFormatWriter::StreamCookie::Init(const media_format* format,
|
|||||||
// Some formats want stream headers to be separate
|
// Some formats want stream headers to be separate
|
||||||
if ((fContext->oformat->flags & AVFMT_GLOBALHEADER) != 0)
|
if ((fContext->oformat->flags & AVFMT_GLOBALHEADER) != 0)
|
||||||
fStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
fStream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
fCalculatePTS = true;
|
|
||||||
} else if (format->type == B_MEDIA_RAW_AUDIO) {
|
} else if (format->type == B_MEDIA_RAW_AUDIO) {
|
||||||
fStream->codec->codec_type = CODEC_TYPE_AUDIO;
|
fStream->codec->codec_type = CODEC_TYPE_AUDIO;
|
||||||
#if GET_CONTEXT_DEFAULTS
|
#if GET_CONTEXT_DEFAULTS
|
||||||
@@ -170,11 +166,6 @@ AVFormatWriter::StreamCookie::Init(const media_format* format,
|
|||||||
#endif
|
#endif
|
||||||
// frame rate
|
// frame rate
|
||||||
fStream->codec->sample_rate = (int)format->u.raw_audio.frame_rate;
|
fStream->codec->sample_rate = (int)format->u.raw_audio.frame_rate;
|
||||||
// NOTE: API example does not do this:
|
|
||||||
// fStream->codec->time_base.den = (int)format->u.raw_audio.frame_rate;
|
|
||||||
// fStream->codec->time_base.num = 1;
|
|
||||||
// fStream->time_base.den = (int)format->u.raw_audio.frame_rate;
|
|
||||||
// fStream->time_base.num = 1;
|
|
||||||
|
|
||||||
// channels
|
// channels
|
||||||
fStream->codec->channels = format->u.raw_audio.channel_count;
|
fStream->codec->channels = format->u.raw_audio.channel_count;
|
||||||
@@ -233,8 +224,6 @@ AVFormatWriter::StreamCookie::Init(const media_format* format,
|
|||||||
// The bits match 1:1 for media_multi_channels and FFmpeg defines.
|
// The bits match 1:1 for media_multi_channels and FFmpeg defines.
|
||||||
fStream->codec->channel_layout = format->u.raw_audio.channel_mask;
|
fStream->codec->channel_layout = format->u.raw_audio.channel_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
fCalculatePTS = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE(" stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n",
|
TRACE(" stream->time_base: (%d/%d), codec->time_base: (%d/%d))\n",
|
||||||
@@ -249,8 +238,9 @@ status_t
|
|||||||
AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
|
AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
|
||||||
size_t chunkSize, media_encode_info* encodeInfo)
|
size_t chunkSize, media_encode_info* encodeInfo)
|
||||||
{
|
{
|
||||||
TRACE_PACKET("AVFormatWriter::StreamCookie::WriteChunk(%p, %ld, "
|
TRACE_PACKET("AVFormatWriter::StreamCookie[%d]::WriteChunk(%p, %ld, "
|
||||||
"start_time: %lld)\n", chunkBuffer, chunkSize, encodeInfo->start_time);
|
"start_time: %lld)\n", fStream->index, chunkBuffer, chunkSize,
|
||||||
|
encodeInfo->start_time);
|
||||||
|
|
||||||
BAutolock _(fStreamLock);
|
BAutolock _(fStreamLock);
|
||||||
|
|
||||||
@@ -260,14 +250,13 @@ AVFormatWriter::StreamCookie::WriteChunk(const void* chunkBuffer,
|
|||||||
fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
|
fPacket.data = const_cast<uint8_t*>((const uint8_t*)chunkBuffer);
|
||||||
fPacket.size = chunkSize;
|
fPacket.size = chunkSize;
|
||||||
|
|
||||||
if (fCalculatePTS) {
|
fPacket.pts = int64_t((double)encodeInfo->start_time
|
||||||
fPacket.pts = (encodeInfo->start_time
|
* fStream->time_base.den / (1000000.0 * fStream->time_base.num)
|
||||||
* fStream->time_base.den / fStream->time_base.num) / 1000000;
|
+ 0.5);
|
||||||
TRACE_PACKET(" PTS: %lld (stream->time_base: (%d/%d), "
|
TRACE_PACKET(" PTS: %lld (stream->time_base: (%d/%d), "
|
||||||
"codec->time_base: (%d/%d))\n", fPacket.pts,
|
"codec->time_base: (%d/%d))\n", fPacket.pts,
|
||||||
fStream->time_base.num, fStream->time_base.den,
|
fStream->time_base.num, fStream->time_base.den,
|
||||||
fStream->codec->time_base.num, fStream->codec->time_base.den);
|
fStream->codec->time_base.num, fStream->codec->time_base.den);
|
||||||
}
|
|
||||||
|
|
||||||
// From ffmpeg.c::do_audio_out():
|
// From ffmpeg.c::do_audio_out():
|
||||||
// TODO:
|
// TODO:
|
||||||
@@ -315,7 +304,6 @@ AVFormatWriter::AVFormatWriter()
|
|||||||
:
|
:
|
||||||
fContext(avformat_alloc_context()),
|
fContext(avformat_alloc_context()),
|
||||||
fHeaderWritten(false),
|
fHeaderWritten(false),
|
||||||
fIOBuffer(NULL),
|
|
||||||
fStreamLock("stream lock")
|
fStreamLock("stream lock")
|
||||||
{
|
{
|
||||||
TRACE("AVFormatWriter::AVFormatWriter\n");
|
TRACE("AVFormatWriter::AVFormatWriter\n");
|
||||||
@@ -339,8 +327,7 @@ AVFormatWriter::~AVFormatWriter()
|
|||||||
}
|
}
|
||||||
|
|
||||||
av_free(fContext);
|
av_free(fContext);
|
||||||
|
av_free(fIOContext.buffer);
|
||||||
delete[] fIOBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -352,14 +339,13 @@ AVFormatWriter::Init(const media_file_format* fileFormat)
|
|||||||
{
|
{
|
||||||
TRACE("AVFormatWriter::Init()\n");
|
TRACE("AVFormatWriter::Init()\n");
|
||||||
|
|
||||||
delete[] fIOBuffer;
|
uint8* buffer = static_cast<uint8*>(av_malloc(kIOBufferSize));
|
||||||
fIOBuffer = new(std::nothrow) uint8[kIOBufferSize];
|
if (buffer == NULL)
|
||||||
if (fIOBuffer == NULL)
|
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
// Init I/O context with buffer and hook functions, pass ourself as
|
// Init I/O context with buffer and hook functions, pass ourself as
|
||||||
// cookie.
|
// cookie.
|
||||||
if (init_put_byte(&fIOContext, fIOBuffer, kIOBufferSize, 0, this,
|
if (init_put_byte(&fIOContext, buffer, kIOBufferSize, 1, this,
|
||||||
0, _Write, _Seek) != 0) {
|
0, _Write, _Seek) != 0) {
|
||||||
TRACE(" init_put_byte() failed!\n");
|
TRACE(" init_put_byte() failed!\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
@@ -369,7 +355,7 @@ AVFormatWriter::Init(const media_file_format* fileFormat)
|
|||||||
fContext->pb = &fIOContext;
|
fContext->pb = &fIOContext;
|
||||||
|
|
||||||
// Set the AVOutputFormat according to fileFormat...
|
// Set the AVOutputFormat according to fileFormat...
|
||||||
fContext->oformat = guess_format(fileFormat->short_name,
|
fContext->oformat = av_guess_format(fileFormat->short_name,
|
||||||
fileFormat->file_extension, fileFormat->mime_type);
|
fileFormat->file_extension, fileFormat->mime_type);
|
||||||
if (fContext->oformat == NULL) {
|
if (fContext->oformat == NULL) {
|
||||||
TRACE(" failed to find AVOuputFormat for %s\n",
|
TRACE(" failed to find AVOuputFormat for %s\n",
|
||||||
@@ -430,10 +416,11 @@ AVFormatWriter::CommitHeader()
|
|||||||
int result = av_write_header(fContext);
|
int result = av_write_header(fContext);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
TRACE(" av_write_header(): %d\n", result);
|
TRACE(" av_write_header(): %d\n", result);
|
||||||
else
|
|
||||||
fHeaderWritten = true;
|
|
||||||
|
|
||||||
#if TRACE_AVFORMAT_WRITER
|
// We need to close the codecs we opened, even in case of failure.
|
||||||
|
fHeaderWritten = true;
|
||||||
|
|
||||||
|
#ifdef TRACE_AVFORMAT_WRITER
|
||||||
TRACE(" wrote header\n");
|
TRACE(" wrote header\n");
|
||||||
for (unsigned i = 0; i < fContext->nb_streams; i++) {
|
for (unsigned i = 0; i < fContext->nb_streams; i++) {
|
||||||
AVStream* stream = fContext->streams[i];
|
AVStream* stream = fContext->streams[i];
|
||||||
|
|||||||
@@ -46,17 +46,15 @@ public:
|
|||||||
private:
|
private:
|
||||||
static int _Write(void* cookie, uint8* buffer,
|
static int _Write(void* cookie, uint8* buffer,
|
||||||
int bufferSize);
|
int bufferSize);
|
||||||
|
|
||||||
static off_t _Seek(void* cookie, off_t offset, int whence);
|
static off_t _Seek(void* cookie, off_t offset, int whence);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class StreamCookie;
|
class StreamCookie;
|
||||||
|
|
||||||
AVFormatContext* fContext;
|
AVFormatContext* fContext;
|
||||||
bool fHeaderWritten;
|
bool fHeaderWritten;
|
||||||
|
|
||||||
ByteIOContext fIOContext;
|
ByteIOContext fIOContext;
|
||||||
uint8* fIOBuffer;
|
|
||||||
|
|
||||||
StreamCookie** fStreams;
|
StreamCookie** fStreams;
|
||||||
BLocker fStreamLock;
|
BLocker fStreamLock;
|
||||||
|
|||||||
Reference in New Issue
Block a user