* GXF muxer.
* Copyright (c) 2006 SmartJog S.A., Baptiste Coudurier <baptiste dot coudurier at smartjog dot com>.
*
- * This library 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 file is part of FFmpeg.
*
- * This library is distributed in the hope that it will be useful,
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser 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
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/fifo.h"
#include "avformat.h"
#include "gxf.h"
#include "riff.h"
typedef struct GXFStreamContext {
AVCodecContext *codec;
- FifoBuffer audio_buffer;
+ AVFifoBuffer audio_buffer;
uint32_t track_type;
uint32_t sample_size;
uint32_t sample_rate;
int bframes;
int p_per_gop;
int b_per_gop;
+ int first_gop_closed;
+ int64_t current_dts;
+ int dts_delay;
} GXFStreamContext;
typedef struct GXFContext {
uint16_t umf_media_size;
int audio_written;
int sample_rate;
- int field_number;
int flags;
AVFormatContext *fc;
GXFStreamContext streams[48];
{ 720, 6 },
};
-static const CodecTag gxf_media_types[] = {
+static const AVCodecTag gxf_media_types[] = {
{ CODEC_ID_MJPEG , 3 }, /* NTSC */
{ CODEC_ID_MJPEG , 4 }, /* PAL */
{ CODEC_ID_PCM_S24LE , 9 },
static void gxf_write_padding(ByteIOContext *pb, offset_t to_pad)
{
- while (to_pad--) {
+ for (; to_pad > 0; to_pad--) {
put_byte(pb, 0);
}
}
ctx->b_per_gop = 9; /* ensure value won't take more than one char */
}
size = snprintf(buffer, 1024, "Ver 1\nBr %.6f\nIpg 1\nPpi %d\nBpiop %d\n"
- "Pix 0\nCf %d\nCg 1\nSl 7\nnl16 %d\nVi 1\nf1 1\n",
+ "Pix 0\nCf %d\nCg %d\nSl 7\nnl16 %d\nVi 1\nf1 1\n",
(float)ctx->codec->bit_rate, ctx->p_per_gop, ctx->b_per_gop,
- ctx->codec->pix_fmt == PIX_FMT_YUV422P ? 2 : 1,
+ ctx->codec->pix_fmt == PIX_FMT_YUV422P ? 2 : 1, ctx->first_gop_closed == 1,
ctx->codec->height / 16);
- put_byte(pb, 0x4F);
+ put_byte(pb, TRACK_MPG_AUX);
put_byte(pb, size + 1);
put_buffer(pb, (uint8_t *)buffer, size + 1);
return size + 3;
put_be16(pb, 0); /* size */
/* media file name */
- put_byte(pb, 0x4C);
+ put_byte(pb, TRACK_NAME);
put_byte(pb, strlen(ES_NAME_PATTERN) + 3);
put_tag(pb, ES_NAME_PATTERN);
put_be16(pb, stream->media_info);
if (stream->codec->codec_id != CODEC_ID_MPEG2VIDEO) {
/* auxiliary information */
- put_byte(pb, 0x4D);
+ put_byte(pb, TRACK_AUX);
put_byte(pb, 8);
if (stream->codec->codec_id == CODEC_ID_NONE)
gxf_write_timecode_auxiliary(pb, stream);
}
/* file system version */
- put_byte(pb, 0x4E);
+ put_byte(pb, TRACK_VER);
put_byte(pb, 4);
put_be32(pb, 0);
gxf_write_mpeg_auxiliary(pb, stream);
/* frame rate */
- put_byte(pb, 0x50);
+ put_byte(pb, TRACK_FPS);
put_byte(pb, 4);
put_be32(pb, stream->frame_rate_index);
/* lines per frame */
- put_byte(pb, 0x51);
+ put_byte(pb, TRACK_LINES);
put_byte(pb, 4);
put_be32(pb, stream->lines_index);
/* fields per frame */
- put_byte(pb, 0x52);
+ put_byte(pb, TRACK_FPF);
put_byte(pb, 4);
put_be32(pb, stream->fields);
filename++;
else
filename = ctx->fc->filename;
- put_byte(pb, 0x40);
+ put_byte(pb, MAT_NAME);
put_byte(pb, strlen(SERVER_PATH) + strlen(filename) + 1);
put_tag(pb, SERVER_PATH);
put_tag(pb, filename);
put_byte(pb, 0);
/* first field */
- put_byte(pb, 0x41);
+ put_byte(pb, MAT_FIRST_FIELD);
put_byte(pb, 4);
put_be32(pb, 0);
/* last field */
- put_byte(pb, 0x42);
+ put_byte(pb, MAT_LAST_FIELD);
put_byte(pb, 4);
put_be32(pb, ctx->nb_frames);
/* reserved */
- put_byte(pb, 0x43);
+ put_byte(pb, MAT_MARK_IN);
put_byte(pb, 4);
put_be32(pb, 0);
- put_byte(pb, 0x44);
+ put_byte(pb, MAT_MARK_OUT);
put_byte(pb, 4);
put_be32(pb, ctx->nb_frames);
/* estimated size */
- put_byte(pb, 0x45);
+ put_byte(pb, MAT_SIZE);
put_byte(pb, 4);
put_be32(pb, url_fsize(pb) / 1024);
case CODEC_ID_PCM_S16LE: id= 'A'; break;
case CODEC_ID_DVVIDEO: id= sc->track_type == 6 ? 'E' : 'D'; break;
case CODEC_ID_MJPEG: id= 'V'; break;
+ default: break;
}
sc->media_info= id << 8;
/* FIXME first 10 audio tracks are 0 to 9 next 22 are A to V */
put_le32(pb, 2);
else
put_le32(pb, 1); /* default to 420 */
- put_le32(pb, 1); /* closed = 1, open = 0, unknown = 255 */
+ put_le32(pb, stream->first_gop_closed == 1); /* closed = 1, open = 0, unknown = 255 */
put_le32(pb, 3); /* top = 1, bottom = 2, frame = 3, unknown = 0 */
put_le32(pb, 1); /* I picture per GOP */
put_le32(pb, stream->p_per_gop);
offset_t startpos, curpos;
int path_size = strlen(ES_NAME_PATTERN);
+ memset(buffer, 0, 88);
startpos = url_ftell(pb);
put_le16(pb, 0); /* length */
put_le16(pb, sc->media_info);
return updatePacketSize(pb, pos);
}
+#define GXF_NODELAY -5000
+
static int gxf_write_header(AVFormatContext *s)
{
- ByteIOContext *pb = &s->pb;
+ ByteIOContext *pb = s->pb;
GXFContext *gxf = s->priv_data;
int i;
}
sc->track_type = 2;
sc->sample_rate = st->codec->sample_rate;
+ av_set_pts_info(st, 64, 1, sc->sample_rate);
sc->sample_size = 16;
sc->frame_rate_index = -2;
sc->lines_index = -2;
sc->fields = -2;
gxf->audio_tracks++;
gxf->flags |= 0x04000000; /* audio is 16 bit pcm */
- fifo_init(&sc->audio_buffer, 3*GXF_AUDIO_PACKET_SIZE);
+ av_fifo_init(&sc->audio_buffer, 3*GXF_AUDIO_PACKET_SIZE);
} else if (sc->codec->codec_type == CODEC_TYPE_VIDEO) {
/* FIXME check from time_base ? */
if (sc->codec->height == 480 || sc->codec->height == 512) { /* NTSC or NTSC+VBI */
gxf->flags |= 0x00000040;
}
gxf->sample_rate = sc->sample_rate;
+ av_set_pts_info(st, 64, 1, st->codec->time_base.den);
+ sc->dts_delay = GXF_NODELAY;
if (gxf_find_lines_index(sc) < 0)
sc->lines_index = -1;
sc->sample_size = st->codec->bit_rate;
sc->fields = 2; /* interlaced */
switch (sc->codec->codec_id) {
case CODEC_ID_MPEG2VIDEO:
+ sc->first_gop_closed = -1;
sc->track_type = 4;
gxf->mpeg_tracks++;
gxf->flags |= 0x00008000;
}
break;
default:
- av_log(NULL, AV_LOG_ERROR, "video codec not supported\n");
+ av_log(s, AV_LOG_ERROR, "video codec not supported\n");
return -1;
}
}
static int gxf_write_trailer(AVFormatContext *s)
{
- ByteIOContext *pb = &s->pb;
+ ByteIOContext *pb = s->pb;
GXFContext *gxf = s->priv_data;
offset_t end;
int i;
for (i = 0; i < s->nb_streams; ++i) {
if (s->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) {
- fifo_free(&gxf->streams[i].audio_buffer);
- }
- if (s->streams[i]->codec->frame_number > gxf->nb_frames)
+ av_fifo_free(&gxf->streams[i].audio_buffer);
+ } else if (s->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
gxf->nb_frames = 2 * s->streams[i]->codec->frame_number;
+ }
}
gxf_write_eos_packet(pb, gxf);
return 0;
}
+static int gxf_parse_mpeg_frame(GXFStreamContext *sc, const uint8_t *buf, int size)
+{
+ uint32_t c=-1;
+ int i;
+ for(i=0; i<size-4 && c!=0x100; i++){
+ c = (c<<8) + buf[i];
+ if(c == 0x1B8 && sc->first_gop_closed == -1) /* GOP start code */
+ sc->first_gop_closed= (buf[i+4]>>6)&1;
+ }
+ return (buf[i+1]>>3)&7;
+}
+
static int gxf_write_media_preamble(ByteIOContext *pb, GXFContext *ctx, AVPacket *pkt, int size)
{
GXFStreamContext *sc = &ctx->streams[pkt->stream_index];
+ int64_t dts = av_rescale_rnd(pkt->dts, ctx->sample_rate, sc->codec->time_base.den, AV_ROUND_UP);
put_byte(pb, sc->media_type);
put_byte(pb, sc->index);
- put_be32(pb, ctx->field_number);
+ put_be32(pb, dts);
if (sc->codec->codec_type == CODEC_TYPE_AUDIO) {
put_be16(pb, 0);
put_be16(pb, size / 2);
} else if (sc->codec->codec_id == CODEC_ID_MPEG2VIDEO) {
- if (pkt->flags & PKT_FLAG_KEY) {
+ int frame_type = gxf_parse_mpeg_frame(sc, pkt->data, pkt->size);
+ if (frame_type == FF_I_TYPE) {
put_byte(pb, 0x0d);
sc->iframes++;
- } else if (pkt->pts != pkt->dts) {
+ } else if (frame_type == FF_B_TYPE) {
put_byte(pb, 0x0f);
sc->bframes++;
} else {
put_be24(pb, 0);
} else
put_be32(pb, size);
- put_be32(pb, ctx->field_number);
+ put_be32(pb, dts);
put_byte(pb, 1); /* flags */
put_byte(pb, 0); /* reserved */
return 16;
gxf_write_media_preamble(pb, ctx, pkt, pkt->size + padding);
put_buffer(pb, pkt->data, pkt->size);
gxf_write_padding(pb, padding);
- if (sc->codec->codec_type == CODEC_TYPE_VIDEO)
- ctx->field_number += 2;
return updatePacketSize(pb, pos);
}
{
GXFContext *gxf = s->priv_data;
- gxf_write_media_packet(&s->pb, gxf, pkt);
- put_flush_packet(&s->pb);
+ gxf_write_media_packet(s->pb, gxf, pkt);
+ put_flush_packet(s->pb);
return 0;
}
+static int gxf_new_audio_packet(GXFContext *gxf, GXFStreamContext *sc, AVPacket *pkt, int flush)
+{
+ int size = flush ? av_fifo_size(&sc->audio_buffer) : GXF_AUDIO_PACKET_SIZE;
+
+ if (!size)
+ return 0;
+ av_new_packet(pkt, size);
+ av_fifo_read(&sc->audio_buffer, pkt->data, size);
+ pkt->stream_index = sc->index;
+ pkt->dts = sc->current_dts;
+ sc->current_dts += size / 2; /* we only support 16 bit pcm mono for now */
+ return size;
+}
+
static int gxf_interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int flush)
{
- AVPacketList *pktl, **next_point, *this_pktl;
GXFContext *gxf = s->priv_data;
- GXFStreamContext *sc;
+ AVPacket new_pkt;
int i;
- if (pkt) {
- sc = &gxf->streams[pkt->stream_index];
- if (sc->codec->codec_type == CODEC_TYPE_AUDIO) {
- fifo_write(&sc->audio_buffer, pkt->data, pkt->size, NULL);
- } else {
- this_pktl = av_mallocz(sizeof(AVPacketList));
- this_pktl->pkt = *pkt;
- if(pkt->destruct == av_destruct_packet)
- pkt->destruct = NULL; // non shared -> must keep original from being freed
- else
- av_dup_packet(&this_pktl->pkt); //shared -> must dup
- next_point = &s->packet_buffer;
- while(*next_point){
- AVStream *st= s->streams[ pkt->stream_index];
- AVStream *st2= s->streams[ (*next_point)->pkt.stream_index];
- int64_t left= st2->time_base.num * (int64_t)st ->time_base.den;
- int64_t right= st ->time_base.num * (int64_t)st2->time_base.den;
- if((*next_point)->pkt.dts * left > pkt->dts * right) //FIXME this can overflow
- break;
- next_point= &(*next_point)->next;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ GXFStreamContext *sc = &gxf->streams[i];
+ if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
+ if (pkt && pkt->stream_index == i) {
+ av_fifo_generic_write(&sc->audio_buffer, pkt->data, pkt->size, NULL);
+ pkt = NULL;
}
- this_pktl->next = *next_point;
- *next_point = this_pktl;
- }
- }
-
- if (gxf->audio_written == gxf->audio_tracks) {
- if (!s->packet_buffer) {
- gxf->audio_written = 0;
- return 0;
- }
- pktl = s->packet_buffer;
- *out = pktl->pkt;
- s->packet_buffer = pktl->next;
- av_freep(&pktl);
- return 1;
- } else {
- for (i = 0; i < s->nb_streams; i++) {
- sc = &gxf->streams[i];
- if (sc->codec->codec_type == CODEC_TYPE_AUDIO &&
- (flush || fifo_size(&sc->audio_buffer, NULL) >= GXF_AUDIO_PACKET_SIZE)) {
- int size = flush ? fifo_size(&sc->audio_buffer, NULL) : GXF_AUDIO_PACKET_SIZE;
- av_new_packet(out, size);
- fifo_read(&sc->audio_buffer, out->data, size, NULL);
- gxf->audio_written++;
- out->stream_index = i;
- return 1;
+ if (flush || av_fifo_size(&sc->audio_buffer) >= GXF_AUDIO_PACKET_SIZE) {
+ if (!pkt && gxf_new_audio_packet(gxf, sc, &new_pkt, flush) > 0) {
+ pkt = &new_pkt;
+ break; /* add pkt right now into list */
+ }
}
+ } else if (pkt && pkt->stream_index == i) {
+ if (sc->dts_delay == GXF_NODELAY) /* adjust dts if needed */
+ sc->dts_delay = pkt->dts;
+ pkt->dts -= sc->dts_delay;
}
}
- av_init_packet(out);
- return 0;
+ return av_interleave_packet_per_dts(s, out, pkt, flush);
}
AVOutputFormat gxf_muxer = {
"gxf",
- "GXF format",
+ NULL_IF_CONFIG_SMALL("GXF format"),
NULL,
"gxf",
sizeof(GXFContext),