In addition to the other very good answers here, one way to think about it is that you are not really improving the level of abstraction (in the direction of a full duplex capability) by applying individual wrappers to the input & output streams.
While it is true that the input & output streams are individually providing their own single responsibility when viewed at their level of abstraction: on the other hand, the bundle of input & output put together forms a higher level of abstraction, the full duplex stream. Having a higher level of abstraction, when we look it it that way: it has its own single responsibility, the pairing of an appropriate input & output stream to form a full duplex com link.
One hint that suggests you are raising the level of abstraction by combining the two together because now you have a single entity that will provide full duplex, and you can pass around to others, and especially whereas by comparison:
If you wrap input & output individually and separately, you would (still) have two abstractions to pass around, and clients would have to concern themselves with (potentially the details of) pairing the two, if not at least keeping them together when passed around, to form the full duplex abstraction. (You could still put a wrapped input & an output together to finally manifest a good full duplex abstraction, but then what's the point of the intermediate wrappers?)
So, while the underlying unidirectional streams each have their own single responsibility, so does a class that bundles them together have its own single responsibility. These are not mutually exclusive: because the latter is at a higher level of abstraction. Taken together these classes are exhibiting layering, which is one way to keep designs focused on their single responsibility. Layering is where one abstraction is implemented entirely by abstractions at the next layer below, without having to go any lower. (Layering is a technique I like to reduce complexity of very large software projects.)