/* NAME lex_test.go DESCRIPTION lex_test.go provides tests for the lexer in lex.go. AUTHOR Dan Kortschak Saxon A. Nelson-Milton LICENSE lex_test.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ // lex_test.go provides tests for the lexer in lex.go. package h264 import ( "io" "testing" "time" ) var h264Tests = []struct { name string input []byte delay time.Duration want [][]byte err error }{ { name: "empty", }, { name: "null short", input: []byte{0x00, 0x00, 0x01, 0x0}, delay: 0, want: [][]byte{{0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x0}}, }, { name: "null short delayed", input: []byte{0x00, 0x00, 0x01, 0x0}, delay: time.Millisecond, want: [][]byte{{0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x0}}, }, { name: "full short type 1", input: []byte{ 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h', 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: 0, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full short type 5", input: []byte{ 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x45, 'w', 'i', 't', 'h', 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: 0, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x45, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full short type 8", input: []byte{ 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x48, 'w', 'i', 't', 'h', 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: 0, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x48, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full short delayed", input: []byte{ 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h', 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: time.Millisecond, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full long type 1", input: []byte{ 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: 0, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full long type 5", input: []byte{ 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x45, 'w', 'i', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: 0, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x45, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full long type 8", input: []byte{ 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x48, 'w', 'i', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: 0, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x48, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, { name: "full long delayed", input: []byte{ 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd', }, delay: time.Millisecond, want: [][]byte{ {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'u', 'l', 'l', 0x00, 0x00, 0x00, 0x01, 0x00, 'f', 'r', 'a', 'm', 'e', 0x00, 0x00, 0x00, 0x01, 0x41, 'w', 'i', 't', 'h'}, {0x0, 0x0, 0x1, 0x9, 0xf0, 0x00, 0x00, 0x00, 0x01, 0x00, 'l', 'e', 'n', 'g', 't', 'h', 0x00, 0x00, 0x00, 0x01, 0x00, 's', 'p', 'r', 'e', 'a', 'd'}, }, }, } // FIXME: this needs to be adapted /* func TestH264(t *testing.T) { for _, test := range h264Tests { var buf chunkEncoder err := Lex(&buf, bytes.NewReader(test.input), test.delay) if fmt.Sprint(err) != fmt.Sprint(test.err) { t.Errorf("unexpected error for %q: got:%v want:%v", test.name, err, test.err) } if err != nil { continue } got := [][]byte(buf) if !reflect.DeepEqual(got, test.want) { t.Errorf("unexpected result for %q:\ngot :%#v\nwant:%#v", test.name, got, test.want) } } } */ // rtpReader provides the RTP stream. type rtpReader struct { packets [][]byte idx int } // Read implements io.Reader. func (r *rtpReader) Read(p []byte) (int, error) { if r.idx == len(r.packets) { return 0, io.EOF } b := r.packets[r.idx] n := copy(p, b) if n < len(r.packets[r.idx]) { r.packets[r.idx] = r.packets[r.idx][n:] } else { r.idx++ } return n, nil } // destination holds the access units extracted during the lexing process. type destination [][]byte // Write implements io.Writer. func (d *destination) Write(p []byte) (int, error) { t := make([]byte, len(p)) copy(t, p) *d = append([][]byte(*d), t) return len(p), nil } // TestLex checks that the Lexer can correctly extract H264 access units from // h264 RTP stream in RTP payload format. func TestRTPLex(t *testing.T) { const rtpVer = 2 tests := []struct { packets [][]byte expect [][]byte }{ { packets: [][]byte{ { // Single NAL unit. 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeSingleNALULowBound, // NAL header. 0x01, 0x02, 0x03, 0x04, // NAL Data. }, { // Fragmentation (start packet). 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeFUA, // FU indicator. 0x80 | typeSingleNALULowBound, // FU header. 0x01, 0x02, 0x03, // FU payload. }, { // Fragmentation (middle packet) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeFUA, // NAL indicator. typeSingleNALULowBound, // FU header. 0x04, 0x05, 0x06, // FU payload. }, { // Fragmentation (end packet) 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeFUA, // NAL indicator. 0x40 | typeSingleNALULowBound, // FU header. 0x07, 0x08, 0x09, // FU payload }, { // Aggregation. Make last packet of access unit => marker bit true. 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeSTAPA, // NAL header. 0x00, 0x04, // NAL 1 size. 0x01, 0x02, 0x03, 0x04, // NAL 1 data. 0x00, 0x04, // NAL 2 size. 0x01, 0x02, 0x03, 0x04, // NAL 2 data. }, // Second access unit. { // Single NAL unit. 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeSingleNALULowBound, // NAL header. 0x01, 0x02, 0x03, 0x04, // NAL Data. }, { // Single NAL. Make last packet of access unit => marker bit true. 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // RTP header. typeSingleNALULowBound, // NAL header. 0x01, 0x02, 0x03, 0x04, // NAL data. }, }, expect: [][]byte{ // First access unit. { // NAL 1 0x00, 0x00, 0x00, 0x01, // Start code. typeSingleNALULowBound, // NAL header. 0x01, 0x02, 0x03, 0x04, // NAL data. // NAL 2 0x00, 0x00, 0x00, 0x01, // Start code. typeSingleNALULowBound, 0x01, 0x02, 0x03, // FU payload. 0x04, 0x05, 0x06, // FU payload. 0x07, 0x08, 0x09, // FU payload. // NAL 3 0x00, 0x00, 0x00, 0x01, // Start code. 0x01, 0x02, 0x03, 0x04, // NAL data. // NAL 4 0x00, 0x00, 0x00, 0x01, // Start code. 0x01, 0x02, 0x03, 0x04, // NAL 2 data }, // Second access unit. { // NAL 1 0x00, 0x00, 0x00, 0x01, // Start code. typeSingleNALULowBound, // NAL header. 0x01, 0x02, 0x03, 0x04, // Data. // NAL 2 0x00, 0x00, 0x00, 0x01, // Start code. typeSingleNALULowBound, // NAL header. 0x01, 0x02, 0x03, 0x04, // Data. }, }, }, } for testNum, test := range tests { r := &rtpReader{packets: test.packets} d := &destination{} err := NewRTPLexer().Lex(d, r, 0) if err != nil { t.Fatalf("error lexing: %v\n", err) } for i, accessUnit := range test.expect { for j, part := range accessUnit { if part != [][]byte(*d)[i][j] { t.Fatalf("did not get expected data for test: %v.\nGot: %v\nWant: %v\n", testNum, d, test.expect) } } } } }