github.com/pion/webrtc/v4@v4.0.1/pkg/media/ivfwriter/ivfwriter_test.go (about)

     1  // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
     2  // SPDX-License-Identifier: MIT
     3  
     4  package ivfwriter
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"io"
    10  	"testing"
    11  
    12  	"github.com/pion/rtp"
    13  	"github.com/pion/rtp/codecs"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  type ivfWriterPacketTest struct {
    18  	buffer       io.Writer
    19  	message      string
    20  	messageClose string
    21  	packet       *rtp.Packet
    22  	writer       *IVFWriter
    23  	err          error
    24  	closeErr     error
    25  }
    26  
    27  func TestIVFWriter_Basic(t *testing.T) {
    28  	assert := assert.New(t)
    29  	addPacketTestCase := []ivfWriterPacketTest{
    30  		{
    31  			buffer:       &bytes.Buffer{},
    32  			message:      "IVFWriter shouldn't be able to write something to a closed file",
    33  			messageClose: "IVFWriter should be able to close an already closed file",
    34  			packet:       nil,
    35  			err:          errFileNotOpened,
    36  			closeErr:     nil,
    37  		},
    38  		{
    39  			buffer:       &bytes.Buffer{},
    40  			message:      "IVFWriter shouldn't be able to write something an empty packet",
    41  			messageClose: "IVFWriter should be able to close the file",
    42  			packet:       &rtp.Packet{},
    43  			err:          errInvalidNilPacket,
    44  			closeErr:     nil,
    45  		},
    46  		{
    47  			buffer:       nil,
    48  			message:      "IVFWriter shouldn't be able to write something to a closed file",
    49  			messageClose: "IVFWriter should be able to close an already closed file",
    50  			packet:       nil,
    51  			err:          errFileNotOpened,
    52  			closeErr:     nil,
    53  		},
    54  	}
    55  
    56  	// First test case has a 'nil' file descriptor
    57  	writer, err := NewWith(addPacketTestCase[0].buffer)
    58  	assert.Nil(err, "IVFWriter should be created")
    59  	assert.NotNil(writer, "Writer shouldn't be nil")
    60  	assert.False(writer.seenKeyFrame, "Writer's seenKeyFrame should initialize false")
    61  	assert.Equal(uint64(0), writer.count, "Writer's packet count should initialize 0")
    62  	err = writer.Close()
    63  	assert.Nil(err, "IVFWriter should be able to close the stream")
    64  	writer.ioWriter = nil
    65  	addPacketTestCase[0].writer = writer
    66  
    67  	// Second test tries to write an empty packet
    68  	writer, err = NewWith(addPacketTestCase[1].buffer)
    69  	assert.Nil(err, "IVFWriter should be created")
    70  	assert.NotNil(writer, "Writer shouldn't be nil")
    71  	assert.False(writer.seenKeyFrame, "Writer's seenKeyFrame should initialize false")
    72  	assert.Equal(uint64(0), writer.count, "Writer's packet count should initialize 0")
    73  	addPacketTestCase[1].writer = writer
    74  
    75  	// Fourth test tries to write to a nil stream
    76  	writer, err = NewWith(addPacketTestCase[2].buffer)
    77  	assert.NotNil(err, "IVFWriter shouldn't be created")
    78  	assert.Nil(writer, "Writer should be nil")
    79  	addPacketTestCase[2].writer = writer
    80  }
    81  
    82  func TestIVFWriter_VP8(t *testing.T) {
    83  	// Construct valid packet
    84  	rawValidPkt := []byte{
    85  		0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64,
    86  		0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x36, 0xbe, 0x89, 0x9e,
    87  	}
    88  
    89  	validPacket := &rtp.Packet{
    90  		Header: rtp.Header{
    91  			Marker:           true,
    92  			Extension:        true,
    93  			ExtensionProfile: 1,
    94  			Version:          2,
    95  			PayloadType:      96,
    96  			SequenceNumber:   27023,
    97  			Timestamp:        3653407706,
    98  			SSRC:             476325762,
    99  			CSRC:             []uint32{},
   100  		},
   101  		Payload: rawValidPkt[20:],
   102  	}
   103  	assert.NoError(t, validPacket.SetExtension(0, []byte{0xFF, 0xFF, 0xFF, 0xFF}))
   104  
   105  	// Construct mid partition packet
   106  	rawMidPartPkt := []byte{
   107  		0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64,
   108  		0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0x36, 0xbe, 0x89, 0x9e,
   109  	}
   110  
   111  	midPartPacket := &rtp.Packet{
   112  		Header: rtp.Header{
   113  			Marker:           true,
   114  			Extension:        true,
   115  			ExtensionProfile: 1,
   116  			Version:          2,
   117  			PayloadType:      96,
   118  			SequenceNumber:   27023,
   119  			Timestamp:        3653407706,
   120  			SSRC:             476325762,
   121  			CSRC:             []uint32{},
   122  		},
   123  		Payload: rawMidPartPkt[20:],
   124  	}
   125  	assert.NoError(t, midPartPacket.SetExtension(0, []byte{0xFF, 0xFF, 0xFF, 0xFF}))
   126  
   127  	// Construct keyframe packet
   128  	rawKeyframePkt := []byte{
   129  		0x90, 0xe0, 0x69, 0x8f, 0xd9, 0xc2, 0x93, 0xda, 0x1c, 0x64,
   130  		0x27, 0x82, 0x00, 0x01, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x98, 0x36, 0xbe, 0x88, 0x9e,
   131  	}
   132  
   133  	keyframePacket := &rtp.Packet{
   134  		Header: rtp.Header{
   135  			Marker:           true,
   136  			Extension:        true,
   137  			ExtensionProfile: 1,
   138  			Version:          2,
   139  			PayloadType:      96,
   140  			SequenceNumber:   27023,
   141  			Timestamp:        3653407706,
   142  			SSRC:             476325762,
   143  			CSRC:             []uint32{},
   144  		},
   145  		Payload: rawKeyframePkt[20:],
   146  	}
   147  	assert.NoError(t, keyframePacket.SetExtension(0, []byte{0xFF, 0xFF, 0xFF, 0xFF}))
   148  
   149  	assert := assert.New(t)
   150  
   151  	// Check valid packet parameters
   152  	vp8Packet := codecs.VP8Packet{}
   153  	_, err := vp8Packet.Unmarshal(validPacket.Payload)
   154  	assert.Nil(err, "Packet did not process")
   155  	assert.Equal(uint8(1), vp8Packet.S, "Start packet S value should be 1")
   156  	assert.Equal(uint8(1), vp8Packet.Payload[0]&0x01, "Non Keyframe packet P value should be 1")
   157  
   158  	// Check mid partition packet parameters
   159  	vp8Packet = codecs.VP8Packet{}
   160  	_, err = vp8Packet.Unmarshal(midPartPacket.Payload)
   161  	assert.Nil(err, "Packet did not process")
   162  	assert.Equal(uint8(0), vp8Packet.S, "Mid Partition packet S value should be 0")
   163  	assert.Equal(uint8(1), vp8Packet.Payload[0]&0x01, "Non Keyframe packet P value should be 1")
   164  
   165  	// Check keyframe packet parameters
   166  	vp8Packet = codecs.VP8Packet{}
   167  	_, err = vp8Packet.Unmarshal(keyframePacket.Payload)
   168  	assert.Nil(err, "Packet did not process")
   169  	assert.Equal(uint8(1), vp8Packet.S, "Start packet S value should be 1")
   170  	assert.Equal(uint8(0), vp8Packet.Payload[0]&0x01, "Keyframe packet P value should be 0")
   171  
   172  	// The linter misbehave and thinks this code is the same as the tests in oggwriter_test
   173  	// nolint:dupl
   174  	addPacketTestCase := []ivfWriterPacketTest{
   175  		{
   176  			buffer:       &bytes.Buffer{},
   177  			message:      "IVFWriter should be able to write an IVF packet",
   178  			messageClose: "IVFWriter should be able to close the file",
   179  			packet:       validPacket,
   180  			err:          nil,
   181  			closeErr:     nil,
   182  		},
   183  		{
   184  			buffer:       &bytes.Buffer{},
   185  			message:      "IVFWriter should be able to write a Keframe IVF packet",
   186  			messageClose: "IVFWriter should be able to close the file",
   187  			packet:       keyframePacket,
   188  			err:          nil,
   189  			closeErr:     nil,
   190  		},
   191  	}
   192  
   193  	// first test tries to write a valid VP8 packet
   194  	writer, err := NewWith(addPacketTestCase[0].buffer, WithCodec(mimeTypeVP8))
   195  	assert.Nil(err, "IVFWriter should be created")
   196  	assert.NotNil(writer, "Writer shouldn't be nil")
   197  	assert.False(writer.seenKeyFrame, "Writer's seenKeyFrame should initialize false")
   198  	assert.Equal(uint64(0), writer.count, "Writer's packet count should initialize 0")
   199  	addPacketTestCase[0].writer = writer
   200  
   201  	// second test tries to write a keyframe packet
   202  	writer, err = NewWith(addPacketTestCase[1].buffer)
   203  	assert.Nil(err, "IVFWriter should be created")
   204  	assert.NotNil(writer, "Writer shouldn't be nil")
   205  	assert.False(writer.seenKeyFrame, "Writer's seenKeyFrame should initialize false")
   206  	assert.Equal(uint64(0), writer.count, "Writer's packet count should initialize 0")
   207  	addPacketTestCase[1].writer = writer
   208  
   209  	for _, t := range addPacketTestCase {
   210  		if t.writer != nil {
   211  			res := t.writer.WriteRTP(t.packet)
   212  			assert.Equal(res, t.err, t.message)
   213  		}
   214  	}
   215  
   216  	// Third test tries to write a valid VP8 packet - No Keyframe
   217  	assert.False(addPacketTestCase[0].writer.seenKeyFrame, "Writer's seenKeyFrame should remain false")
   218  	assert.Equal(uint64(0), addPacketTestCase[0].writer.count, "Writer's packet count should remain 0")
   219  	assert.Equal(nil, addPacketTestCase[0].writer.WriteRTP(midPartPacket), "Write packet failed") // add a mid partition packet
   220  	assert.Equal(uint64(0), addPacketTestCase[0].writer.count, "Writer's packet count should remain 0")
   221  
   222  	// Fifth test tries to write a keyframe packet
   223  	assert.True(addPacketTestCase[1].writer.seenKeyFrame, "Writer's seenKeyFrame should now be true")
   224  	assert.Equal(uint64(1), addPacketTestCase[1].writer.count, "Writer's packet count should now be 1")
   225  	assert.Equal(nil, addPacketTestCase[1].writer.WriteRTP(midPartPacket), "Write packet failed") // add a mid partition packet
   226  	assert.Equal(uint64(1), addPacketTestCase[1].writer.count, "Writer's packet count should remain 1")
   227  	assert.Equal(nil, addPacketTestCase[1].writer.WriteRTP(validPacket), "Write packet failed") // add a valid packet
   228  	assert.Equal(uint64(2), addPacketTestCase[1].writer.count, "Writer's packet count should now be 2")
   229  
   230  	for _, t := range addPacketTestCase {
   231  		if t.writer != nil {
   232  			res := t.writer.Close()
   233  			assert.Equal(res, t.closeErr, t.messageClose)
   234  		}
   235  	}
   236  }
   237  
   238  func TestIVFWriter_EmptyPayload(t *testing.T) {
   239  	buffer := &bytes.Buffer{}
   240  
   241  	writer, err := NewWith(buffer)
   242  	assert.NoError(t, err)
   243  
   244  	assert.NoError(t, writer.WriteRTP(&rtp.Packet{Payload: []byte{}}))
   245  }
   246  
   247  func TestIVFWriter_Errors(t *testing.T) {
   248  	// Creating a Writer with AV1 and VP8
   249  	_, err := NewWith(&bytes.Buffer{}, WithCodec(mimeTypeAV1), WithCodec(mimeTypeAV1))
   250  	assert.ErrorIs(t, err, errCodecAlreadySet)
   251  
   252  	// Creating a Writer with Invalid Codec
   253  	_, err = NewWith(&bytes.Buffer{}, WithCodec(""))
   254  	assert.ErrorIs(t, err, errNoSuchCodec)
   255  }
   256  
   257  func TestIVFWriter_AV1(t *testing.T) {
   258  	t.Run("Unfragmented", func(t *testing.T) {
   259  		buffer := &bytes.Buffer{}
   260  
   261  		expectedTimestamp := uint32(3653407706)
   262  
   263  		// the timestamp is an uint32, 4 bytes from offset 36
   264  		expectedPayloadWithTimestamp := []byte{
   265  			0x44, 0x4b, 0x49, 0x46, 0x0, 0x0, 0x20,
   266  			0x0, 0x41, 0x56, 0x30, 0x31, 0x80, 0x2,
   267  			0xe0, 0x1, 0x1e, 0x0, 0x0, 0x0, 0x1, 0x0,
   268  			0x0, 0x0, 0x84, 0x3, 0x0, 0x0, 0x0, 0x0,
   269  			0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xda, 0x93, 0xc2,
   270  			0xd9, 0x0, 0x0, 0x0, 0x0, 0xff,
   271  		}
   272  
   273  		writer, err := NewWith(buffer, WithCodec(mimeTypeAV1))
   274  		assert.NoError(t, err)
   275  
   276  		assert.NoError(t, writer.WriteRTP(&rtp.Packet{Header: rtp.Header{Timestamp: expectedTimestamp}, Payload: []byte{0x00, 0x01, 0xFF}}))
   277  		assert.NoError(t, writer.Close())
   278  		assert.Equal(t, expectedPayloadWithTimestamp, buffer.Bytes())
   279  		assert.Equal(t, expectedTimestamp, binary.LittleEndian.Uint32(expectedPayloadWithTimestamp[36:40]))
   280  	})
   281  
   282  	t.Run("Fragmented", func(t *testing.T) {
   283  		buffer := &bytes.Buffer{}
   284  
   285  		writer, err := NewWith(buffer, WithCodec(mimeTypeAV1))
   286  		assert.NoError(t, err)
   287  
   288  		for _, p := range [][]byte{{0x40, 0x02, 0x00, 0x01}, {0xc0, 0x02, 0x02, 0x03}, {0xc0, 0x02, 0x04, 0x04}} {
   289  			assert.NoError(t, writer.WriteRTP(&rtp.Packet{Payload: p}))
   290  			assert.Equal(t, buffer.Bytes(), []byte{
   291  				0x44, 0x4b, 0x49, 0x46, 0x0,
   292  				0x0, 0x20, 0x0, 0x41, 0x56, 0x30,
   293  				0x31, 0x80, 0x2, 0xe0, 0x1, 0x1e,
   294  				0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
   295  				0x0, 0x84, 0x3, 0x0, 0x0, 0x0, 0x0,
   296  				0x0, 0x0,
   297  			})
   298  		}
   299  		assert.NoError(t, writer.WriteRTP(&rtp.Packet{Payload: []byte{0x80, 0x01, 0x05}}))
   300  		assert.Equal(t, buffer.Bytes(), []byte{
   301  			0x44, 0x4b, 0x49, 0x46, 0x0, 0x0, 0x20, 0x0, 0x41, 0x56, 0x30, 0x31, 0x80,
   302  			0x2, 0xe0, 0x1, 0x1e, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x84, 0x3, 0x0, 0x0,
   303  			0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   304  			0x0, 0x1, 0x2, 0x3, 0x4, 0x4, 0x5,
   305  		})
   306  		assert.NoError(t, writer.Close())
   307  	})
   308  }