Skip to content

Commit 07b5266

Browse files
Try to fix timestamps
1 parent 509d3ea commit 07b5266

File tree

1 file changed

+187
-5
lines changed

1 file changed

+187
-5
lines changed

FFmpegWrapper/FFmpegWrapper.m

Lines changed: 187 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#define VSYNC_DROP 0xff
4242

4343
@interface FFStream : NSObject
44+
@property (nonatomic) AVRational frameRate;
4445
@property (nonatomic) AVStream *stream;
4546
- (id) initWithStream:(AVStream*)newStream;
4647
@end
@@ -55,12 +56,48 @@ - (id) initWithStream:(AVStream *)newStream {
5556
}
5657
@end
5758

59+
@interface FFInputStream : FFStream
60+
/* predicted dts of the next packet read for this stream or (when there are
61+
* several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */
62+
@property (nonatomic) int64_t nextDTS;
63+
@property (nonatomic) int64_t DTS; ///< dts of the last packet read for this stream (in AV_TIME_BASE units)
64+
65+
@property (nonatomic) int64_t nextPTS; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units)
66+
@property (nonatomic) int64_t PTS; ///< current pts of the decoded frame (in AV_TIME_BASE units)
67+
@property (nonatomic) int64_t filterInRescaleDeltaLast;
68+
69+
@property (nonatomic) BOOL sawFirstTS;
70+
@end
71+
72+
@implementation FFInputStream
73+
@synthesize nextDTS, DTS, nextPTS, PTS;
74+
- (id) initWithStream:(AVStream *)newStream {
75+
if (self = [super initWithStream:newStream]) {
76+
self.nextPTS = AV_NOPTS_VALUE;
77+
self.PTS = AV_NOPTS_VALUE;
78+
self.nextDTS = AV_NOPTS_VALUE;
79+
self.DTS = AV_NOPTS_VALUE;
80+
self.filterInRescaleDeltaLast = AV_NOPTS_VALUE;
81+
self.sawFirstTS = NO;
82+
}
83+
return self;
84+
}
85+
@end
86+
5887
@interface FFOutputStream : FFStream
5988
@property (nonatomic) int64_t lastMuxDTS;
89+
@property (nonatomic) int frameNumber;
6090
@end
6191

6292
@implementation FFOutputStream
63-
@synthesize lastMuxDTS;
93+
@synthesize lastMuxDTS, frameNumber;
94+
- (id) initWithStream:(AVStream *)newStream {
95+
if (self = [super initWithStream:newStream]) {
96+
self.lastMuxDTS = AV_NOPTS_VALUE;
97+
self.frameNumber = 0;
98+
}
99+
return self;
100+
}
64101
@end
65102

66103
@implementation FFmpegWrapper
@@ -103,6 +140,136 @@ + (NSError*) errorWithCode:(int)errorCode localizedDescription:(NSString*)descri
103140
return [NSError errorWithDomain:kFFmpegErrorDomain code:errorCode userInfo:userInfo];
104141
}
105142

143+
+ (void) copyInputStream:(FFInputStream*)inputStream outputStream:(FFOutputStream*)outputStream packet:(AVPacket*)packet outputFormatContext:(AVFormatContext*)outputFormatContext
144+
{
145+
int64_t ost_tb_start_time = av_rescale_q(0, AV_TIME_BASE_Q, outputStream.stream->time_base);
146+
AVPicture picture;
147+
AVPacket outputPacket;
148+
149+
av_init_packet(&outputPacket);
150+
151+
if (packet->pts != AV_NOPTS_VALUE)
152+
outputPacket.pts = av_rescale_q(packet->pts, inputStream.stream->time_base, outputStream.stream->time_base) - ost_tb_start_time;
153+
else
154+
outputPacket.pts = AV_NOPTS_VALUE;
155+
156+
if (packet->dts == AV_NOPTS_VALUE)
157+
outputPacket.dts = av_rescale_q(inputStream.DTS, AV_TIME_BASE_Q, outputStream.stream->time_base);
158+
else
159+
outputPacket.dts = av_rescale_q(packet->dts, inputStream.stream->time_base, outputStream.stream->time_base);
160+
outputPacket.dts -= ost_tb_start_time;
161+
162+
if (outputStream.stream->codec->codec_type == AVMEDIA_TYPE_AUDIO && packet->dts != AV_NOPTS_VALUE) {
163+
int duration = av_get_audio_frame_duration(inputStream.stream->codec, packet->size);
164+
if(!duration)
165+
duration = inputStream.stream->codec->frame_size;
166+
int64_t filter_in_rescale_delta_last;
167+
outputPacket.dts = outputPacket.pts = av_rescale_delta(inputStream.stream->time_base, packet->dts,
168+
(AVRational){1, inputStream.stream->codec->sample_rate}, duration, &filter_in_rescale_delta_last,
169+
outputStream.stream->time_base) - ost_tb_start_time;
170+
inputStream.filterInRescaleDeltaLast = filter_in_rescale_delta_last;
171+
}
172+
173+
outputPacket.duration = av_rescale_q(packet->duration, inputStream.stream->time_base, outputStream.stream->time_base);
174+
outputPacket.flags = packet->flags;
175+
176+
// FIXME remove the following 2 lines they shall be replaced by the bitstream filters
177+
if ( outputStream.stream->codec->codec_id != AV_CODEC_ID_H264
178+
&& outputStream.stream->codec->codec_id != AV_CODEC_ID_MPEG1VIDEO
179+
&& outputStream.stream->codec->codec_id != AV_CODEC_ID_MPEG2VIDEO
180+
&& outputStream.stream->codec->codec_id != AV_CODEC_ID_VC1
181+
) {
182+
if (av_parser_change(inputStream.stream->parser, outputStream.stream->codec, &outputPacket.data, &outputPacket.size, packet->data, packet->size, packet->flags & AV_PKT_FLAG_KEY)) {
183+
outputPacket.buf = av_buffer_create(outputPacket.data, outputPacket.size, av_buffer_default_free, NULL, 0);
184+
if (!outputPacket.buf) {
185+
NSLog(@"couldnt allocate packet buffer");
186+
}
187+
}
188+
} else {
189+
outputPacket.data = packet->data;
190+
outputPacket.size = packet->size;
191+
}
192+
193+
if (outputStream.stream->codec->codec_type == AVMEDIA_TYPE_VIDEO && (outputFormatContext->oformat->flags & AVFMT_RAWPICTURE)) {
194+
/* store AVPicture in AVPacket, as expected by the output format */
195+
avpicture_fill(&picture, outputPacket.data, outputStream.stream->codec->pix_fmt, outputStream.stream->codec->width, outputStream.stream->codec->height);
196+
outputPacket.data = (uint8_t *)&picture;
197+
outputPacket.size = sizeof(AVPicture);
198+
outputPacket.flags |= AV_PKT_FLAG_KEY;
199+
}
200+
201+
//write_frame(of->ctx, &outputPacket, ost);
202+
outputStream.stream->codec->frame_number++;
203+
}
204+
205+
206+
/* pkt = NULL means EOF (needed to flush decoder buffers) */
207+
+ (int) processInputStream:(FFInputStream*)inputStream outputStream:(FFOutputStream*)outputStream packet:(AVPacket*)packet outputFormatContext:(AVFormatContext*)outputFormatContext
208+
{
209+
AVPacket avpkt;
210+
if (!inputStream.sawFirstTS) {
211+
inputStream.DTS = inputStream.stream->avg_frame_rate.num ? - inputStream.stream->codec->has_b_frames * AV_TIME_BASE / av_q2d(inputStream.stream->avg_frame_rate) : 0;
212+
inputStream.PTS = 0;
213+
if (packet != NULL && packet->pts != AV_NOPTS_VALUE) {
214+
inputStream.DTS += av_rescale_q(packet->pts, inputStream.stream->time_base, AV_TIME_BASE_Q);
215+
inputStream.PTS = inputStream.DTS; //unused but better to set it to a value thats not totally wrong
216+
}
217+
inputStream.sawFirstTS = YES;
218+
}
219+
220+
if (inputStream.nextDTS == AV_NOPTS_VALUE)
221+
inputStream.nextDTS = inputStream.DTS;
222+
if (inputStream.nextPTS == AV_NOPTS_VALUE)
223+
inputStream.nextPTS = inputStream.PTS;
224+
225+
if (packet == NULL) {
226+
/* EOF handling */
227+
av_init_packet(&avpkt);
228+
avpkt.data = NULL;
229+
avpkt.size = 0;
230+
//goto handle_eof;
231+
} else {
232+
avpkt = *packet;
233+
}
234+
235+
if (packet->dts != AV_NOPTS_VALUE) {
236+
inputStream.nextDTS = inputStream.DTS = av_rescale_q(packet->dts, inputStream.stream->time_base, AV_TIME_BASE_Q);
237+
inputStream.nextPTS = inputStream.PTS = inputStream.DTS;
238+
}
239+
240+
/* handle stream copy */
241+
inputStream.DTS = inputStream.nextDTS;
242+
switch (inputStream.stream->codec->codec_type) {
243+
case AVMEDIA_TYPE_AUDIO:
244+
inputStream.nextDTS += ((int64_t)AV_TIME_BASE * inputStream.stream->codec->frame_size) /
245+
inputStream.stream->codec->sample_rate;
246+
break;
247+
case AVMEDIA_TYPE_VIDEO:
248+
if (inputStream.frameRate.num) {
249+
// TODO: Remove work-around for c99-to-c89 issue 7
250+
AVRational time_base_q = AV_TIME_BASE_Q;
251+
int64_t next_dts = av_rescale_q(inputStream.nextDTS, time_base_q, av_inv_q(inputStream.frameRate));
252+
inputStream.nextDTS = av_rescale_q(next_dts + 1, av_inv_q(inputStream.frameRate), time_base_q);
253+
} else if (packet->duration) {
254+
inputStream.nextDTS += av_rescale_q(packet->duration, inputStream.stream->time_base, AV_TIME_BASE_Q);
255+
} else if(inputStream.stream->codec->time_base.num != 0) {
256+
int ticks= inputStream.stream->parser ? inputStream.stream->parser->repeat_pict + 1 : inputStream.stream->codec->ticks_per_frame;
257+
inputStream.nextDTS += ((int64_t)AV_TIME_BASE *
258+
inputStream.stream->codec->time_base.num * ticks) /
259+
inputStream.stream->codec->time_base.den;
260+
}
261+
break;
262+
default:
263+
break;
264+
}
265+
inputStream.PTS = inputStream.DTS;
266+
inputStream.nextPTS = inputStream.nextDTS;
267+
268+
[[self class] copyInputStream:inputStream outputStream:outputStream packet:packet outputFormatContext:outputFormatContext];
269+
270+
return 0;
271+
}
272+
106273
+ (NSError*) errorForAVErrorNumber:(int)errorNumber {
107274
NSString *description = [self stringForAVErrorNumber:errorNumber];
108275
return [self errorWithCode:errorNumber localizedDescription:description];
@@ -122,6 +289,7 @@ - (void) convertInputPath:(NSString*)inputPath outputPath:(NSString*)outputPath
122289
BOOL success = NO;
123290
int video_sync_method = VSYNC_PASSTHROUGH;
124291
int audio_sync_method = 0;
292+
int64_t videoDTS = 0;
125293
NSError *error = nil;
126294
NSFileManager *fileManager = [NSFileManager defaultManager];
127295
NSDictionary *inputFileAttributes = [fileManager attributesOfItemAtPath:inputPath error:&error];
@@ -180,11 +348,13 @@ - (void) convertInputPath:(NSString*)inputPath outputPath:(NSString*)outputPath
180348
int copy_tb = -1;
181349
for (int i = 0; i < inputStreamCount; i++) {
182350
AVStream *inputStream = inputFormatContext->streams[i];
183-
[inputStreams addObject:[[FFStream alloc] initWithStream:inputStream]];
351+
FFInputStream *ffInputStream = [[FFInputStream alloc] initWithStream:inputStream];
352+
[inputStreams addObject:ffInputStream];
184353
AVCodecContext *inputCodecContext = inputStream->codec;
185354
AVCodec *outputCodec = avcodec_find_encoder(inputCodecContext->codec_id);
186355
AVStream *outputStream = avformat_new_stream(outputFormatContext, outputCodec);
187-
[outputStreams addObject:[[FFOutputStream alloc] initWithStream:outputStream]];
356+
FFOutputStream *ffOutputStream = [[FFOutputStream alloc] initWithStream:outputStream];
357+
[outputStreams addObject:ffOutputStream];
188358

189359
AVCodecContext *outputCodecContext = outputStream->codec;
190360

@@ -252,6 +422,11 @@ - (void) convertInputPath:(NSString*)inputPath outputPath:(NSString*)outputPath
252422
outputCodecContext->time_base = inputCodecContext->time_base;
253423
}
254424

425+
if (ffInputStream && !ffOutputStream.frameRate.num)
426+
ffOutputStream.frameRate = ffInputStream.frameRate;
427+
if(ffOutputStream.frameRate.num)
428+
outputCodecContext->time_base = av_inv_q(ffOutputStream.frameRate);
429+
255430
av_reduce(&outputCodecContext->time_base.num, &outputCodecContext->time_base.den,
256431
outputCodecContext->time_base.num, outputCodecContext->time_base.den, INT_MAX);
257432

@@ -273,8 +448,12 @@ - (void) convertInputPath:(NSString*)inputPath outputPath:(NSString*)outputPath
273448
outputCodecContext->width = inputCodecContext->width;
274449
outputCodecContext->height = inputCodecContext->height;
275450
outputCodecContext->has_b_frames = inputCodecContext->has_b_frames;
276-
sar = inputCodecContext->sample_aspect_ratio;
277-
break;
451+
if (inputStream->sample_aspect_ratio.num)
452+
sar = inputStream->sample_aspect_ratio;
453+
else
454+
sar = inputCodecContext->sample_aspect_ratio;
455+
outputStream->sample_aspect_ratio = inputCodecContext->sample_aspect_ratio = sar;
456+
outputStream->avg_frame_rate = inputStream->avg_frame_rate; break;
278457
case AVMEDIA_TYPE_SUBTITLE:
279458
outputCodecContext->width = inputCodecContext->width;
280459
outputCodecContext->height = inputCodecContext->height;
@@ -329,6 +508,9 @@ - (void) convertInputPath:(NSString*)inputPath outputPath:(NSString*)outputPath
329508
}
330509

331510
FFOutputStream *ffOutputStream = [outputStreams objectAtIndex:packet->stream_index];
511+
FFInputStream *ffInputStream = [inputStreams objectAtIndex:packet->stream_index];
512+
513+
[[self class] processInputStream:ffInputStream outputStream:ffOutputStream packet:packet outputFormatContext:outputFormatContext];
332514

333515
AVStream *outputStream = ffOutputStream.stream;
334516

0 commit comments

Comments
 (0)