Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add media duration to lpms_get_codec_info for GetCodecInfo #407

Merged
merged 27 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eef214e
add fps and duration to GetCodecInfo
ad-astra-video Oct 31, 2023
1fcc47e
update fps and dur calc
ad-astra-video Nov 3, 2023
904f309
add duration if video or audio present
ad-astra-video Jul 3, 2024
6a2b014
add cmd script to test fps and duration
ad-astra-video Jul 3, 2024
0e84f60
revert to using AV_TIME_BASE and add notes for future improvements
ad-astra-video Jul 3, 2024
2b723a4
Fix duration for audio only file
eliteprox Jul 5, 2024
b601475
Code cleanup
eliteprox Jul 5, 2024
bad8498
Fix duration calculation
eliteprox Jul 5, 2024
146d718
Cleanup test
eliteprox Jul 5, 2024
75bd67c
Merge branch 'add-codec-info' of github.com:eliteprox/lpms into add-c…
eliteprox Jul 8, 2024
e6389f2
Move fps duration test to ffmpeg test, add descriptive comment
eliteprox Jul 8, 2024
f05997e
Add tests, cleanup from review
eliteprox Jul 9, 2024
f0eda1a
Generate test files with ffmpeg, check expected duration and fps, rem…
eliteprox Jul 9, 2024
f3c8917
Remove test file
eliteprox Jul 9, 2024
d025d0a
Fix tests and review comments
eliteprox Jul 9, 2024
6579e6e
Save indention
eliteprox Jul 9, 2024
67db771
Remove seek
eliteprox Jul 9, 2024
cc58010
simplify test
eliteprox Jul 10, 2024
8cdef44
Add webm type to test
eliteprox Jul 10, 2024
a8f19c4
add max_analyze_duration and remove strays
eliteprox Jul 10, 2024
d99d32b
Optimize duration calculation
eliteprox Jul 10, 2024
f2edd37
Use the largest pts for last_pts and smallest pts for first
eliteprox Jul 10, 2024
1fd0f5c
Remove calculate_stream_duration, add ffprobe to tests
eliteprox Jul 11, 2024
afaf25d
remove max_analyze_duration
eliteprox Jul 11, 2024
56c2328
Update ffmpeg/ffmpeg_test.go
eliteprox Jul 11, 2024
0e12477
Update ffmpeg/extras.c
eliteprox Jul 11, 2024
c85f817
add ffprobe expected duration and fps values to test
eliteprox Jul 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added data/bunny.flac
Binary file not shown.
Binary file added data/bunny.m4a
Binary file not shown.
Binary file added data/bunny.mp3
Binary file not shown.
Binary file added data/bunny.webm
Binary file not shown.
33 changes: 33 additions & 0 deletions ffmpeg/extras.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,31 @@ int lpms_rtmp2hls(char *listen, char *outf, char *ts_tmpl, char* seg_time, char
return ret == AVERROR_EOF ? 0 : ret;
}

//Calculates the duration of audio stream by counting the number of audio packets
double calculate_audio_dur(AVFormatContext *ic, int astream) {
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
AVPacket pkt;
av_init_packet(&pkt);
double duration = 0;
int64_t last_pts = AV_NOPTS_VALUE;
// Seek to the beginning of the audio stream
av_seek_frame(ic, astream, 0, AVSEEK_FLAG_BACKWARD);
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
while (av_read_frame(ic, &pkt) >= 0) {
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
if (pkt.stream_index == astream) {
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
if (pkt.pts != AV_NOPTS_VALUE) {
if (last_pts != AV_NOPTS_VALUE) {
// Calculate the difference between the current and last PTS
int64_t pts_diff = pkt.pts - last_pts;
// Convert the PTS difference to seconds and add to duration
duration += pts_diff * av_q2d(ic->streams[astream]->time_base);
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
}
last_pts = pkt.pts;
}
av_packet_unref(&pkt);
}
}
return duration;
}

#define GET_CODEC_INTERNAL_ERROR -1
#define GET_CODEC_OK 0
#define GET_CODEC_NEEDS_BYPASS 1
Expand Down Expand Up @@ -163,6 +188,13 @@ int lpms_get_codec_info(char *fname, pcodec_info out)
if(!audio_present && !video_present) {
// instead of returning -1
ret = GET_CODEC_STREAMS_MISSING;
} else if (video_present) {
out->dur = ic->duration / AV_TIME_BASE;
}

if (out->dur <= 0) {
//ic->duration / AV_TIME_BASE doesn't return accurate duration for audio-only streams, so we calculate the duration manually
out->dur = calculate_audio_dur(ic, astream);
j0sh marked this conversation as resolved.
Show resolved Hide resolved
}
// Return
if (video_present && vc->name) {
Expand All @@ -176,6 +208,7 @@ int lpms_get_codec_info(char *fname, pcodec_info out)
}
out->width = ic->streams[vstream]->codecpar->width;
out->height = ic->streams[vstream]->codecpar->height;
out->fps = av_q2d(ic->streams[vstream]->avg_frame_rate);
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
} else {
// Indicate failure to extract video codec from given container
out->video_codec[0] = 0;
Expand Down
2 changes: 2 additions & 0 deletions ffmpeg/extras.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ typedef struct s_codec_info {
int pixel_format;
int width;
int height;
double fps;
double dur;
} codec_info, *pcodec_info;

int lpms_rtmp2hls(char *listen, char *outf, char *ts_tmpl, char *seg_time, char *seg_start);
Expand Down
5 changes: 5 additions & 0 deletions ffmpeg/ffmpeg.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ type MediaFormatInfo struct {
Acodec, Vcodec string
PixFormat PixelFormat
Width, Height int
FPS float32
Dur float32
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
}

func (f *MediaFormatInfo) ScaledHeight(width int) int {
Expand Down Expand Up @@ -277,6 +279,9 @@ func GetCodecInfo(fname string) (CodecStatus, MediaFormatInfo, error) {
format.PixFormat = PixelFormat{int(params_c.pixel_format)}
format.Width = int(params_c.width)
format.Height = int(params_c.height)
format.FPS = float32(params_c.fps)
format.Dur = float32(params_c.dur)

return status, format, nil
}

Expand Down
62 changes: 62 additions & 0 deletions ffmpeg/ffmpeg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1869,3 +1869,65 @@ func TestResolution_Clamp(t *testing.T) {
checkError = require.Error
test(l, Size{300, 600}, portrait, Size{300, 600})
}

func TestDuration_GetCodecInfo(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}

files := []string{"bunny.mp4", "duplicate-audio-dts.ts", "bunny.mp3", "bunny.webm", "bunny.flac", "bunny.m4a"}
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
for _, file := range files {
start := time.Now()
fname := path.Join(wd, "..", "data", file)
_, format, err := GetCodecInfo(fname)
data, err := ioutil.ReadFile(fname)
if err != nil {
t.Error(err)
}

_, format, err = GetCodecInfoBytes(data)
if err != nil {
t.Error(err)
}

took := time.Since(start).Milliseconds()
fmt.Printf("%v\ttook=%v\tdur=%v\tacodec=%v\n", fname, took, format.Dur, format.Acodec)
if format.Dur <= float32(0) {
eliteprox marked this conversation as resolved.
Show resolved Hide resolved
assert.Fail(t, "Duration should be > 0", format.Dur)
} else {
assert.True(t, format.Dur > float32(0), "Duration should be > 0", format.Dur)
}
}
}

func TestFPS_GetCodecInfo(t *testing.T) {
wd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}

files := []string{"bunny.mp4", "duplicate-audio-dts.ts", "bunny.webm"}
for _, file := range files {
start := time.Now()
fname := path.Join(wd, "..", "data", file)
_, format, err := GetCodecInfo(fname)
data, err := ioutil.ReadFile(fname)
if err != nil {
t.Error(err)
}

_, format, err = GetCodecInfoBytes(data)
if err != nil {
t.Error(err)
}

took := time.Since(start).Milliseconds()
fmt.Printf("%v\ttook=%v\tfps=%v\tdur=%v\tacodec=%v\n", fname, took, format.FPS, format.Dur, format.Acodec)
if format.FPS <= float32(0) {
assert.Fail(t, "FPS should be > 0", format.FPS)
} else {
assert.True(t, format.FPS > float32(0), "FPS should be > 0", format.FPS)
}
}
}
Loading