diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index d974f777..12060d23 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -1000,8 +1000,10 @@ void FFmpegReader::UpdateAudioInfo() { info.fps.den = 1; info.video_timebase.num = 1; info.video_timebase.den = 30; - info.width = 720; - info.height = 480; + if (info.width <= 0 || info.height <= 0) { + info.width = 720; + info.height = 480; + } // Use timeline to set correct width & height (if any) Clip *parent = static_cast(ParentClip()); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index 84a45651..eedd0586 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -1486,6 +1486,10 @@ void Timeline::apply_json_to_clips(Json::Value change) { // Keep track of allocated clip objects allocated_clips.insert(clip); + // Match full timeline JSON loading: parent timeline must be available + // before clip JSON can inflate nested readers/effects. + clip->ParentTimeline(this); + // Set properties of clip from JSON clip->SetJsonValue(change["value"]); diff --git a/tests/FFmpegReader.cpp b/tests/FFmpegReader.cpp index fe8f88e1..2d0aa2da 100644 --- a/tests/FFmpegReader.cpp +++ b/tests/FFmpegReader.cpp @@ -143,6 +143,36 @@ TEST_CASE( "Check_Audio_File", "[libopenshot][ffmpegreader]" ) r.Close(); } +TEST_CASE( "Audio_Only_SetJson_Preserves_Stored_Dimensions", "[libopenshot][ffmpegreader][json]" ) +{ + std::stringstream path; + path << TEST_MEDIA_PATH << "piano.wav"; + + FFmpegReader r(path.str()); + std::stringstream json; + json << "{\"type\":\"FFmpegReader\"," + << "\"path\":\"" << path.str() << "\"," + << "\"has_audio\":true," + << "\"has_video\":false," + << "\"width\":1280," + << "\"height\":720," + << "\"fps\":{\"num\":30,\"den\":1}," + << "\"sample_rate\":44100," + << "\"channels\":2," + << "\"channel_layout\":3," + << "\"duration\":1.0}"; + + r.SetJson(json.str()); + r.Open(); + + CHECK(r.info.has_audio); + CHECK_FALSE(r.info.has_video); + CHECK(r.info.width == 1280); + CHECK(r.info.height == 720); + CHECK(r.GetFrame(1)->GetWidth() == 1280); + CHECK(r.GetFrame(1)->GetHeight() == 720); +} + TEST_CASE( "Check_Video_File", "[libopenshot][ffmpegreader]" ) { // Create a reader diff --git a/tests/Timeline.cpp b/tests/Timeline.cpp index 1820e29a..7b75bb12 100644 --- a/tests/Timeline.cpp +++ b/tests/Timeline.cpp @@ -480,6 +480,48 @@ TEST_CASE("ReaderInfo constructor", "[libopenshot][timeline]") CHECK(r1->info.channel_layout == t1.info.channel_layout); } +TEST_CASE("JSON diff insert keeps audio-only clip at stored timeline dimensions", "[libopenshot][timeline][json]") +{ + std::stringstream path; + path << TEST_MEDIA_PATH << "piano.wav"; + + Timeline t(1280, 720, Fraction(30, 1), 44100, 2, LAYOUT_STEREO); + std::stringstream diff; + diff << "[{\"type\":\"insert\",\"key\":[\"clips\"],\"value\":{" + << "\"id\":\"audio-copy\"," + << "\"position\":0," + << "\"start\":0," + << "\"end\":1," + << "\"duration\":1," + << "\"layer\":1," + << "\"reader\":{\"type\":\"FFmpegReader\"," + << "\"path\":\"" << path.str() << "\"," + << "\"has_audio\":true," + << "\"has_video\":false," + << "\"width\":1280," + << "\"height\":720," + << "\"fps\":{\"num\":30,\"den\":1}," + << "\"sample_rate\":44100," + << "\"channels\":2," + << "\"channel_layout\":3," + << "\"duration\":1.0}" + << "}}]"; + + t.ApplyJsonDiff(diff.str()); + + std::list clips = t.Clips(); + REQUIRE(clips.size() == 1); + Clip* clip = clips.front(); + CHECK(clip->ParentTimeline() == &t); + CHECK(clip->Reader()->info.width == 1280); + CHECK(clip->Reader()->info.height == 720); + + std::shared_ptr frame = t.GetFrame(1); + REQUIRE(frame); + CHECK(frame->GetWidth() == 1280); + CHECK(frame->GetHeight() == 720); +} + TEST_CASE( "width and height functions", "[libopenshot][timeline]" ) { Fraction fps(30000,1000);