diff --git a/cmd/ffmpeg/resample_audio/main.go b/cmd/ffmpeg/resample_audio/main.go index 198eb73..b3abf51 100644 --- a/cmd/ffmpeg/resample_audio/main.go +++ b/cmd/ffmpeg/resample_audio/main.go @@ -92,7 +92,7 @@ func main() { } // convert to destination format - n, err := ff.SWResample_convert(ctx, dest, dest_nb_samples, src, src_nb_samples) + n, err := ff.SWResample_convert(ctx, dest, src) if err != nil { log.Fatal(err) } diff --git a/pkg/ffmpeg/writer.go b/pkg/ffmpeg/writer.go index c004348..f1bf62a 100644 --- a/pkg/ffmpeg/writer.go +++ b/pkg/ffmpeg/writer.go @@ -230,7 +230,7 @@ func (w *Writer) String() string { // Return a "stream" for encoding func (w *Writer) Stream(stream int) *Encoder { for _, encoder := range w.encoders { - if encoder.stream.Index() == stream { + if encoder.stream.Id() == stream { return encoder } } @@ -327,21 +327,18 @@ func encode(in EncoderFrameFn, out EncoderPacketFn, encoders map[int]*Encoder) e var frame *Frame var err error if !next_encoder.eof { - frame, err = in(next_stream) + // Get the frame based on the id (rather than index) of the stream + frame, err = in(next_encoder.stream.Id()) if errors.Is(err, io.EOF) { next_encoder.eof = true } else if err != nil { - return fmt.Errorf("stream %v: %w", next_stream, err) + return fmt.Errorf("stream %v: %w", next_encoder.stream.Id(), err) } } - // If frame not ready, try again - if frame == nil { - return nil - } // Send a frame for encoding if err := next_encoder.Encode(frame, out); err != nil { - return fmt.Errorf("stream %v: %w", next_stream, err) + return fmt.Errorf("stream %v: %w", next_encoder.stream.Id(), err) } // If eof then delete the encoder @@ -351,7 +348,11 @@ func encode(in EncoderFrameFn, out EncoderPacketFn, encoders map[int]*Encoder) e } // Calculate the next PTS - next_encoder.next_pts = next_encoder.next_pts + next_encoder.nextPts(frame) + if frame != nil { + next_encoder.next_pts = next_encoder.next_pts + next_encoder.nextPts(frame) + } + + // Return success return nil } diff --git a/pkg/ffmpeg/writer_test.go b/pkg/ffmpeg/writer_test.go index d6e4b3e..b22441a 100644 --- a/pkg/ffmpeg/writer_test.go +++ b/pkg/ffmpeg/writer_test.go @@ -86,8 +86,8 @@ func Test_writer_002(t *testing.T) { } defer audio.Close() - // Write 15 mins of frames - duration := float64(15 * 60) + // Write 15 secs of frames + duration := float64(15) assert.NoError(writer.Encode(context.Background(), func(stream int) (*ffmpeg.Frame, error) { frame := audio.Frame() if frame.Ts() >= duration { @@ -166,7 +166,7 @@ func Test_writer_004(t *testing.T) { } defer w.Close() - // Create a writer with an audio stream + // Create a writer with an audio and video stream writer, err := ffmpeg.Create(w.Name(), ffmpeg.OptMetadata(ffmpeg.NewMetadata("title", t.Name())), ffmpeg.OptStream(1, ffmpeg.VideoPar("yuv420p", "640x480", 30)), @@ -200,6 +200,7 @@ func Test_writer_004(t *testing.T) { case 2: frame = audio.Frame() } + t.Log("frame = ", stream, frame) if frame.Ts() >= duration { t.Log("Frame time is EOF", frame.Ts()) return nil, io.EOF diff --git a/sys/ffmpeg61/swresample_convert.go b/sys/ffmpeg61/swresample_convert.go index 6f5ad54..a12c30d 100644 --- a/sys/ffmpeg61/swresample_convert.go +++ b/sys/ffmpeg61/swresample_convert.go @@ -68,17 +68,22 @@ func SWResample_get_out_samples(ctx *SWRContext, in_samples int) (int, error) { // Convert the samples in the input AVFrame and write them to the output AVFrame. func SWResample_convert_frame(ctx *SWRContext, src, dest *AVFrame) error { - // TODO: This is likely a terrible idea but the only thing I can get to work - // at the moment. Later find out why swr_convert_frame isn't working. // Ref: https://stackoverflow.com/questions/77502983/libswresample-why-does-swr-init-change-in-ch-layout-order-so-it-no-longer-m - if err := SWResample_config_frame(ctx, src, dest); err != nil { + if err := AVError(C.swr_convert_frame((*C.struct_SwrContext)(ctx), (*C.struct_AVFrame)(dest), (*C.struct_AVFrame)(src))); err == 0 { + return nil + } else if err != AVERROR_INPUT_CHANGED && err != AVERROR_OUTPUT_CHANGED { + return err + } else if err := SWResample_config_frame(ctx, src, dest); err != nil { return err } + + // Try again if err := AVError(C.swr_convert_frame((*C.struct_SwrContext)(ctx), (*C.struct_AVFrame)(dest), (*C.struct_AVFrame)(src))); err != 0 { return err - } else { - return nil } + + // Return success + return nil } // Configure or reconfigure the SwrContext using the information provided by the AVFrames.