|
|
|
@@ -34,81 +34,21 @@ |
|
|
|
#include "internal.h" |
|
|
|
#include "avio_internal.h" |
|
|
|
|
|
|
|
typedef struct FILMPacket { |
|
|
|
int audio; |
|
|
|
int keyframe; |
|
|
|
int32_t pts; |
|
|
|
int32_t duration; |
|
|
|
int32_t size; |
|
|
|
int32_t index; |
|
|
|
struct FILMPacket *next; |
|
|
|
} FILMPacket; |
|
|
|
|
|
|
|
typedef struct FILMOutputContext { |
|
|
|
AVIOContext *header; |
|
|
|
unsigned index; |
|
|
|
int audio_index; |
|
|
|
int video_index; |
|
|
|
FILMPacket *start; |
|
|
|
FILMPacket *last; |
|
|
|
int64_t packet_count; |
|
|
|
} FILMOutputContext; |
|
|
|
|
|
|
|
static int film_write_packet_to_header(AVFormatContext *format_context, FILMPacket *pkt) |
|
|
|
{ |
|
|
|
AVIOContext *pb = format_context->pb; |
|
|
|
/* The bits in these two 32-bit integers contain info about the contents of this sample */ |
|
|
|
int32_t info1 = 0; |
|
|
|
int32_t info2 = 0; |
|
|
|
|
|
|
|
if (pkt->audio) { |
|
|
|
/* Always the same, carries no more information than "this is audio" */ |
|
|
|
info1 = 0xFFFFFFFF; |
|
|
|
info2 = 1; |
|
|
|
} else { |
|
|
|
info1 = pkt->pts; |
|
|
|
info2 = pkt->duration; |
|
|
|
/* The top bit being set indicates a key frame */ |
|
|
|
if (!pkt->keyframe) |
|
|
|
info1 |= 1U << 31; |
|
|
|
} |
|
|
|
|
|
|
|
/* Write the 16-byte sample info packet to the STAB chunk in the header */ |
|
|
|
avio_wb32(pb, pkt->index); |
|
|
|
avio_wb32(pb, pkt->size); |
|
|
|
avio_wb32(pb, info1); |
|
|
|
avio_wb32(pb, info2); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt) |
|
|
|
{ |
|
|
|
FILMPacket *metadata; |
|
|
|
AVIOContext *pb = format_context->pb; |
|
|
|
FILMOutputContext *film = format_context->priv_data; |
|
|
|
int encoded_buf_size = 0; |
|
|
|
int encoded_buf_size, size = pkt->size; |
|
|
|
uint32_t info1, info2; |
|
|
|
enum AVCodecID codec_id; |
|
|
|
|
|
|
|
/* Track the metadata used to write the header and add it to the linked list */ |
|
|
|
metadata = av_mallocz(sizeof(FILMPacket)); |
|
|
|
if (!metadata) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
metadata->audio = pkt->stream_index == film->audio_index; |
|
|
|
metadata->keyframe = pkt->flags & AV_PKT_FLAG_KEY; |
|
|
|
metadata->pts = pkt->pts; |
|
|
|
metadata->duration = pkt->duration; |
|
|
|
metadata->size = pkt->size; |
|
|
|
if (film->last == NULL) { |
|
|
|
metadata->index = 0; |
|
|
|
} else { |
|
|
|
metadata->index = film->last->index + film->last->size; |
|
|
|
film->last->next = metadata; |
|
|
|
} |
|
|
|
metadata->next = NULL; |
|
|
|
if (film->start == NULL) |
|
|
|
film->start = metadata; |
|
|
|
film->packet_count++; |
|
|
|
film->last = metadata; |
|
|
|
|
|
|
|
codec_id = format_context->streams[pkt->stream_index]->codecpar->codec_id; |
|
|
|
|
|
|
|
/* Sega Cinepak has an extra two-byte header; write dummy data there, |
|
|
|
@@ -123,7 +63,7 @@ static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt) |
|
|
|
* 8 bytes too short. However, the size in the STAB section of the header |
|
|
|
* is correct, taking into account the extra two bytes. */ |
|
|
|
AV_WB24(&pkt->data[1], pkt->size - 8 + 2); |
|
|
|
metadata->size += 2; |
|
|
|
size += 2; |
|
|
|
|
|
|
|
avio_write(pb, pkt->data, 10); |
|
|
|
avio_wb16(pb, 0); |
|
|
|
@@ -134,7 +74,27 @@ static int film_write_packet(AVFormatContext *format_context, AVPacket *pkt) |
|
|
|
avio_write(pb, pkt->data, pkt->size); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
/* Add the 16-byte sample info entry to the dynamic buffer |
|
|
|
* for the STAB chunk in the header */ |
|
|
|
pb = film->header; |
|
|
|
avio_wb32(pb, film->index); |
|
|
|
film->index += size; |
|
|
|
avio_wb32(pb, size); |
|
|
|
if (film->audio_index == pkt->stream_index) { |
|
|
|
/* Always the same, carries no more information than "this is audio" */ |
|
|
|
info1 = 0xFFFFFFFF; |
|
|
|
info2 = 1; |
|
|
|
} else { |
|
|
|
info1 = pkt->pts; |
|
|
|
info2 = pkt->duration; |
|
|
|
/* The top bit being set indicates a key frame */ |
|
|
|
if (!(pkt->flags & AV_PKT_FLAG_KEY)) |
|
|
|
info1 |= 1U << 31; |
|
|
|
} |
|
|
|
avio_wb32(pb, info1); |
|
|
|
avio_wb32(pb, info2); |
|
|
|
|
|
|
|
return pb->error; |
|
|
|
} |
|
|
|
|
|
|
|
static int get_audio_codec_id(enum AVCodecID codec_id) |
|
|
|
@@ -154,11 +114,10 @@ static int get_audio_codec_id(enum AVCodecID codec_id) |
|
|
|
static int film_init(AVFormatContext *format_context) |
|
|
|
{ |
|
|
|
FILMOutputContext *film = format_context->priv_data; |
|
|
|
int ret; |
|
|
|
|
|
|
|
film->audio_index = -1; |
|
|
|
film->video_index = -1; |
|
|
|
film->packet_count = 0; |
|
|
|
film->start = NULL; |
|
|
|
film->last = NULL; |
|
|
|
|
|
|
|
for (int i = 0; i < format_context->nb_streams; i++) { |
|
|
|
AVStream *st = format_context->streams[i]; |
|
|
|
@@ -199,6 +158,8 @@ static int film_init(AVFormatContext *format_context) |
|
|
|
av_log(format_context, AV_LOG_ERROR, "No video stream present.\n"); |
|
|
|
return AVERROR(EINVAL); |
|
|
|
} |
|
|
|
if ((ret = avio_open_dyn_buf(&film->header)) < 0) |
|
|
|
return ret; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
@@ -263,15 +224,17 @@ static int shift_data(AVFormatContext *format_context, int64_t shift_size) |
|
|
|
static int film_write_header(AVFormatContext *format_context) |
|
|
|
{ |
|
|
|
int ret = 0; |
|
|
|
int64_t sample_table_size, stabsize, headersize; |
|
|
|
unsigned sample_table_size, stabsize, headersize, packet_count; |
|
|
|
AVIOContext *pb = format_context->pb; |
|
|
|
FILMOutputContext *film = format_context->priv_data; |
|
|
|
FILMPacket *prev, *packet; |
|
|
|
AVStream *video = NULL; |
|
|
|
uint8_t *sample_table; |
|
|
|
|
|
|
|
/* Calculate how much we need to reserve for the header; |
|
|
|
* this is the amount the rest of the data will be shifted up by. */ |
|
|
|
sample_table_size = film->packet_count * 16; |
|
|
|
sample_table_size = avio_get_dyn_buf(film->header, &sample_table); |
|
|
|
packet_count = sample_table_size / 16; |
|
|
|
sample_table_size = packet_count * 16; |
|
|
|
stabsize = 16 + sample_table_size; |
|
|
|
headersize = 16 + /* FILM header base */ |
|
|
|
32 + /* FDSC chunk */ |
|
|
|
@@ -335,7 +298,7 @@ static int film_write_header(AVFormatContext *format_context) |
|
|
|
|
|
|
|
/* Finally, write the STAB (sample table) chunk */ |
|
|
|
ffio_wfourcc(pb, "STAB"); |
|
|
|
avio_wb32(pb, 16 + (film->packet_count * 16)); |
|
|
|
avio_wb32(pb, stabsize); |
|
|
|
/* Framerate base frequency. Here we're assuming that the frame rate is even. |
|
|
|
* In real world Sega FILM files, there are usually a couple of approaches: |
|
|
|
* a) framerate base frequency is the same as the framerate, and ticks |
|
|
|
@@ -347,17 +310,10 @@ static int film_write_header(AVFormatContext *format_context) |
|
|
|
* are incremented by 25 for an evenly spaced framerate of 24fps. */ |
|
|
|
avio_wb32(pb, av_q2d(av_inv_q(video->time_base))); |
|
|
|
|
|
|
|
avio_wb32(pb, film->packet_count); |
|
|
|
avio_wb32(pb, packet_count); |
|
|
|
|
|
|
|
/* Finally, write out each packet's data to the header */ |
|
|
|
packet = film->start; |
|
|
|
while (packet != NULL) { |
|
|
|
film_write_packet_to_header(format_context, packet); |
|
|
|
prev = packet; |
|
|
|
packet = packet->next; |
|
|
|
av_freep(&prev); |
|
|
|
} |
|
|
|
film->start = film->last = NULL; |
|
|
|
avio_write(pb, sample_table, sample_table_size); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
@@ -365,13 +321,8 @@ static int film_write_header(AVFormatContext *format_context) |
|
|
|
static void film_deinit(AVFormatContext *format_context) |
|
|
|
{ |
|
|
|
FILMOutputContext *film = format_context->priv_data; |
|
|
|
FILMPacket *packet = film->start; |
|
|
|
while (packet != NULL) { |
|
|
|
FILMPacket *next = packet->next; |
|
|
|
av_free(packet); |
|
|
|
packet = next; |
|
|
|
} |
|
|
|
film->start = film->last = NULL; |
|
|
|
|
|
|
|
ffio_free_dyn_buf(&film->header); |
|
|
|
} |
|
|
|
|
|
|
|
AVOutputFormat ff_segafilm_muxer = { |
|
|
|
|